├── .all-contributorsrc ├── .commitlintrc.yml ├── .config └── configuration.winget ├── .devcontainer ├── Dockerfile ├── Microsoft.PowerShell_profile.ps1 ├── config.fish └── devcontainer.json ├── .editorconfig ├── .gitattributes ├── .github ├── FUNDING.yml ├── ISSUE_TEMPLATE │ ├── bug.yml │ ├── config.yml │ ├── docs.yml │ ├── enhancement.yml │ └── feat.yml ├── PULL_REQUEST_TEMPLATE.md ├── dependabot.yml ├── holopin.yml ├── stale.yml └── workflows │ ├── android.yml │ ├── bluesky.yml │ ├── build_code.yml │ ├── close_themes_pr.yml │ ├── code.yml │ ├── commits.yml │ ├── composite │ └── bootstrap-go │ │ └── action.yml │ ├── contributors.yml │ ├── dependabot.yml │ ├── discord.yml │ ├── docs.yml │ ├── edit_rights.yml │ ├── gomod.yml │ ├── homebrew.yml │ ├── lock.yml │ ├── markdown.yml │ ├── merge_contributions_pr.yml │ ├── microsoft_store.yml │ ├── release.yml │ └── winget.yml ├── .gitignore ├── .markdownlint.yaml ├── .markdownlintignore ├── .versionrc.json ├── .vscode ├── extensions.json ├── launch.json ├── settings.json └── tasks.json ├── CODE_OF_CONDUCT.md ├── CONTRIBUTING.md ├── COPYING ├── README.md ├── SECURITY.md ├── build ├── post.ps1 └── pre.ps1 ├── packages ├── msi │ ├── README.md │ ├── build.ps1 │ ├── icon.ico │ └── oh-my-posh.wxs └── winget │ ├── JanDeDobbeleer.OhMyPosh.installer.yaml │ ├── JanDeDobbeleer.OhMyPosh.locale.en-US.yaml │ ├── JanDeDobbeleer.OhMyPosh.yaml │ └── build.ps1 ├── src ├── .golangci.yml ├── .goreleaser.yml ├── build │ └── version.go ├── cache │ ├── cache.go │ ├── clear.go │ ├── command.go │ ├── config.go │ ├── duration.go │ ├── duration_test.go │ ├── file.go │ ├── mock │ │ └── cache.go │ ├── path.go │ └── template.go ├── cli │ ├── args.go │ ├── cache.go │ ├── config.go │ ├── config_export.go │ ├── config_export_image.go │ ├── config_migrate.go │ ├── config_migrate_glyphs.go │ ├── debug.go │ ├── disable.go │ ├── edit.go │ ├── enable.go │ ├── font.go │ ├── get.go │ ├── init.go │ ├── notice.go │ ├── print.go │ ├── prompt.go │ ├── root.go │ ├── toggle.go │ ├── upgrade.go │ └── version.go ├── color │ ├── colors.go │ ├── colors_darwin.go │ ├── colors_test.go │ ├── colors_unix.go │ ├── colors_windows.go │ ├── cycle.go │ ├── keywords.go │ ├── palette.go │ ├── palette_test.go │ └── palettes.go ├── config │ ├── backup.go │ ├── block.go │ ├── config.go │ ├── config_test.go │ ├── default.go │ ├── download.go │ ├── load.go │ ├── migrate.go │ ├── migrate_glyphs.go │ ├── migrate_glyphs_test.go │ ├── migrate_test.go │ ├── responsive.go │ ├── responsive_test.go │ ├── segment.go │ ├── segment_test.go │ └── segment_types.go ├── constants │ ├── constants_unix.go │ ├── constants_windows64.go │ └── constrants_windows386.go ├── font │ ├── cli.go │ ├── download.go │ ├── font.go │ ├── fonts.go │ ├── install.go │ ├── install_darwin.go │ ├── install_unix.go │ └── install_windows.go ├── go.mod ├── go.sum ├── image │ ├── image.go │ └── image_test.go ├── log │ ├── log.go │ └── print.go ├── main.go ├── main_test.go ├── maps │ ├── concurrent.go │ ├── config.go │ └── simple.go ├── metadata.json ├── progress │ ├── model.go │ └── reader.go ├── prompt │ ├── debug.go │ ├── engine.go │ ├── engine_test.go │ ├── extra.go │ ├── primary.go │ ├── rprompt.go │ ├── segments.go │ ├── segments_test.go │ └── tooltip.go ├── properties │ ├── map.go │ ├── map_test.go │ └── properties.go ├── regex │ ├── regex.go │ └── regex_test.go ├── runtime │ ├── battery │ │ ├── battery.go │ │ ├── battery_darwin.go │ │ ├── battery_darwin_test.go │ │ ├── battery_linux.go │ │ ├── battery_netbsd.go │ │ ├── battery_openandfreebsd.go │ │ ├── battery_openandfreebsd_test.go │ │ ├── battery_windows.go │ │ ├── battery_windows_nix.go │ │ ├── battery_windows_nix_test.go │ │ ├── errors.go │ │ └── errors_test.go │ ├── cmd │ │ └── run.go │ ├── environment.go │ ├── http │ │ ├── connection.go │ │ ├── http.go │ │ ├── oauth.go │ │ ├── oauth_test.go │ │ ├── request.go │ │ └── request_test.go │ ├── mock │ │ └── environment.go │ ├── networks_windows.go │ ├── path │ │ ├── clean.go │ │ ├── home.go │ │ └── separator.go │ ├── terminal.go │ ├── terminal_darwin.go │ ├── terminal_test.go │ ├── terminal_unix.go │ ├── terminal_windows.go │ ├── terminal_windows_nix.go │ └── win32_windows.go ├── segments │ ├── angular.go │ ├── argocd.go │ ├── argocd_test.go │ ├── aurelia.go │ ├── aws.go │ ├── aws_test.go │ ├── az.go │ ├── az_functions.go │ ├── az_test.go │ ├── azd.go │ ├── azd_test.go │ ├── base.go │ ├── battery.go │ ├── bazel.go │ ├── bazel_test.go │ ├── brewfather.go │ ├── brewfather_test.go │ ├── buf.go │ ├── buf_test.go │ ├── bun.go │ ├── bun_test.go │ ├── carbon_intensity.go │ ├── carbon_intensity_test.go │ ├── cds.go │ ├── cds_test.go │ ├── cf.go │ ├── cf_target.go │ ├── cf_target_test.go │ ├── cf_test.go │ ├── cmake.go │ ├── cmake_test.go │ ├── command.go │ ├── command_test.go │ ├── connection.go │ ├── connection_test.go │ ├── crystal.go │ ├── crystal_test.go │ ├── dart.go │ ├── dart_test.go │ ├── deno.go │ ├── deno_test.go │ ├── docker.go │ ├── docker_test.go │ ├── dotnet.go │ ├── dotnet_test.go │ ├── elixir.go │ ├── elixir_test.go │ ├── executiontime.go │ ├── executiontime_test.go │ ├── firebase.go │ ├── firebase_test.go │ ├── flutter.go │ ├── flutter_test.go │ ├── fortran.go │ ├── fortran_test.go │ ├── fossil.go │ ├── fossil_test.go │ ├── gcp.go │ ├── gcp_test.go │ ├── git.go │ ├── git_test.go │ ├── git_unix.go │ ├── git_unix_test.go │ ├── git_windows.go │ ├── git_windows_test.go │ ├── gitversion.go │ ├── gitversion_test.go │ ├── golang.go │ ├── golang_test.go │ ├── haskell.go │ ├── haskell_test.go │ ├── helm.go │ ├── helm_test.go │ ├── http.go │ ├── http_test.go │ ├── ipify.go │ ├── ipify_test.go │ ├── java.go │ ├── java_test.go │ ├── jujutsu.go │ ├── jujutsu_test.go │ ├── julia.go │ ├── julia_test.go │ ├── kotlin.go │ ├── kotlin_test.go │ ├── kubectl.go │ ├── kubectl_test.go │ ├── language.go │ ├── language_test.go │ ├── lastfm.go │ ├── lastfm_test.go │ ├── lua.go │ ├── lua_test.go │ ├── mercurial.go │ ├── mercurial_test.go │ ├── mojo.go │ ├── mojo_test.go │ ├── mvn.go │ ├── mvn_test.go │ ├── nba.go │ ├── nba_test.go │ ├── nbgv.go │ ├── nbgv_test.go │ ├── nightscout.go │ ├── nightscout_test.go │ ├── nim.go │ ├── nim_test.go │ ├── nixshell.go │ ├── nixshell_test.go │ ├── node.go │ ├── node_test.go │ ├── npm.go │ ├── npm_test.go │ ├── nx.go │ ├── ocaml.go │ ├── ocaml_test.go │ ├── os.go │ ├── os_test.go │ ├── owm.go │ ├── owm_test.go │ ├── path.go │ ├── path_test.go │ ├── path_unix_test.go │ ├── path_windows_test.go │ ├── perl.go │ ├── perl_test.go │ ├── php.go │ ├── php_test.go │ ├── plastic.go │ ├── plastic_test.go │ ├── pnpm.go │ ├── pnpm_test.go │ ├── posh_git.go │ ├── posh_git_test.go │ ├── project.go │ ├── project_test.go │ ├── pulumi.go │ ├── pulumi_test.go │ ├── python.go │ ├── python_test.go │ ├── quasar.go │ ├── quasar_test.go │ ├── r.go │ ├── r_test.go │ ├── react.go │ ├── root.go │ ├── ruby.go │ ├── ruby_test.go │ ├── rust.go │ ├── rust_test.go │ ├── sapling.go │ ├── sapling_test.go │ ├── scm.go │ ├── scm_test.go │ ├── session.go │ ├── session_test.go │ ├── shell.go │ ├── shell_test.go │ ├── sitecore.go │ ├── sitecore_test.go │ ├── spotify.go │ ├── spotify_darwin.go │ ├── spotify_darwin_test.go │ ├── spotify_linux.go │ ├── spotify_linux_test.go │ ├── spotify_noop.go │ ├── spotify_test.go │ ├── spotify_windows.go │ ├── spotify_windows_test.go │ ├── status.go │ ├── status_test.go │ ├── strava.go │ ├── strava_test.go │ ├── svelte.go │ ├── svn.go │ ├── svn_test.go │ ├── swift.go │ ├── swift_test.go │ ├── sysinfo.go │ ├── sysinfo_test.go │ ├── talosctl.go │ ├── talosctl_test.go │ ├── tauri.go │ ├── terraform.go │ ├── terraform_test.go │ ├── text.go │ ├── text_test.go │ ├── time.go │ ├── time_test.go │ ├── ui5tooling.go │ ├── ui5tooling_test.go │ ├── umbraco.go │ ├── umbraco_test.go │ ├── unity.go │ ├── unity_test.go │ ├── upgrade.go │ ├── upgrade_test.go │ ├── v.go │ ├── v_test.go │ ├── vala.go │ ├── vala_test.go │ ├── wakatime.go │ ├── wakatime_test.go │ ├── winreg.go │ ├── winreg_test.go │ ├── withings.go │ ├── withings_test.go │ ├── xmake.go │ ├── xmake_test.go │ ├── yarn.go │ ├── yarn_test.go │ ├── ytm.go │ ├── ytm_test.go │ ├── zig.go │ └── zig_test.go ├── shell │ ├── bash.go │ ├── bash_test.go │ ├── cmd.go │ ├── cmd_test.go │ ├── code.go │ ├── constants.go │ ├── elvish.go │ ├── elvish_test.go │ ├── features.go │ ├── filesystem.go │ ├── fish.go │ ├── fish_test.go │ ├── formats.go │ ├── init.go │ ├── nu.go │ ├── nu_test.go │ ├── pwsh.go │ ├── pwsh_test.go │ ├── scripts │ │ ├── omp.bash │ │ ├── omp.elv │ │ ├── omp.fish │ │ ├── omp.lua │ │ ├── omp.nu │ │ ├── omp.ps1 │ │ ├── omp.xsh │ │ └── omp.zsh │ ├── xonsh.go │ ├── xonsh_test.go │ ├── zsh.go │ └── zsh_test.go ├── template │ ├── cache.go │ ├── compare.go │ ├── compare_test.go │ ├── files.go │ ├── files_test.go │ ├── func_map.go │ ├── init.go │ ├── link.go │ ├── link_test.go │ ├── list.go │ ├── numbers.go │ ├── numbers_test.go │ ├── random.go │ ├── random_test.go │ ├── reason.go │ ├── regex.go │ ├── render_pool.go │ ├── round.go │ ├── round_test.go │ ├── strings.go │ ├── strings_test.go │ ├── text.go │ └── text_test.go ├── terminal │ ├── iterm.go │ ├── writer.go │ ├── writer_hyperlink_test.go │ └── writer_test.go ├── test │ ├── AzureRmContext.json │ ├── azureProfile.json │ ├── empty.nuspec │ ├── go.work │ ├── invalid.nuspec │ ├── jandedobbeleer-palette.omp.json │ ├── jandedobbeleer.omp.json │ ├── kubectl.yml │ ├── nba │ │ ├── schedule.json │ │ └── score.json │ ├── oh-my-posh.psd1 │ ├── signing │ │ ├── checksums.txt │ │ ├── checksums.txt.invalid.sig │ │ └── checksums.txt.sig │ ├── terraform.tfstate │ ├── umbraco │ │ ├── ANonUmbracoProject.csproj │ │ ├── MyProject.csproj │ │ ├── web.config │ │ └── web.old.config │ ├── valid.nuspec │ └── versions.tf ├── upgrade │ ├── cli.go │ ├── cli_test.go │ ├── config.go │ ├── install.go │ ├── install_noop.go │ ├── install_windows.go │ ├── notice.go │ ├── notice_test.go │ ├── public_key.pem │ ├── verify.go │ └── verify_test.go └── winres │ ├── icon.png │ ├── icon16.png │ ├── icon32.png │ ├── icon48.png │ ├── icon64.png │ └── winres.json ├── themes ├── 1_shell.omp.json ├── M365Princess.omp.json ├── agnoster.minimal.omp.json ├── agnoster.omp.json ├── agnosterplus.omp.json ├── aliens.omp.json ├── amro.omp.json ├── atomic.omp.json ├── atomicBit.omp.json ├── avit.omp.json ├── blue-owl.omp.json ├── blueish.omp.json ├── bubbles.omp.json ├── bubblesextra.omp.json ├── bubblesline.omp.json ├── capr4n.omp.json ├── catppuccin.omp.json ├── catppuccin_frappe.omp.json ├── catppuccin_latte.omp.json ├── catppuccin_macchiato.omp.json ├── catppuccin_mocha.omp.json ├── cert.omp.json ├── chips.omp.json ├── cinnamon.omp.json ├── clean-detailed.omp.json ├── cloud-context.omp.json ├── cloud-native-azure.omp.json ├── cobalt2.omp.json ├── craver.omp.json ├── darkblood.omp.json ├── devious-diamonds.omp.yaml ├── di4am0nd.omp.json ├── dracula.omp.json ├── easy-term.omp.json ├── emodipt-extend.omp.json ├── emodipt.omp.json ├── fish.omp.json ├── free-ukraine.omp.json ├── froczh.omp.json ├── glowsticks.omp.yaml ├── gmay.omp.json ├── grandpa-style.omp.json ├── gruvbox.omp.json ├── half-life.omp.json ├── honukai.omp.json ├── hotstick.minimal.omp.json ├── hul10.omp.json ├── hunk.omp.json ├── huvix.omp.json ├── if_tea.omp.json ├── illusi0n.omp.json ├── iterm2.omp.json ├── jandedobbeleer.omp.json ├── jblab_2021.omp.json ├── jonnychipz.omp.json ├── json.omp.json ├── jtracey93.omp.json ├── jv_sitecorian.omp.json ├── kali.omp.json ├── kushal.omp.json ├── lambda.omp.json ├── lambdageneration.omp.json ├── larserikfinholt.omp.json ├── lightgreen.omp.json ├── marcduiker.omp.json ├── markbull.omp.json ├── material.omp.json ├── microverse-power.omp.json ├── mojada.omp.json ├── montys.omp.json ├── mt.omp.json ├── multiverse-neon.omp.json ├── negligible.omp.json ├── neko.omp.json ├── night-owl.omp.json ├── nordtron.omp.json ├── nu4a.omp.json ├── onehalf.minimal.omp.json ├── paradox.omp.json ├── pararussel.omp.json ├── patriksvensson.omp.json ├── peru.omp.json ├── pixelrobots.omp.json ├── plague.omp.json ├── poshmon.omp.json ├── powerlevel10k_classic.omp.json ├── powerlevel10k_lean.omp.json ├── powerlevel10k_modern.omp.json ├── powerlevel10k_rainbow.omp.json ├── powerline.omp.json ├── probua.minimal.omp.json ├── pure.omp.json ├── quick-term.omp.json ├── remk.omp.json ├── robbyrussell.omp.json ├── rudolfs-dark.omp.json ├── rudolfs-light.omp.json ├── schema.json ├── sim-web.omp.json ├── slim.omp.json ├── slimfat.omp.json ├── smoothie.omp.json ├── sonicboom_dark.omp.json ├── sonicboom_light.omp.json ├── sorin.omp.json ├── space.omp.json ├── spaceship.omp.json ├── star.omp.json ├── stelbent-compact.minimal.omp.json ├── stelbent.minimal.omp.json ├── takuya.omp.json ├── the-unnamed.omp.json ├── thecyberden.omp.json ├── tiwahu.omp.json ├── tokyo.omp.json ├── tokyonight_storm.omp.json ├── tonybaloney.omp.json ├── uew.omp.json ├── unicorn.omp.json ├── velvet.omp.json ├── wholespace.omp.json ├── wopian.omp.json ├── xtoys.omp.json ├── ys.omp.json └── zash.omp.json └── website ├── .gitignore ├── README.md ├── api ├── .funcignore ├── .gitignore ├── auth │ ├── function.json │ └── index.js ├── host.json ├── package-lock.json ├── package.json ├── proxies.json ├── refresh │ ├── function.json │ └── index.js └── shared │ ├── strava.js │ └── withings.js ├── blog ├── 2022-03-20-whats-new-1.mdx ├── 2022-03-27-whats-new-2.mdx ├── 2022-03-28-idiots-everywhere.md ├── 2022-05-19-whats-new-3.mdx └── 2024-07-22-bash-rprompt.mdx ├── docs ├── auth.mdx ├── configuration │ ├── block.mdx │ ├── colors.mdx │ ├── debug-prompt.mdx │ ├── example.mdx │ ├── general.mdx │ ├── introduction.md │ ├── line-error.mdx │ ├── secondary-prompt.mdx │ ├── segment.mdx │ ├── templates.mdx │ ├── title.mdx │ ├── tooltips.mdx │ └── transient.mdx ├── contributing │ ├── git.mdx │ ├── plastic.mdx │ ├── segment.mdx │ └── started.mdx ├── contributors.md ├── faq.mdx ├── installation │ ├── customize.mdx │ ├── fonts.mdx │ ├── homebrew.mdx │ ├── linux.mdx │ ├── macos.mdx │ ├── next.mdx │ ├── prompt.mdx │ ├── upgrade.mdx │ └── windows.mdx ├── migrating-module.md ├── segments │ ├── cli │ │ ├── angular.mdx │ │ ├── argocd.mdx │ │ ├── aurelia.mdx │ │ ├── bazel.mdx │ │ ├── buf.mdx │ │ ├── bun.mdx │ │ ├── cmake.mdx │ │ ├── deno.mdx │ │ ├── docker.mdx │ │ ├── firebase.mdx │ │ ├── flutter.mdx │ │ ├── gitversion.mdx │ │ ├── helm.mdx │ │ ├── kubectl.mdx │ │ ├── mvn.mdx │ │ ├── nbgv.mdx │ │ ├── nix-shell.mdx │ │ ├── npm.mdx │ │ ├── nx.mdx │ │ ├── pnpm.mdx │ │ ├── quasar.mdx │ │ ├── react.mdx │ │ ├── svelte.mdx │ │ ├── talosctl.mdx │ │ ├── tauri.mdx │ │ ├── terraform.mdx │ │ ├── ui5tooling.mdx │ │ ├── umbraco.mdx │ │ ├── unity.mdx │ │ ├── xmake.mdx │ │ └── yarn.mdx │ ├── cloud │ │ ├── aws.mdx │ │ ├── az.mdx │ │ ├── azd.mdx │ │ ├── azfunc.mdx │ │ ├── cds.mdx │ │ ├── cf.mdx │ │ ├── cftarget.mdx │ │ ├── gcp.mdx │ │ ├── pulumi.mdx │ │ └── sitecore.mdx │ ├── health │ │ ├── nightscout.mdx │ │ ├── strava.mdx │ │ └── withings.mdx │ ├── languages │ │ ├── crystal.mdx │ │ ├── dart.mdx │ │ ├── dotnet.mdx │ │ ├── elixir.mdx │ │ ├── fortran.mdx │ │ ├── golang.mdx │ │ ├── haskell.mdx │ │ ├── java.mdx │ │ ├── julia.mdx │ │ ├── kotlin.mdx │ │ ├── lua.mdx │ │ ├── mojo.mdx │ │ ├── nim.mdx │ │ ├── node.mdx │ │ ├── ocaml.mdx │ │ ├── perl.mdx │ │ ├── php.mdx │ │ ├── python.mdx │ │ ├── r.mdx │ │ ├── ruby.mdx │ │ ├── rust.mdx │ │ ├── swift.mdx │ │ ├── v.mdx │ │ ├── vala.mdx │ │ └── zig.mdx │ ├── music │ │ ├── lastfm.mdx │ │ ├── spotify.mdx │ │ └── ytm.mdx │ ├── scm │ │ ├── fossil.mdx │ │ ├── git.mdx │ │ ├── jujutsu.mdx │ │ ├── mercurial.mdx │ │ ├── plastic.mdx │ │ ├── sapling.mdx │ │ └── svn.mdx │ ├── system │ │ ├── battery.mdx │ │ ├── command.mdx │ │ ├── connection.mdx │ │ ├── executiontime.mdx │ │ ├── os.mdx │ │ ├── path.mdx │ │ ├── project.mdx │ │ ├── root.mdx │ │ ├── session.mdx │ │ ├── shell.mdx │ │ ├── status.mdx │ │ ├── sysinfo.mdx │ │ ├── text.mdx │ │ ├── time.mdx │ │ ├── upgrade.mdx │ │ └── winreg.mdx │ └── web │ │ ├── brewfather.mdx │ │ ├── carbonintensity.mdx │ │ ├── http.mdx │ │ ├── ipify.mdx │ │ ├── nba.mdx │ │ ├── owm.mdx │ │ └── wakatime.mdx ├── share-theme.md └── themes.md ├── docusaurus.config.js ├── export_themes.js ├── package-lock.json ├── package.json ├── plugins └── appinsights │ ├── analytics.js │ └── index.js ├── sidebars.js ├── src ├── components │ ├── Auth.js │ └── Config.js ├── css │ ├── custom.css │ └── prism-rose-pine-moon.css └── pages │ ├── index.js │ ├── privacy.mdx │ └── styles.module.css ├── static ├── .nojekyll ├── codepoints.csv ├── fonts │ ├── VictorMono.ttf │ └── fonts.zip ├── img │ ├── accordeon.png │ ├── favicons.svg │ ├── hero.png │ ├── logo-dark.svg │ ├── logo-light.svg │ ├── logo.png │ ├── msstore-dark.svg │ ├── msstore-light.svg │ ├── posh-tooltip.gif │ ├── strava_connect.svg │ ├── themes │ │ └── .keep │ ├── transient-after.gif │ ├── transient-before.gif │ ├── transient-color.png │ └── withings.svg ├── install.ps1 └── install.sh └── staticwebapp.config.json /.commitlintrc.yml: -------------------------------------------------------------------------------- 1 | --- 2 | extends: 3 | - '@commitlint/config-conventional' 4 | rules: 5 | body-max-line-length: 6 | - 2 7 | - always 8 | - 200 9 | type-enum: 10 | - 2 11 | - always 12 | - - chore 13 | - ci 14 | - docs 15 | - feat 16 | - fix 17 | - perf 18 | - refactor 19 | - revert 20 | - style 21 | - test 22 | - theme 23 | -------------------------------------------------------------------------------- /.config/configuration.winget: -------------------------------------------------------------------------------- 1 | # yaml-language-server: $schema=https://aka.ms/configuration-dsc-schema/0.2 2 | properties: 3 | resources: 4 | - resource: Microsoft.WinGet.DSC/WinGetPackage 5 | directives: 6 | description: Install Visual Studio Code 7 | settings: 8 | id: Microsoft.VisualStudioCode 9 | source: winget 10 | - resource: Microsoft.WinGet.DSC/WinGetPackage 11 | id: golang 12 | directives: 13 | description: Install Golang 14 | settings: 15 | id: GoLang.Go 16 | source: winget 17 | - resource: Microsoft.WinGet.DSC/WinGetPackage 18 | dependsOn: [golang] 19 | directives: 20 | description: Install golangci-lint 21 | settings: 22 | id: GolangCI.golangci-lint 23 | source: winget 24 | - resource: Microsoft.WinGet.DSC/WinGetPackage 25 | directives: 26 | description: Install NodeJS 27 | securityContext: elevated 28 | settings: 29 | id: OpenJS.NodeJS 30 | source: winget 31 | configurationVersion: 0.2.0 32 | -------------------------------------------------------------------------------- /.devcontainer/Microsoft.PowerShell_profile.ps1: -------------------------------------------------------------------------------- 1 | Import-Module posh-git 2 | Import-Module PSFzf -ArgumentList 'Ctrl+t', 'Ctrl+r' 3 | Import-Module z 4 | Import-Module Terminal-Icons 5 | 6 | Set-PSReadlineKeyHandler -Key Tab -Function MenuComplete 7 | 8 | $env:POSH_GIT_ENABLED=$true 9 | oh-my-posh init pwsh | Invoke-Expression 10 | -------------------------------------------------------------------------------- /.devcontainer/config.fish: -------------------------------------------------------------------------------- 1 | # Activate oh-my-posh prompt: 2 | oh-my-posh init fish | source 3 | -------------------------------------------------------------------------------- /.editorconfig: -------------------------------------------------------------------------------- 1 | ; EditorConfig to support per-solution formatting. 2 | ; http://editorconfig.org/ 3 | 4 | ; This is the default for the codeline. 5 | root = true 6 | 7 | ; Default 8 | [*] 9 | indent_style = space 10 | trim_trailing_whitespace = true 11 | insert_final_newline = true 12 | end_of_line = lf 13 | 14 | ; Go Code - match go fmt 15 | [*.go] 16 | indent_style = tab 17 | 18 | ; TOML - match default for dep 19 | [*.toml] 20 | indent_size = 2 21 | 22 | ; JavaScript and JS mixes - match eslint, other standards 23 | [*.{js,json,ts,vue}] 24 | indent_size = 2 25 | 26 | ; Markdown - match markdownlint settings 27 | [*.{md,markdown}] 28 | indent_size = 2 29 | trim_trailing_whitespace = false 30 | 31 | ; PowerShell - match defaults for New-ModuleManifest and PSScriptAnalyzer Invoke-Formatter 32 | [*.{ps1,psd1,psm1}] 33 | indent_size = 4 34 | charset = utf-8-bom 35 | 36 | ; Lua 37 | [*.lua] 38 | line_space_after_comment = max(2) 39 | line_space_after_do_statement = max(2) 40 | line_space_after_expression_statement = max(2) 41 | line_space_after_for_statement = max(2) 42 | line_space_after_function_statement = fixed(2) 43 | line_space_after_if_statement = max(2) 44 | line_space_after_local_or_assign_statement = max(2) 45 | line_space_after_repeat_statement = max(2) 46 | line_space_after_while_statement = max(2) 47 | max_line_length = unset 48 | quote_style = single 49 | -------------------------------------------------------------------------------- /.gitattributes: -------------------------------------------------------------------------------- 1 | * text=auto eol=lf 2 | -------------------------------------------------------------------------------- /.github/FUNDING.yml: -------------------------------------------------------------------------------- 1 | # These are supported funding model platforms 2 | 3 | github: jandedobbeleer 4 | ko_fi: jandedobbeleer 5 | polar: oh-my-posh 6 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/config.yml: -------------------------------------------------------------------------------- 1 | blank_issues_enabled: false 2 | contact_links: 3 | - name: Oh My Posh FAQ 4 | url: https://ohmyposh.dev/docs/faq 5 | about: Please find common issues here. 6 | - name: Oh My Posh Docs 7 | url: https://ohmyposh.dev/docs 8 | about: RTFM 9 | - name: Oh My Posh Q&A 10 | url: https://github.com/JanDeDobbeleer/oh-my-posh/discussions 11 | about: Please ask questions here. 12 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/docs.yml: -------------------------------------------------------------------------------- 1 | name: 📖 Documentation 2 | description: Suggest a change to the documentation 3 | labels: ["📖 docs"] 4 | assignees: 5 | - jandedobbeleer 6 | body: 7 | - type: markdown 8 | attributes: 9 | value: | 10 | Thanks for taking the time to request this improvement! 11 | - type: checkboxes 12 | id: terms 13 | attributes: 14 | label: Code of Conduct 15 | description: By submitting this issue, you agree to follow our [Code of Conduct](https://github.com/JanDeDobbeleer/oh-my-posh/blob/main/CONTRIBUTING.md) 16 | options: 17 | - label: I agree to follow this project's Code of Conduct 18 | required: true 19 | - type: textarea 20 | id: enhancement-request 21 | attributes: 22 | label: What would you like to see changed/added? 23 | description: Try to give some examples or text to make it really clear! 24 | placeholder: Tell us what you would like to see! 25 | value: "This could change in the documentation!" 26 | validations: 27 | required: true 28 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/enhancement.yml: -------------------------------------------------------------------------------- 1 | name: 🤩 Enhancement 2 | description: Suggest a change to an existing feature 3 | labels: ["🤩 enhancement"] 4 | assignees: 5 | - jandedobbeleer 6 | body: 7 | - type: markdown 8 | attributes: 9 | value: | 10 | Thanks for taking the time to request this improvement! 11 | - type: checkboxes 12 | id: terms 13 | attributes: 14 | label: Code of Conduct 15 | description: By submitting this issue, you agree to follow our [Code of Conduct](https://github.com/JanDeDobbeleer/oh-my-posh/blob/main/CONTRIBUTING.md) 16 | options: 17 | - label: I agree to follow this project's Code of Conduct 18 | required: true 19 | - type: textarea 20 | id: enhancement-request 21 | attributes: 22 | label: What would you like to see changed? 23 | description: Try to give some examples to make it really clear! 24 | placeholder: Tell us what you would like to see! 25 | value: "This feature would benefit from this!" 26 | validations: 27 | required: true 28 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/feat.yml: -------------------------------------------------------------------------------- 1 | name: 🚀 Feature Request 2 | description: Request a new feature 3 | labels: ["🚀 feat"] 4 | assignees: 5 | - jandedobbeleer 6 | body: 7 | - type: markdown 8 | attributes: 9 | value: | 10 | Thanks for taking the time to request a new feature! 11 | - type: checkboxes 12 | id: terms 13 | attributes: 14 | label: Code of Conduct 15 | description: By submitting this issue, you agree to follow our [Code of Conduct](https://github.com/JanDeDobbeleer/oh-my-posh/blob/main/CONTRIBUTING.md) 16 | options: 17 | - label: I agree to follow this project's Code of Conduct 18 | required: true 19 | - type: textarea 20 | id: feature-request 21 | attributes: 22 | label: What would you like to see added? 23 | description: Try to give some examples to make it really clear. 24 | placeholder: Tell us what you would like to see! 25 | value: "Something new and amazing!" 26 | validations: 27 | required: true 28 | -------------------------------------------------------------------------------- /.github/PULL_REQUEST_TEMPLATE.md: -------------------------------------------------------------------------------- 1 | ### Prerequisites 2 | 3 | - [ ] I have read and understood the [contributing guide][CONTRIBUTING.md]. 4 | - [ ] The commit message follows the [conventional commits][cc] guidelines. 5 | - [ ] Tests for the changes have been added (for bug fixes / features). 6 | - [ ] Docs have been added/updated (for bug fixes / features). 7 | 8 | ### Description 9 | 10 | 19 | 20 | [CONTRIBUTING.md]: https://github.com/JanDeDobbeleer/oh-my-posh/blob/main/CONTRIBUTING.md 21 | [cc]: https://www.conventionalcommits.org/en/v1.0.0/#summary 22 | -------------------------------------------------------------------------------- /.github/dependabot.yml: -------------------------------------------------------------------------------- 1 | version: 2 2 | updates: 3 | 4 | - package-ecosystem: "github-actions" 5 | directory: "/" 6 | target-branch: "main" 7 | schedule: 8 | interval: "daily" 9 | groups: 10 | all: 11 | patterns: 12 | - "*" 13 | ignore: 14 | - dependency-name: "softprops/action-gh-release" 15 | # https://github.com/softprops/action-gh-release/issues/556 16 | versions: ["2.2.0"] 17 | 18 | - package-ecosystem: "gomod" 19 | directory: "/src" 20 | target-branch: "main" 21 | schedule: 22 | interval: "daily" 23 | groups: 24 | minor-patch: 25 | patterns: 26 | - "*" 27 | update-types: 28 | - "minor" 29 | - "patch" 30 | 31 | - package-ecosystem: "npm" 32 | directory: "/website" 33 | schedule: 34 | interval: "daily" 35 | ignore: 36 | - dependency-name: "*" 37 | -------------------------------------------------------------------------------- /.github/holopin.yml: -------------------------------------------------------------------------------- 1 | organization: ohmyposh 2 | defaultSticker: clg0u51g681700fmfr086ofc6 3 | stickers: 4 | - 5 | id: clg0u51g681700fmfr086ofc6 6 | alias: wizard 7 | - 8 | id: clu72f66x59170fjoo6t2b7zs 9 | alias: helping 10 | -------------------------------------------------------------------------------- /.github/workflows/bluesky.yml: -------------------------------------------------------------------------------- 1 | name: Bluesky 2 | on: 3 | release: 4 | types: [published] 5 | workflow_dispatch: 6 | 7 | jobs: 8 | bluesky: 9 | runs-on: ubuntu-latest 10 | steps: 11 | - name: Publish 12 | uses: JanDeDobbeleer/bluesky-releasenotes-action@main 13 | with: 14 | title: "The best release yet 🚀" 15 | bluesky-identifier: ${{ secrets.BLUESKY_IDENTIFIER }} 16 | bluesky-password: ${{ secrets.BLUESKY_PASSWORD }} 17 | github-token: ${{ secrets.GH_PAT }} 18 | -------------------------------------------------------------------------------- /.github/workflows/build_code.yml: -------------------------------------------------------------------------------- 1 | on: 2 | pull_request: 3 | paths-ignore: 4 | - 'README.md' 5 | - 'CONTRIBUTING.md' 6 | - 'COPYING' 7 | - 'website/**' 8 | - '.github/*.md' 9 | - '.github/FUNDING.yml' 10 | 11 | name: Build Code 12 | jobs: 13 | build: 14 | runs-on: macos-latest 15 | defaults: 16 | run: 17 | shell: pwsh 18 | steps: 19 | - name: Checkout code 👋 20 | uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 21 | - name: Install Go 🗳 22 | uses: ./.github/workflows/composite/bootstrap-go 23 | - name: Run GoReleaser 🚀 24 | uses: goreleaser/goreleaser-action@9c156ee8a17a598857849441385a2041ef570552 25 | with: 26 | distribution: goreleaser 27 | version: v2.3.2 28 | args: build --clean --snapshot --skip=post-hooks --skip=before 29 | workdir: src 30 | - name: Archive production artifacts 31 | uses: actions/upload-artifact@ea165f8d65b6e75b540449e92b4886f43607fa02 32 | with: 33 | name: builds 34 | retention-days: 1 35 | path: | 36 | src/dist 37 | -------------------------------------------------------------------------------- /.github/workflows/code.yml: -------------------------------------------------------------------------------- 1 | on: 2 | pull_request: 3 | paths-ignore: 4 | - 'README.md' 5 | - 'CONTRIBUTING.md' 6 | - 'COPYING' 7 | - 'website/**' 8 | - '.github/*.md' 9 | - '.github/FUNDING.yml' 10 | 11 | name: Validate Code 12 | jobs: 13 | test: 14 | strategy: 15 | matrix: 16 | os: [ubuntu-latest, macos-latest, windows-latest] 17 | runs-on: ${{ matrix.os }} 18 | defaults: 19 | run: 20 | working-directory: ${{ github.workspace }}/src 21 | steps: 22 | - name: Checkout code 23 | uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 24 | - name: Install Go 🗳 25 | uses: ./.github/workflows/composite/bootstrap-go 26 | - name: Golang CI 27 | uses: golangci/golangci-lint-action@4afd733a84b1f43292c63897423277bb7f4313a9 28 | with: 29 | working-directory: src 30 | - name: Fieldalignment 31 | run: | 32 | go install golang.org/x/tools/go/analysis/passes/fieldalignment/cmd/fieldalignment@latest 33 | fieldalignment "./..." 34 | - name: Unit Tests 35 | run: go test -v ./... 36 | -------------------------------------------------------------------------------- /.github/workflows/commits.yml: -------------------------------------------------------------------------------- 1 | name: Validate Commits 2 | 3 | on: [pull_request] 4 | 5 | jobs: 6 | commitlint: 7 | uses: jandedobbeleer/workflows/.github/workflows/commits.yml@main 8 | -------------------------------------------------------------------------------- /.github/workflows/composite/bootstrap-go/action.yml: -------------------------------------------------------------------------------- 1 | # yaml-language-server: $schema=https://json.schemastore.org/github-action.json 2 | name: 'Setup Go' 3 | description: 'Install Go and override with the custom build' 4 | branding: 5 | icon: download 6 | color: purple 7 | runs: 8 | using: "composite" 9 | steps: 10 | - uses: actions/setup-go@0c52d547c9bc32b1aa3301fd7a9cb496313a4491 11 | with: 12 | go-version: '1.24.1' 13 | cache-dependency-path: src/go.sum 14 | -------------------------------------------------------------------------------- /.github/workflows/contributors.yml: -------------------------------------------------------------------------------- 1 | name: Contributors 2 | on: 3 | pull_request_target: 4 | types: 5 | - closed 6 | 7 | jobs: 8 | contributors: 9 | uses: jandedobbeleer/workflows/.github/workflows/contributors.yml@main 10 | secrets: 11 | token: ${{ secrets.GH_PAT }} 12 | -------------------------------------------------------------------------------- /.github/workflows/dependabot.yml: -------------------------------------------------------------------------------- 1 | name: Dependabot auto-merge 2 | on: 3 | pull_request: 4 | types: [opened, reopened] 5 | 6 | permissions: 7 | contents: write 8 | pull-requests: write 9 | 10 | jobs: 11 | dependabot: 12 | uses: jandedobbeleer/workflows/.github/workflows/dependabot.yml@main 13 | -------------------------------------------------------------------------------- /.github/workflows/discord.yml: -------------------------------------------------------------------------------- 1 | name: Discord 2 | on: 3 | release: 4 | types: [published] 5 | 6 | jobs: 7 | notify: 8 | uses: jandedobbeleer/workflows/.github/workflows/discord.yml@main 9 | secrets: 10 | webhook: ${{ secrets.CHANGELOG_WEBHOOK }} 11 | -------------------------------------------------------------------------------- /.github/workflows/edit_rights.yml: -------------------------------------------------------------------------------- 1 | name: Notify When Maintainers Cannot Edit 2 | 3 | # **What it does**: Notifies the author of a PR when their PR does not allow maintainers to edit it. 4 | # **Why we have it**: To prevent having to do this manually. 5 | # **Who does it impact**: Open-source. 6 | 7 | on: 8 | pull_request_target: 9 | types: 10 | - opened 11 | - edited 12 | 13 | permissions: 14 | pull-requests: write 15 | 16 | jobs: 17 | notify-when-maintainers-cannot-edit: 18 | uses: jandedobbeleer/workflows/.github/workflows/edit_rights.yml@main 19 | secrets: 20 | token: ${{ secrets.GH_PAT }} 21 | -------------------------------------------------------------------------------- /.github/workflows/gomod.yml: -------------------------------------------------------------------------------- 1 | name: Go Mod 2 | 3 | on: [pull_request] 4 | 5 | jobs: 6 | go-mod: 7 | runs-on: ubuntu-latest 8 | defaults: 9 | run: 10 | working-directory: ${{ github.workspace }}/src 11 | steps: 12 | - name: Checkout code 13 | uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 14 | - name: Install Go 🗳 15 | uses: ./.github/workflows/composite/bootstrap-go 16 | - name: Check for unused dependencies 17 | run: | 18 | go mod tidy 19 | if [ "$(git status | grep -c "nothing to commit, working tree clean")" == "1" ]; then 20 | echo "Nothing to tidy" 21 | exit 0 22 | fi 23 | echo "Go mod tidy is needed" 24 | exit 1 25 | -------------------------------------------------------------------------------- /.github/workflows/homebrew.yml: -------------------------------------------------------------------------------- 1 | name: Homebrew 2 | on: 3 | release: 4 | types: [published] 5 | 6 | jobs: 7 | notify: 8 | runs-on: ubuntu-latest 9 | steps: 10 | - name: Notify Homebrew Repo 🙋🏾‍♀️ 11 | uses: actions/github-script@60a0d83039c74a4aee543508d2ffcb1c3799cdea 12 | with: 13 | github-token: ${{ secrets.GH_PAT }} 14 | script: | 15 | await github.request('POST /repos/{owner}/{repo}/actions/workflows/{workflow_id}/dispatches', { 16 | owner: 'jandedobbeleer', 17 | repo: 'homebrew-oh-my-posh', 18 | workflow_id: 'release.yml', 19 | ref: 'main', 20 | inputs: {"version": process.env.GITHUB_REF.replace('refs/tags/v', '')} 21 | }) 22 | -------------------------------------------------------------------------------- /.github/workflows/lock.yml: -------------------------------------------------------------------------------- 1 | name: 'Lock Threads' 2 | 3 | on: 4 | schedule: 5 | - cron: '0 0 * * 1' 6 | 7 | permissions: 8 | issues: write 9 | 10 | concurrency: 11 | group: lock 12 | 13 | jobs: 14 | action: 15 | runs-on: ubuntu-latest 16 | steps: 17 | - uses: dessant/lock-threads@1bf7ec25051fe7c00bdd17e6a7cf3d7bfb7dc771 18 | with: 19 | issue-inactive-days: '90' 20 | issue-comment: > 21 | This issue has been automatically locked since there 22 | has not been any recent activity (i.e. last half year) after it was closed. 23 | It helps our maintainers focus on the active issues. 24 | 25 | If you have found a problem that seems similar, please open a 26 | [discussion](https://github.com/JanDeDobbeleer/oh-my-posh/discussions/new?category=troubleshoot) 27 | first, complete the body with all the details necessary to reproduce, 28 | and mention this issue as reference. 29 | process-only: 'issues' 30 | -------------------------------------------------------------------------------- /.github/workflows/markdown.yml: -------------------------------------------------------------------------------- 1 | name: Markdownlint 2 | 3 | on: [pull_request] 4 | 5 | jobs: 6 | lint: 7 | runs-on: ubuntu-latest 8 | steps: 9 | - name: Checkout code 10 | uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 11 | - name: Lint files 12 | uses: articulate/actions-markdownlint@17b8abe7407cd17590c006ecc837c35e1ac3ed83 13 | with: 14 | files: . 15 | config: .markdownlint.yaml 16 | -------------------------------------------------------------------------------- /.github/workflows/merge_contributions_pr.yml: -------------------------------------------------------------------------------- 1 | name: Merge contributions PR 2 | on: 3 | pull_request_target: 4 | types: 5 | - opened 6 | - reopened 7 | 8 | jobs: 9 | check: 10 | runs-on: ubuntu-latest 11 | steps: 12 | - name: Checkout code 👋 13 | uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 14 | - name: Check and merge ⛙ 15 | uses: actions/github-script@60a0d83039c74a4aee543508d2ffcb1c3799cdea 16 | with: 17 | github-token: ${{ secrets.GH_PAT }} 18 | script: | 19 | const { repo: { owner, repo } } = context; 20 | const pr = context.payload.pull_request; 21 | 22 | if (pr.user.id !== 46447321) { 23 | console.log('Not an all-contributors pull request'); 24 | return; 25 | } 26 | 27 | console.log(`Merging pull request: ${pr.html_url}`); 28 | await github.rest.pulls.merge({ 29 | owner, repo, 30 | pull_number: pr.number, 31 | merge_method: "rebase", 32 | }); 33 | -------------------------------------------------------------------------------- /.github/workflows/winget.yml: -------------------------------------------------------------------------------- 1 | name: Winget 2 | on: 3 | release: 4 | types: [published] 5 | 6 | jobs: 7 | publish: 8 | runs-on: windows-latest 9 | defaults: 10 | run: 11 | shell: pwsh 12 | working-directory: ${{ github.workspace }}/packages/winget 13 | env: 14 | WINGETCREATE_TOKEN: ${{ secrets.WINGETCREATE_TOKEN }} 15 | steps: 16 | - name: Checkout code 👋 17 | uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 18 | - name: Create manifest and submit PR 📦 19 | run: | 20 | ./build.ps1 -Version "${{ github.event.release.tag_name }}" -Token $env:WINGETCREATE_TOKEN 21 | -------------------------------------------------------------------------------- /.markdownlint.yaml: -------------------------------------------------------------------------------- 1 | MD024: false 2 | MD014: false 3 | MD038: false 4 | line-length: 5 | line_length: 120 6 | code_blocks: false 7 | -------------------------------------------------------------------------------- /.markdownlintignore: -------------------------------------------------------------------------------- 1 | node_modules/ 2 | -------------------------------------------------------------------------------- /.versionrc.json: -------------------------------------------------------------------------------- 1 | { 2 | "types": [ 3 | { 4 | "type": "feat", 5 | "section": "Features" 6 | }, 7 | { 8 | "type": "fix", 9 | "section": "Bug Fixes" 10 | }, 11 | { 12 | "type": "refactor", 13 | "section": "Refactor" 14 | }, 15 | { 16 | "type": "revert", 17 | "section": "Reverts" 18 | }, 19 | { 20 | "type": "theme", 21 | "section": "Themes" 22 | }, 23 | { 24 | "type": "chore", 25 | "hidden": true 26 | }, 27 | { 28 | "type": "ci", 29 | "hidden": true 30 | }, 31 | { 32 | "type": "chore", 33 | "hidden": true 34 | }, 35 | { 36 | "type": "docs", 37 | "hidden": true 38 | }, 39 | { 40 | "type": "perf", 41 | "hidden": true 42 | }, 43 | { 44 | "type": "test", 45 | "hidden": true 46 | } 47 | ] 48 | } 49 | -------------------------------------------------------------------------------- /.vscode/extensions.json: -------------------------------------------------------------------------------- 1 | { 2 | "recommendations": [ 3 | "ms-azuretools.vscode-azurefunctions", 4 | "golang.go", 5 | "github.vscode-pull-request-github", 6 | "esbenp.prettier-vscode", 7 | "davidanson.vscode-markdownlint", 8 | "yzhang.markdown-all-in-one", 9 | "tamasfe.even-better-toml", 10 | "redhat.vscode-yaml", 11 | "ms-vscode.powershell", 12 | "sumneko.lua", 13 | "bmalehorn.vscode-fish", 14 | "elves.elvish", 15 | "jnoortheen.xonsh" 16 | ] 17 | } 18 | -------------------------------------------------------------------------------- /.vscode/settings.json: -------------------------------------------------------------------------------- 1 | { 2 | "go.lintTool": "golangci-lint", 3 | "go.useLanguageServer": true, 4 | "go.lintFlags": ["--fast"], 5 | "go.testOnSave": true, 6 | "[go]": { 7 | "editor.formatOnSave": true, 8 | "editor.codeActionsOnSave": { 9 | "source.organizeImports": "explicit" 10 | } 11 | }, 12 | "go.formatTool": "gofmt", 13 | "go.formatFlags": [ 14 | "-s" 15 | ], 16 | "azureFunctions.deploySubpath": "docs/api", 17 | "azureFunctions.postDeployTask": "npm install (functions)", 18 | "azureFunctions.projectLanguage": "JavaScript", 19 | "azureFunctions.projectRuntime": "~4", 20 | "debug.internalConsoleOptions": "neverOpen", 21 | "azureFunctions.projectSubpath": "docs/api", 22 | "azureFunctions.preDeployTask": "npm prune (functions)" 23 | } 24 | -------------------------------------------------------------------------------- /COPYING: -------------------------------------------------------------------------------- 1 | Copyright 2022 Jan De Dobbeleer 2 | 3 | Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated 4 | documentation files (the "Software"), to deal in the Software without restriction, including without limitation 5 | the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, 6 | and to permit persons to whom the Software is furnished to do so, subject to the following conditions: 7 | 8 | The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. 9 | 10 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE 11 | WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR 12 | COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, 13 | ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 14 | -------------------------------------------------------------------------------- /SECURITY.md: -------------------------------------------------------------------------------- 1 | # Security Policy 2 | 3 | ## Supported Versions 4 | 5 | Only the latest [release][releases] is supported. 6 | 7 | ## Reporting a Vulnerability 8 | 9 | Vulnerabilities can be sent in via [email][email] to avoid publishing in the open. 10 | Oh My Posh does not have a bounty program, neither do we respond to bug bounties. 11 | 12 | For valid security concerns, you can expect a response within 48 hours, 13 | and credit is given once an acceptable fix is found and published. 14 | 15 | [releases]: https://github.com/JanDeDobbeleer/oh-my-posh/releases 16 | [email]: mailto:security@ohmyposh.dev 17 | -------------------------------------------------------------------------------- /build/post.ps1: -------------------------------------------------------------------------------- 1 | # Description: Post build script to compress the themes and generate SHA256 hashes for all files in the dist folder 2 | 3 | # Compress all themes 4 | $compress = @{ 5 | Path = "../themes/*.omp.*" 6 | CompressionLevel = "Fastest" 7 | DestinationPath = "../src/dist/themes.zip" 8 | } 9 | Compress-Archive @compress 10 | 11 | # Generate SHA256 hashes for all files in the dist folder 12 | Get-ChildItem ./dist -Exclude *.yaml, *.sig | Get-Unique | 13 | Foreach-Object { 14 | $zipHash = Get-FileHash $_.FullName -Algorithm SHA256 15 | $zipHash.Hash | Out-File -Encoding 'UTF8' "../src/dist/$($_.Name).sha256" 16 | } 17 | -------------------------------------------------------------------------------- /build/pre.ps1: -------------------------------------------------------------------------------- 1 | Param 2 | ( 3 | [string] 4 | $Version, 5 | [parameter(Mandatory = $false)] 6 | [string] 7 | $SDKVersion = "10.0.22621.0" 8 | ) 9 | 10 | git config --global user.name "GitHub Actions" 11 | git config --global user.email "41898282+github-actions[bot]@users.noreply.github.com" 12 | git tag $Version --force 13 | 14 | $PSDefaultParameterValues['Out-File:Encoding'] = 'UTF8' 15 | 16 | $shaSigningKeyLocation = Join-Path -Path $env:RUNNER_TEMP -ChildPath sha_signing_key.pem 17 | $env:SIGNING_KEY > $shaSigningKeyLocation 18 | Write-Output "SHA_SIGNING_KEY_LOCATION=$shaSigningKeyLocation" | Out-File -FilePath $env:GITHUB_ENV -Encoding utf8 -Append 19 | 20 | # install code signing dlib 21 | nuget.exe install Microsoft.Trusted.Signing.Client -Version 1.0.60 -ExcludeVersion -OutputDirectory $env:RUNNER_TEMP 22 | Write-Output "SIGNTOOLDLIB=$env:RUNNER_TEMP/Microsoft.Trusted.Signing.Client/bin/x64/Azure.CodeSigning.Dlib.dll" | Out-File -FilePath $env:GITHUB_ENV -Encoding utf8 -Append 23 | 24 | # requires Windows Dev Kit 10.0.22621.0 25 | $signtool = "C:/Program Files (x86)/Windows Kits/10/bin/$SDKVersion/x64/signtool.exe" 26 | Write-Output "SIGNTOOL=$signtool" | Out-File -FilePath $env:GITHUB_ENV -Encoding utf8 -Append 27 | 28 | # openssl 29 | $openssl = 'C:/Program Files/Git/usr/bin/openssl.exe' 30 | Write-Output "OPENSSL=$openssl" | Out-File -FilePath $env:GITHUB_ENV -Encoding utf8 -Append 31 | -------------------------------------------------------------------------------- /packages/msi/README.md: -------------------------------------------------------------------------------- 1 | # MSI Package 2 | 3 | ## Prerequisites 4 | 5 | - [dotnet] 6 | - [wix]: `dotnet tool install --global wix` 7 | 8 | ## Build the package 9 | 10 | This guide assumes and advices the use of PowerShell as your shell environment for this purpose. 11 | 12 | ### Set the environment variables 13 | 14 | ```powershell 15 | $env:VERSION = "1.3.37" 16 | ``` 17 | 18 | ### Build the installer 19 | 20 | ```powershell 21 | wix build -arch arm64 -out install-arm64.msi 22 | ``` 23 | 24 | ## Install the package 25 | 26 | ### For the current user 27 | 28 | ```powershell 29 | install-arm64.msi 30 | ``` 31 | 32 | ### For all users 33 | 34 | ```powershell 35 | install-arm64.msi ALLUSERS=1 36 | ``` 37 | 38 | [dotnet]: https://dotnet.microsoft.com/en-us/download/dotnet?cid=getdotnetcorecli 39 | [wix]: https://wixtoolset.org/docs/intro/ 40 | -------------------------------------------------------------------------------- /packages/msi/icon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/JanDeDobbeleer/oh-my-posh/84cb3af96245c6f890e4273a3f6c2eaee692b04a/packages/msi/icon.ico -------------------------------------------------------------------------------- /packages/winget/JanDeDobbeleer.OhMyPosh.locale.en-US.yaml: -------------------------------------------------------------------------------- 1 | PackageIdentifier: JanDeDobbeleer.OhMyPosh 2 | PackageVersion: 3 | PackageLocale: en-US 4 | Publisher: Jan De Dobbeleer 5 | PublisherUrl: https://github.com/JanDeDobbeleer/oh-my-posh/ 6 | PublisherSupportUrl: https://github.com/JanDeDobbeleer/oh-my-posh/issues 7 | Author: Jan De Dobbeleer 8 | PackageName: Oh My Posh 9 | PackageUrl: https://ohmyposh.dev/ 10 | License: MIT 11 | LicenseUrl: https://github.com/JanDeDobbeleer/oh-my-posh/raw/main/COPYING 12 | ShortDescription: Prompt theme engine for any shell 13 | Moniker: oh-my-posh 14 | Tags: 15 | - "console" 16 | - "command-line" 17 | - "shell" 18 | - "command-prompt" 19 | - "powershell" 20 | - "wsl" 21 | - "developer-tools" 22 | - "utilities" 23 | - "cli" 24 | - "cmd" 25 | - "ps" 26 | - "terminal" 27 | - "oh-my-posh" 28 | ManifestType: defaultLocale 29 | ManifestVersion: 1.6.0 30 | ReleaseNotesUrl: https://github.com/JanDeDobbeleer/oh-my-posh/releases/tag/v 31 | -------------------------------------------------------------------------------- /packages/winget/JanDeDobbeleer.OhMyPosh.yaml: -------------------------------------------------------------------------------- 1 | PackageIdentifier: JanDeDobbeleer.OhMyPosh 2 | PackageVersion: 3 | DefaultLocale: en-US 4 | ManifestType: version 5 | ManifestVersion: 1.6.0 6 | -------------------------------------------------------------------------------- /src/.golangci.yml: -------------------------------------------------------------------------------- 1 | version: "2" 2 | run: 3 | allow-parallel-runners: true 4 | linters: 5 | default: none 6 | enable: 7 | - bodyclose 8 | - copyloopvar 9 | - dupl 10 | - errcheck 11 | - exhaustive 12 | - goconst 13 | - gocritic 14 | - gocyclo 15 | - goprintffuncname 16 | - govet 17 | - ineffassign 18 | - lll 19 | - misspell 20 | - nakedret 21 | - noctx 22 | - nolintlint 23 | - revive 24 | - rowserrcheck 25 | - staticcheck 26 | - unconvert 27 | - unparam 28 | - unused 29 | - whitespace 30 | settings: 31 | gocritic: 32 | enabled-tags: 33 | - diagnostic 34 | - opinionated 35 | - performance 36 | - style 37 | disabled-tags: 38 | - experimental 39 | lll: 40 | line-length: 180 41 | exclusions: 42 | generated: lax 43 | presets: 44 | - comments 45 | - common-false-positives 46 | - legacy 47 | - std-error-handling 48 | paths: 49 | - third_party$ 50 | - builtin$ 51 | - examples$ 52 | formatters: 53 | enable: 54 | - gofmt 55 | - goimports 56 | exclusions: 57 | generated: lax 58 | paths: 59 | - third_party$ 60 | - builtin$ 61 | - examples$ 62 | -------------------------------------------------------------------------------- /src/build/version.go: -------------------------------------------------------------------------------- 1 | package build 2 | 3 | import "time" 4 | 5 | var ( 6 | Date = time.Now().UTC().String() 7 | Version = "dev" 8 | ) 9 | -------------------------------------------------------------------------------- /src/cache/clear.go: -------------------------------------------------------------------------------- 1 | package cache 2 | 3 | import ( 4 | "os" 5 | "path/filepath" 6 | "strings" 7 | ) 8 | 9 | func Clear(cachePath string, force bool) ([]string, error) { 10 | // get all files in the cache directory that start with omp.cache and delete them 11 | files, err := os.ReadDir(cachePath) 12 | if err != nil { 13 | return []string{}, err 14 | } 15 | 16 | var removed []string 17 | 18 | deleteFile := func(file string) { 19 | path := filepath.Join(cachePath, file) 20 | if err := os.Remove(path); err == nil { 21 | removed = append(removed, path) 22 | } 23 | } 24 | 25 | for _, file := range files { 26 | if file.IsDir() { 27 | continue 28 | } 29 | 30 | if !strings.HasPrefix(file.Name(), FileName) && !strings.HasPrefix(file.Name(), "init.") { 31 | continue 32 | } 33 | 34 | if force { 35 | deleteFile(file.Name()) 36 | continue 37 | } 38 | 39 | // don't delete the system cache file unless forced 40 | if file.Name() == FileName { 41 | continue 42 | } 43 | 44 | info, err := file.Info() 45 | if err != nil { 46 | continue 47 | } 48 | 49 | if info.ModTime().AddDate(0, 0, 7).After(info.ModTime()) { 50 | continue 51 | } 52 | 53 | deleteFile(file.Name()) 54 | } 55 | 56 | return removed, nil 57 | } 58 | -------------------------------------------------------------------------------- /src/cache/command.go: -------------------------------------------------------------------------------- 1 | package cache 2 | 3 | import ( 4 | "github.com/jandedobbeleer/oh-my-posh/src/maps" 5 | ) 6 | 7 | type Command struct { 8 | Commands *maps.Concurrent 9 | } 10 | 11 | func (c *Command) Set(command, path string) { 12 | c.Commands.Set(command, path) 13 | } 14 | 15 | func (c *Command) Get(command string) (string, bool) { 16 | cacheCommand, found := c.Commands.Get(command) 17 | if !found { 18 | return "", false 19 | } 20 | command, ok := cacheCommand.(string) 21 | return command, ok 22 | } 23 | -------------------------------------------------------------------------------- /src/cache/config.go: -------------------------------------------------------------------------------- 1 | package cache 2 | 3 | type Config struct { 4 | Duration Duration `json:"duration,omitempty" toml:"duration,omitempty" yaml:"duration,omitempty"` 5 | Strategy Strategy `json:"strategy,omitempty" toml:"strategy,omitempty" yaml:"strategy,omitempty"` 6 | } 7 | 8 | type Strategy string 9 | 10 | const ( 11 | Folder Strategy = "folder" 12 | Session Strategy = "session" 13 | ) 14 | -------------------------------------------------------------------------------- /src/cache/duration.go: -------------------------------------------------------------------------------- 1 | package cache 2 | 3 | import ( 4 | "time" 5 | ) 6 | 7 | type Duration string 8 | 9 | const ( 10 | INFINITE = Duration("infinite") 11 | NONE = Duration("none") 12 | ONEWEEK = Duration("168h") 13 | ONEDAY = Duration("24h") 14 | TWOYEARS = Duration("17520h") 15 | ) 16 | 17 | func (d Duration) Seconds() int { 18 | if d == NONE { 19 | return 0 20 | } 21 | 22 | if d == INFINITE { 23 | return -1 24 | } 25 | 26 | duration, err := time.ParseDuration(string(d)) 27 | if err != nil { 28 | return 0 29 | } 30 | 31 | return int(duration.Seconds()) 32 | } 33 | 34 | func (d Duration) IsEmpty() bool { 35 | return len(d) == 0 36 | } 37 | 38 | func ToDuration(seconds int) Duration { 39 | if seconds == 0 { 40 | return "" 41 | } 42 | 43 | if seconds == -1 { 44 | return INFINITE 45 | } 46 | 47 | duration := time.Duration(seconds) * time.Second 48 | return Duration(duration.String()) 49 | } 50 | -------------------------------------------------------------------------------- /src/cache/duration_test.go: -------------------------------------------------------------------------------- 1 | package cache 2 | 3 | import ( 4 | "testing" 5 | 6 | "github.com/stretchr/testify/assert" 7 | ) 8 | 9 | func TestSeconds(t *testing.T) { 10 | cases := []struct { 11 | Case string 12 | Duration Duration 13 | Expected int 14 | }{ 15 | { 16 | Case: "2 seconds", 17 | Duration: "2s", 18 | Expected: 2, 19 | }, 20 | { 21 | Case: "1 minute", 22 | Duration: "1m", 23 | Expected: 60, 24 | }, 25 | { 26 | Case: "2 hours", 27 | Duration: "2h", 28 | Expected: 7200, 29 | }, 30 | { 31 | Case: "2 days", 32 | Duration: "48h", 33 | Expected: 172800, 34 | }, 35 | { 36 | Case: "invalid", 37 | Duration: "foo", 38 | Expected: 0, 39 | }, 40 | { 41 | Case: "1 fortnight", 42 | Duration: "1fortnight", 43 | Expected: 0, 44 | }, 45 | { 46 | Case: "infinite", 47 | Duration: "infinite", 48 | Expected: -1, 49 | }, 50 | } 51 | for _, tc := range cases { 52 | got := tc.Duration.Seconds() 53 | assert.Equal(t, tc.Expected, got, tc.Case) 54 | } 55 | } 56 | -------------------------------------------------------------------------------- /src/cache/mock/cache.go: -------------------------------------------------------------------------------- 1 | package mock 2 | 3 | import ( 4 | "github.com/jandedobbeleer/oh-my-posh/src/cache" 5 | mock "github.com/stretchr/testify/mock" 6 | ) 7 | 8 | type Cache struct { 9 | mock.Mock 10 | } 11 | 12 | func (_m *Cache) Init(filePath string, persist bool) { 13 | _m.Called(filePath, persist) 14 | } 15 | 16 | func (_m *Cache) Close() { 17 | _m.Called() 18 | } 19 | 20 | func (_m *Cache) Get(key string) (string, bool) { 21 | ret := _m.Called(key) 22 | 23 | var r0 string 24 | if rf, ok := ret.Get(0).(func(string) string); ok { 25 | r0 = rf(key) 26 | } else { 27 | r0 = ret.Get(0).(string) 28 | } 29 | 30 | var r1 bool 31 | if rf, ok := ret.Get(1).(func(string) bool); ok { 32 | r1 = rf(key) 33 | } else { 34 | r1 = ret.Get(1).(bool) 35 | } 36 | 37 | return r0, r1 38 | } 39 | 40 | // set provides a mock function with given fields: key, value, ttl 41 | func (_m *Cache) Set(key, value string, duration cache.Duration) { 42 | _m.Called(key, value, duration) 43 | } 44 | 45 | func (_m *Cache) Delete(key string) { 46 | _m.Called(key) 47 | } 48 | -------------------------------------------------------------------------------- /src/cache/template.go: -------------------------------------------------------------------------------- 1 | package cache 2 | 3 | import ( 4 | "github.com/jandedobbeleer/oh-my-posh/src/maps" 5 | ) 6 | 7 | type Template struct { 8 | SegmentsCache maps.Simple 9 | Segments *maps.Concurrent 10 | Var maps.Simple 11 | PWD string 12 | Folder string 13 | PSWD string 14 | UserName string 15 | HostName string 16 | ShellVersion string 17 | Shell string 18 | AbsolutePWD string 19 | OS string 20 | Version string 21 | PromptCount int 22 | SHLVL int 23 | Jobs int 24 | Code int 25 | WSL bool 26 | Root bool 27 | } 28 | 29 | func (t *Template) AddSegmentData(key string, value any) { 30 | t.Segments.Set(key, value) 31 | } 32 | 33 | func (t *Template) RemoveSegmentData(key string) { 34 | t.Segments.Delete(key) 35 | } 36 | -------------------------------------------------------------------------------- /src/cli/args.go: -------------------------------------------------------------------------------- 1 | package cli 2 | 3 | import "github.com/spf13/cobra" 4 | 5 | func NoArgsOrOneValidArg(cmd *cobra.Command, args []string) error { 6 | if len(args) == 0 { 7 | return nil 8 | } 9 | if err := cobra.ExactArgs(1)(cmd, args); err != nil { 10 | return err 11 | } 12 | return cobra.OnlyValidArgs(cmd, args) 13 | } 14 | -------------------------------------------------------------------------------- /src/cli/cache.go: -------------------------------------------------------------------------------- 1 | package cli 2 | 3 | import ( 4 | "fmt" 5 | "path/filepath" 6 | 7 | "github.com/jandedobbeleer/oh-my-posh/src/cache" 8 | 9 | "github.com/spf13/cobra" 10 | ) 11 | 12 | // getCmd represents the get command 13 | var getCache = &cobra.Command{ 14 | Use: "cache [path|clear|edit]", 15 | Short: "Interact with the oh-my-posh cache", 16 | Long: `Interact with the oh-my-posh cache. 17 | 18 | You can do the following: 19 | 20 | - path: list cache path 21 | - clear: remove all cache values 22 | - edit: edit cache values`, 23 | ValidArgs: []string{ 24 | "path", 25 | "clear", 26 | "edit", 27 | }, 28 | Args: NoArgsOrOneValidArg, 29 | Run: func(cmd *cobra.Command, args []string) { 30 | if len(args) == 0 { 31 | _ = cmd.Help() 32 | return 33 | } 34 | 35 | switch args[0] { 36 | case "path": 37 | fmt.Println(cache.Path()) 38 | case "clear": 39 | deletedFiles, err := cache.Clear(cache.Path(), true) 40 | if err != nil { 41 | fmt.Println(err) 42 | return 43 | } 44 | 45 | for _, file := range deletedFiles { 46 | fmt.Println("removed cache file:", file) 47 | } 48 | case "edit": 49 | cacheFilePath := filepath.Join(cache.Path(), cache.FileName) 50 | exitcode = editFileWithEditor(cacheFilePath) 51 | } 52 | }, 53 | } 54 | 55 | func init() { 56 | RootCmd.AddCommand(getCache) 57 | } 58 | -------------------------------------------------------------------------------- /src/cli/config.go: -------------------------------------------------------------------------------- 1 | package cli 2 | 3 | import ( 4 | "fmt" 5 | "time" 6 | 7 | "github.com/jandedobbeleer/oh-my-posh/src/config" 8 | 9 | "github.com/spf13/cobra" 10 | ) 11 | 12 | // configCmd represents the config command 13 | var configCmd = &cobra.Command{ 14 | Use: "config edit", 15 | Short: "Interact with the config", 16 | Long: `Interact with the config. 17 | 18 | You can export, migrate or edit the config (via the editor specified in the environment variable "EDITOR").`, 19 | ValidArgs: []string{ 20 | "export", 21 | "migrate", 22 | "edit", 23 | "get", 24 | }, 25 | Args: NoArgsOrOneValidArg, 26 | Run: func(cmd *cobra.Command, args []string) { 27 | if len(args) == 0 { 28 | _ = cmd.Help() 29 | return 30 | } 31 | switch args[0] { 32 | case "edit": 33 | path := config.Path((configFlag)) 34 | exitcode = editFileWithEditor(path) 35 | case "get": 36 | // only here for backwards compatibility 37 | fmt.Print(time.Now().UnixNano() / 1000000) 38 | default: 39 | _ = cmd.Help() 40 | } 41 | }, 42 | } 43 | 44 | func init() { 45 | RootCmd.AddCommand(configCmd) 46 | } 47 | -------------------------------------------------------------------------------- /src/cli/disable.go: -------------------------------------------------------------------------------- 1 | package cli 2 | 3 | import ( 4 | "fmt" 5 | 6 | "github.com/spf13/cobra" 7 | ) 8 | 9 | // getCmd represents the get command 10 | var disableCmd = &cobra.Command{ 11 | Use: fmt.Sprintf(toggleUse, "disable"), 12 | Short: "Disable a feature", 13 | Long: fmt.Sprintf(toggleLong, "Disable"), 14 | ValidArgs: toggleArgs, 15 | Args: NoArgsOrOneValidArg, 16 | Run: func(cmd *cobra.Command, args []string) { 17 | if len(args) == 0 { 18 | _ = cmd.Help() 19 | return 20 | } 21 | toggleFeature(cmd, args[0], false) 22 | }, 23 | } 24 | 25 | func init() { 26 | RootCmd.AddCommand(disableCmd) 27 | } 28 | -------------------------------------------------------------------------------- /src/cli/edit.go: -------------------------------------------------------------------------------- 1 | package cli 2 | 3 | import ( 4 | "fmt" 5 | "os" 6 | "os/exec" 7 | "strings" 8 | ) 9 | 10 | func editFileWithEditor(file string) int { 11 | editor := strings.TrimSpace(os.Getenv("EDITOR")) 12 | if len(editor) == 0 { 13 | fmt.Println(`no editor specified in the environment variable "EDITOR"`) 14 | return 1 15 | } 16 | 17 | editor = strings.TrimSpace(editor) 18 | args := strings.Split(editor, " ") 19 | 20 | editor = args[0] 21 | args = append(args[1:], file) 22 | 23 | cmd := exec.Command(editor, args...) 24 | 25 | cmd.Stdin = os.Stdin 26 | cmd.Stdout = os.Stdout 27 | cmd.Stderr = os.Stderr 28 | 29 | if err := cmd.Run(); err != nil { 30 | fmt.Println(err.Error()) 31 | return 1 32 | } 33 | 34 | return 0 35 | } 36 | -------------------------------------------------------------------------------- /src/cli/notice.go: -------------------------------------------------------------------------------- 1 | package cli 2 | 3 | import ( 4 | "fmt" 5 | "os" 6 | 7 | "github.com/jandedobbeleer/oh-my-posh/src/config" 8 | "github.com/jandedobbeleer/oh-my-posh/src/runtime" 9 | "github.com/spf13/cobra" 10 | ) 11 | 12 | // noticeCmd represents the get command 13 | var noticeCmd = &cobra.Command{ 14 | Use: "notice", 15 | Short: "Print the upgrade notice when a new version is available.", 16 | Long: "Print the upgrade notice when a new version is available.", 17 | Args: cobra.NoArgs, 18 | Run: func(_ *cobra.Command, _ []string) { 19 | flags := &runtime.Flags{ 20 | SaveCache: true, 21 | } 22 | 23 | env := &runtime.Terminal{} 24 | env.Init(flags) 25 | defer env.Close() 26 | 27 | sh := os.Getenv("POSH_SHELL") 28 | configFile := config.Path(configFlag) 29 | cfg, _ := config.Load(configFile, sh, false) 30 | cfg.Upgrade.Cache = env.Cache() 31 | 32 | if notice, hasNotice := cfg.Upgrade.Notice(); hasNotice { 33 | fmt.Println(notice) 34 | } 35 | }, 36 | } 37 | 38 | func init() { 39 | RootCmd.AddCommand(noticeCmd) 40 | } 41 | -------------------------------------------------------------------------------- /src/cli/prompt.go: -------------------------------------------------------------------------------- 1 | package cli 2 | 3 | import ( 4 | "github.com/spf13/cobra" 5 | ) 6 | 7 | // promptCmd represents the prompt command 8 | var promptCmd = &cobra.Command{ 9 | Use: "prompt", 10 | Short: "Set up the prompt for your shell (deprecated)", 11 | Long: `Set up the prompt for your shell. (deprecated)`, 12 | Hidden: true, 13 | Args: cobra.NoArgs, 14 | Run: func(cmd *cobra.Command, _ []string) { 15 | _ = cmd.Help() 16 | }, 17 | } 18 | 19 | func init() { 20 | // legacy support 21 | promptCmd.AddCommand(createInitCmd()) 22 | promptCmd.AddCommand(createDebugCmd()) 23 | promptCmd.AddCommand(createPrintCmd()) 24 | RootCmd.AddCommand(promptCmd) 25 | } 26 | -------------------------------------------------------------------------------- /src/cli/version.go: -------------------------------------------------------------------------------- 1 | package cli 2 | 3 | import ( 4 | "fmt" 5 | 6 | "github.com/jandedobbeleer/oh-my-posh/src/build" 7 | "github.com/spf13/cobra" 8 | ) 9 | 10 | var ( 11 | verbose bool 12 | ) 13 | 14 | // versionCmd represents the version command 15 | var versionCmd = &cobra.Command{ 16 | Use: "version", 17 | Short: "Print the version", 18 | Long: "Print the version number of oh-my-posh.", 19 | Args: cobra.NoArgs, 20 | Run: func(_ *cobra.Command, _ []string) { 21 | if !verbose { 22 | fmt.Println(build.Version) 23 | return 24 | } 25 | fmt.Println("Version: ", build.Version) 26 | fmt.Println("Date: ", build.Date) 27 | }, 28 | } 29 | 30 | func init() { 31 | versionCmd.Flags().BoolVarP(&verbose, "verbose", "v", false, "write verbose output") 32 | RootCmd.AddCommand(versionCmd) 33 | } 34 | -------------------------------------------------------------------------------- /src/color/colors_darwin.go: -------------------------------------------------------------------------------- 1 | package color 2 | 3 | import ( 4 | "errors" 5 | "strconv" 6 | 7 | "github.com/jandedobbeleer/oh-my-posh/src/log" 8 | "github.com/jandedobbeleer/oh-my-posh/src/runtime" 9 | ) 10 | 11 | func GetAccentColor(env runtime.Environment) (*RGB, error) { 12 | output, err := env.RunCommand("defaults", "read", "-g", "AppleAccentColor") 13 | if err != nil { 14 | log.Error(err) 15 | return nil, errors.New("unable to read accent color") 16 | } 17 | 18 | index, err := strconv.Atoi(output) 19 | if err != nil { 20 | log.Error(err) 21 | return nil, errors.New("unable to parse accent color index") 22 | } 23 | 24 | var accentColors = map[int]RGB{ 25 | -1: {152, 152, 152}, // Graphite 26 | 0: {224, 55, 62}, // Red 27 | 1: {247, 130, 25}, // Orange 28 | 2: {255, 199, 38}, // Yellow 29 | 3: {96, 186, 70}, // Green 30 | 4: {0, 122, 255}, // Blue 31 | 5: {149, 61, 150}, // Purple 32 | 6: {247, 79, 159}, // Pink 33 | } 34 | 35 | color, exists := accentColors[index] 36 | if !exists { 37 | color = accentColors[6] // Default to graphite (white) 38 | } 39 | 40 | return &color, nil 41 | } 42 | -------------------------------------------------------------------------------- /src/color/colors_unix.go: -------------------------------------------------------------------------------- 1 | //go:build !windows && !darwin 2 | 3 | package color 4 | 5 | import "github.com/jandedobbeleer/oh-my-posh/src/runtime" 6 | 7 | func GetAccentColor(_ runtime.Environment) (*RGB, error) { 8 | return nil, &runtime.NotImplemented{} 9 | } 10 | -------------------------------------------------------------------------------- /src/color/colors_windows.go: -------------------------------------------------------------------------------- 1 | package color 2 | 3 | import ( 4 | "errors" 5 | "time" 6 | 7 | "github.com/jandedobbeleer/oh-my-posh/src/log" 8 | "github.com/jandedobbeleer/oh-my-posh/src/runtime" 9 | ) 10 | 11 | func GetAccentColor(env runtime.Environment) (*RGB, error) { 12 | defer log.Trace(time.Now()) 13 | 14 | if env == nil { 15 | return nil, errors.New("unable to get color without environment") 16 | } 17 | 18 | // see https://stackoverflow.com/questions/3560890/vista-7-how-to-get-glass-color 19 | value, err := env.WindowsRegistryKeyValue(`HKEY_CURRENT_USER\Software\Microsoft\Windows\DWM\ColorizationColor`) 20 | if err != nil || value.ValueType != runtime.DWORD { 21 | return nil, err 22 | } 23 | 24 | return &RGB{ 25 | R: byte(value.DWord >> 16), 26 | G: byte(value.DWord >> 8), 27 | B: byte(value.DWord), 28 | }, nil 29 | } 30 | -------------------------------------------------------------------------------- /src/color/cycle.go: -------------------------------------------------------------------------------- 1 | package color 2 | 3 | type Cycle []*Set 4 | 5 | func (c Cycle) Loop() (*Set, Cycle) { 6 | if len(c) == 0 { 7 | return nil, c 8 | } 9 | return c[0], append(c[1:], c[0]) 10 | } 11 | -------------------------------------------------------------------------------- /src/color/palettes.go: -------------------------------------------------------------------------------- 1 | package color 2 | 3 | type Palettes struct { 4 | List map[string]Palette `json:"list,omitempty" toml:"list,omitempty" yaml:"list,omitempty"` 5 | Template string `json:"template,omitempty" toml:"template,omitempty" yaml:"template,omitempty"` 6 | } 7 | -------------------------------------------------------------------------------- /src/config/migrate_glyphs_test.go: -------------------------------------------------------------------------------- 1 | package config 2 | 3 | import ( 4 | "testing" 5 | 6 | "github.com/stretchr/testify/assert" 7 | ) 8 | 9 | func TestGetCodePoints(t *testing.T) { 10 | codepoints, err := getGlyphCodePoints() 11 | if connectionError, ok := err.(*ConnectionError); ok { 12 | t.Log(connectionError.Error()) 13 | return 14 | } 15 | assert.Equal(t, 1939, len(codepoints)) 16 | } 17 | 18 | func TestEscapeGlyphs(t *testing.T) { 19 | cases := []struct { 20 | Input string 21 | Expected string 22 | }{ 23 | {Input: "󰉋", Expected: "\\udb80\\ude4b"}, 24 | {Input: "a", Expected: "a"}, 25 | {Input: "\ue0b4", Expected: "\\ue0b4"}, 26 | {Input: "\ufd03", Expected: "\\ufd03"}, 27 | {Input: "}", Expected: "}"}, 28 | {Input: "🏚", Expected: "🏚"}, 29 | {Input: "\U000F011B", Expected: "\\udb80\\udd1b"}, 30 | {Input: "󰄛", Expected: "\\udb80\\udd1b"}, 31 | } 32 | for _, tc := range cases { 33 | assert.Equal(t, tc.Expected, escapeGlyphs(tc.Input, false), tc.Input) 34 | } 35 | } 36 | -------------------------------------------------------------------------------- /src/config/responsive.go: -------------------------------------------------------------------------------- 1 | package config 2 | 3 | import "github.com/jandedobbeleer/oh-my-posh/src/runtime" 4 | 5 | func shouldHideForWidth(env runtime.Environment, minWidth, maxWidth int) bool { 6 | if maxWidth == 0 && minWidth == 0 { 7 | return false 8 | } 9 | width, err := env.TerminalWidth() 10 | if err != nil { 11 | return false 12 | } 13 | if minWidth > 0 && maxWidth > 0 { 14 | return width < minWidth || width > maxWidth 15 | } 16 | if maxWidth > 0 && width > maxWidth { 17 | return true 18 | } 19 | if minWidth > 0 && width < minWidth { 20 | return true 21 | } 22 | return false 23 | } 24 | -------------------------------------------------------------------------------- /src/config/responsive_test.go: -------------------------------------------------------------------------------- 1 | package config 2 | 3 | import ( 4 | "testing" 5 | 6 | "github.com/jandedobbeleer/oh-my-posh/src/runtime/mock" 7 | 8 | "github.com/stretchr/testify/assert" 9 | ) 10 | 11 | func TestShouldHideForWidth(t *testing.T) { 12 | cases := []struct { 13 | Error error 14 | Case string 15 | MinWidth int 16 | MaxWidth int 17 | Width int 18 | Expected bool 19 | }{ 20 | {Case: "No settings"}, 21 | {Case: "Min cols - hide", MinWidth: 10, Width: 9, Expected: true}, 22 | {Case: "Min cols - show", MinWidth: 10, Width: 20, Expected: false}, 23 | {Case: "Max cols - hide", MaxWidth: 10, Width: 11, Expected: true}, 24 | {Case: "Max cols - show", MaxWidth: 10, Width: 8, Expected: false}, 25 | {Case: "Min & Max cols - hide", MinWidth: 10, MaxWidth: 20, Width: 21, Expected: true}, 26 | {Case: "Min & Max cols - hide 2", MinWidth: 10, MaxWidth: 20, Width: 8, Expected: true}, 27 | {Case: "Min & Max cols - show", MinWidth: 10, MaxWidth: 20, Width: 11, Expected: false}, 28 | } 29 | for _, tc := range cases { 30 | env := new(mock.Environment) 31 | env.On("TerminalWidth").Return(tc.Width, tc.Error) 32 | got := shouldHideForWidth(env, tc.MinWidth, tc.MaxWidth) 33 | assert.Equal(t, tc.Expected, got, tc.Case) 34 | } 35 | } 36 | -------------------------------------------------------------------------------- /src/constants/constants_unix.go: -------------------------------------------------------------------------------- 1 | //go:build !windows 2 | 3 | package constants 4 | 5 | const ( 6 | DotnetExitCode = 142 7 | ) 8 | -------------------------------------------------------------------------------- /src/constants/constants_windows64.go: -------------------------------------------------------------------------------- 1 | //go:build windows && !386 2 | 3 | package constants 4 | 5 | const ( 6 | DotnetExitCode = int(0x80008091) 7 | ) 8 | -------------------------------------------------------------------------------- /src/constants/constrants_windows386.go: -------------------------------------------------------------------------------- 1 | //go:build windows && 386 2 | 3 | package constants 4 | 5 | const ( 6 | DotnetExitCode = -2147450735 7 | ) 8 | -------------------------------------------------------------------------------- /src/font/install_darwin.go: -------------------------------------------------------------------------------- 1 | // Derived from https://github.com/Crosse/font-install 2 | // Copyright 2020 Seth Wright 3 | package font 4 | 5 | import ( 6 | "os" 7 | "path" 8 | ) 9 | 10 | var FontsDir = path.Join(os.Getenv("HOME"), "Library", "Fonts") 11 | 12 | func install(font *Font, _ bool) error { 13 | // On darwin/OSX, the user's fonts directory is ~/Library/Fonts, 14 | // and fonts should be installed directly into that path; 15 | // i.e., not in subfolders. 16 | fullPath := path.Join(FontsDir, path.Base(font.FileName)) 17 | 18 | if err := os.MkdirAll(path.Dir(fullPath), 0700); err != nil { 19 | return err 20 | } 21 | 22 | return os.WriteFile(fullPath, font.Data, 0644) 23 | } 24 | -------------------------------------------------------------------------------- /src/font/install_unix.go: -------------------------------------------------------------------------------- 1 | //go:build !windows && !darwin 2 | 3 | // Derived from https://github.com/Crosse/font-install 4 | // Copyright 2020 Seth Wright 5 | package font 6 | 7 | import ( 8 | "os" 9 | "path" 10 | "strings" 11 | ) 12 | 13 | var ( 14 | fontsDir = path.Join(os.Getenv("HOME"), "/.local/share/fonts") 15 | systemFontsDir = "/usr/share/fonts" 16 | ) 17 | 18 | func install(font *Font, _ bool) error { 19 | // If we're running as root, install the font system-wide. 20 | targetDir := fontsDir 21 | if os.Geteuid() == 0 { 22 | targetDir = systemFontsDir 23 | } 24 | 25 | // On Linux, fontconfig can understand subdirectories. So, to keep the 26 | // font directory clean, install all font files for a particular font 27 | // family into a subdirectory named after the family (with hyphens instead 28 | // of spaces). 29 | fullPath := path.Join(targetDir, 30 | strings.ToLower(strings.ReplaceAll(font.Family, " ", "-")), 31 | path.Base(font.FileName)) 32 | 33 | if err := os.MkdirAll(path.Dir(fullPath), 0700); err != nil { 34 | return err 35 | } 36 | 37 | return os.WriteFile(fullPath, font.Data, 0644) 38 | } 39 | -------------------------------------------------------------------------------- /src/image/image_test.go: -------------------------------------------------------------------------------- 1 | package image 2 | 3 | import ( 4 | "testing" 5 | 6 | "github.com/stretchr/testify/assert" 7 | ) 8 | 9 | func TestSetOutputPath(t *testing.T) { 10 | cases := []struct { 11 | Case string 12 | Config string 13 | Path string 14 | Expected string 15 | }{ 16 | {Case: "default config", Expected: "prompt.png"}, 17 | {Case: "hidden file", Config: ".posh.omp.json", Expected: "posh.png"}, 18 | {Case: "hidden file toml", Config: ".posh.omp.toml", Expected: "posh.png"}, 19 | {Case: "hidden file yaml", Config: ".posh.omp.yaml", Expected: "posh.png"}, 20 | {Case: "hidden file yml", Config: ".posh.omp.yml", Expected: "posh.png"}, 21 | {Case: "path provided", Path: "mytheme.png", Expected: "mytheme.png"}, 22 | {Case: "relative, no omp", Config: "~/jandedobbeleer.json", Expected: "jandedobbeleer.png"}, 23 | {Case: "relative path", Config: "~/jandedobbeleer.omp.json", Expected: "jandedobbeleer.png"}, 24 | {Case: "invalid config name", Config: "~/jandedobbeleer.omp.foo", Expected: "prompt.png"}, 25 | } 26 | 27 | for _, tc := range cases { 28 | image := &Renderer{ 29 | Path: tc.Path, 30 | } 31 | 32 | image.setOutputPath(tc.Config) 33 | 34 | assert.Equal(t, tc.Expected, image.Path, tc.Case) 35 | } 36 | } 37 | -------------------------------------------------------------------------------- /src/main.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "github.com/jandedobbeleer/oh-my-posh/src/cli" 5 | ) 6 | 7 | func main() { 8 | cli.Execute() 9 | } 10 | -------------------------------------------------------------------------------- /src/main_test.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "bytes" 5 | "fmt" 6 | "testing" 7 | 8 | "github.com/jandedobbeleer/oh-my-posh/src/cli" 9 | "github.com/jandedobbeleer/oh-my-posh/src/prompt" 10 | ) 11 | 12 | func BenchmarkInit(b *testing.B) { 13 | cmd := cli.RootCmd 14 | // needs to be a non-existing file as we panic otherwise 15 | cmd.SetArgs([]string{"init", "fish", "--print", "--silent"}) 16 | out := bytes.NewBufferString("") 17 | cmd.SetOut(out) 18 | 19 | for i := 0; i < b.N; i++ { 20 | _ = cmd.Execute() 21 | } 22 | } 23 | 24 | func BenchmarkPrimary(b *testing.B) { 25 | cmd := cli.RootCmd 26 | // needs to be a non-existing file as we panic otherwise 27 | cmd.SetArgs([]string{"print", prompt.PRIMARY, "--pwd", "/Users/jan/Code/oh-my-posh/src", "--shell", "fish", "--silent"}) 28 | out := bytes.NewBufferString("") 29 | cmd.SetOut(out) 30 | 31 | for i := 0; i < b.N; i++ { 32 | _ = cmd.Execute() 33 | } 34 | 35 | fmt.Println("") 36 | } 37 | -------------------------------------------------------------------------------- /src/maps/concurrent.go: -------------------------------------------------------------------------------- 1 | package maps 2 | 3 | import ( 4 | "fmt" 5 | "sync" 6 | 7 | "github.com/jandedobbeleer/oh-my-posh/src/log" 8 | ) 9 | 10 | func NewConcurrent() *Concurrent { 11 | var cm Concurrent 12 | return &cm 13 | } 14 | 15 | type Concurrent sync.Map 16 | 17 | func (cm *Concurrent) Set(key string, value any) { 18 | (*sync.Map)(cm).Store(key, value) 19 | } 20 | 21 | func (cm *Concurrent) Get(key string) (any, bool) { 22 | return (*sync.Map)(cm).Load(key) 23 | } 24 | 25 | func (cm *Concurrent) MustGet(key string) any { 26 | val, OK := (*sync.Map)(cm).Load(key) 27 | if !OK { 28 | log.Error(fmt.Errorf("key %s not found", key)) 29 | } 30 | 31 | return val 32 | } 33 | 34 | func (cm *Concurrent) Delete(key string) { 35 | (*sync.Map)(cm).Delete(key) 36 | } 37 | 38 | func (cm *Concurrent) Contains(key string) bool { 39 | _, ok := (*sync.Map)(cm).Load(key) 40 | return ok 41 | } 42 | 43 | func (cm *Concurrent) ToSimple() Simple { 44 | list := make(map[string]any) 45 | (*sync.Map)(cm).Range(func(key, value any) bool { 46 | if value == nil { 47 | return false 48 | } 49 | 50 | list[key.(string)] = value 51 | return true 52 | }) 53 | 54 | return list 55 | } 56 | -------------------------------------------------------------------------------- /src/maps/config.go: -------------------------------------------------------------------------------- 1 | package maps 2 | 3 | type Config struct { 4 | UserName *Map `json:"user_name,omitempty" toml:"user_name,omitempty" yaml:"user_name,omitempty"` 5 | HostName *Map `json:"host_name,omitempty" toml:"host_name,omitempty" yaml:"host_name,omitempty"` 6 | ShellName *Map `json:"shell_name,omitempty" toml:"shell_name,omitempty" yaml:"shell_name,omitempty"` 7 | } 8 | 9 | func (c *Config) GetUserName(key string) string { 10 | if c == nil || c.UserName == nil { 11 | return key 12 | } 13 | 14 | return c.UserName.Get(key) 15 | } 16 | 17 | func (c *Config) GetHostName(key string) string { 18 | if c == nil || c.HostName == nil { 19 | return key 20 | } 21 | 22 | return c.HostName.Get(key) 23 | } 24 | 25 | func (c *Config) GetShellName(key string) string { 26 | if c == nil || c.ShellName == nil { 27 | return key 28 | } 29 | 30 | return c.ShellName.Get(key) 31 | } 32 | 33 | type Map map[string]string 34 | 35 | func (m *Map) Get(key string) string { 36 | if m == nil { 37 | return key 38 | } 39 | 40 | if value, ok := (*m)[key]; ok { 41 | return value 42 | } 43 | 44 | return key 45 | } 46 | -------------------------------------------------------------------------------- /src/maps/simple.go: -------------------------------------------------------------------------------- 1 | package maps 2 | 3 | type Simple map[string]any 4 | 5 | func (m Simple) ToConcurrent() *Concurrent { 6 | var cm Concurrent 7 | for k, v := range m { 8 | cm.Set(k, v) 9 | } 10 | return &cm 11 | } 12 | -------------------------------------------------------------------------------- /src/metadata.json: -------------------------------------------------------------------------------- 1 | { 2 | "Endpoint": "https://weu.codesigning.azure.net", 3 | "CodeSigningAccountName": "oh-my-posh", 4 | "CertificateProfileName": "oh-my-posh", 5 | "ExcludeCredentials": [ 6 | "AzureCliCredential", 7 | "AzurePowerShellCredential", 8 | "ManagedIdentityCredential", 9 | "SharedTokenCacheCredential", 10 | "VisualStudioCredential", 11 | "VisualStudioCodeCredential", 12 | "InteractiveBrowserCredential" 13 | ] 14 | } 15 | -------------------------------------------------------------------------------- /src/progress/model.go: -------------------------------------------------------------------------------- 1 | package progress 2 | 3 | import ( 4 | "github.com/charmbracelet/bubbles/progress" 5 | tea "github.com/charmbracelet/bubbletea" 6 | "github.com/jandedobbeleer/oh-my-posh/src/terminal" 7 | ) 8 | 9 | type Message float64 10 | 11 | func NewModel() *Model { 12 | p := progress.New(progress.WithScaledGradient("#800080", "#ffc0cb")) 13 | return &Model{Model: p} 14 | } 15 | 16 | type Model struct { 17 | progress.Model 18 | } 19 | 20 | func (m *Model) Update(msg tea.Msg) tea.Cmd { 21 | model, cmd := m.Model.Update(msg) 22 | m.Model = model.(progress.Model) 23 | 24 | return cmd 25 | } 26 | 27 | func (m *Model) View() string { 28 | return m.Model.View() + terminal.SetProgress(int(m.Percent()*100)) 29 | } 30 | -------------------------------------------------------------------------------- /src/progress/reader.go: -------------------------------------------------------------------------------- 1 | package progress 2 | 3 | import ( 4 | "io" 5 | 6 | tea "github.com/charmbracelet/bubbletea" 7 | ) 8 | 9 | func NewReader(reader io.Reader, total int64, program *tea.Program) *Reader { 10 | return &Reader{ 11 | Reader: reader, 12 | program: program, 13 | total: total, 14 | } 15 | } 16 | 17 | type Reader struct { 18 | io.Reader 19 | 20 | program *tea.Program 21 | total int64 22 | current int64 23 | } 24 | 25 | func (r *Reader) Read(p []byte) (int, error) { 26 | n, err := r.Reader.Read(p) 27 | r.current += int64(n) 28 | percent := float64(r.current) / float64(r.total) 29 | 30 | if r.program != nil { 31 | r.program.Send(Message(percent)) 32 | } 33 | 34 | return n, err 35 | } 36 | -------------------------------------------------------------------------------- /src/prompt/rprompt.go: -------------------------------------------------------------------------------- 1 | package prompt 2 | 3 | import ( 4 | "strconv" 5 | 6 | "github.com/jandedobbeleer/oh-my-posh/src/cache" 7 | "github.com/jandedobbeleer/oh-my-posh/src/config" 8 | "github.com/jandedobbeleer/oh-my-posh/src/runtime" 9 | "github.com/jandedobbeleer/oh-my-posh/src/shell" 10 | ) 11 | 12 | const ( 13 | RPromptKey = "rprompt" 14 | RPromptLengthKey = "rprompt_length" 15 | ) 16 | 17 | func (e *Engine) RPrompt() string { 18 | var rprompt *config.Block 19 | 20 | for _, block := range e.Config.Blocks { 21 | if block.Type != config.RPrompt { 22 | continue 23 | } 24 | 25 | rprompt = block 26 | break 27 | } 28 | 29 | if rprompt == nil { 30 | return "" 31 | } 32 | 33 | text, length := e.writeBlockSegments(rprompt) 34 | 35 | // do not print anything when we don't have any text 36 | if length == 0 { 37 | return "" 38 | } 39 | 40 | e.rpromptLength = length 41 | 42 | if e.Env.Shell() == shell.ELVISH && e.Env.GOOS() != runtime.WINDOWS { 43 | // Workaround to align with a right-aligned block on non-Windows systems. 44 | text += " " 45 | } 46 | 47 | e.Env.Cache().Set(RPromptKey, text, cache.INFINITE) 48 | e.Env.Cache().Set(RPromptLengthKey, strconv.Itoa(e.rpromptLength), cache.INFINITE) 49 | 50 | return text 51 | } 52 | -------------------------------------------------------------------------------- /src/runtime/battery/battery_netbsd.go: -------------------------------------------------------------------------------- 1 | package battery 2 | 3 | import ( 4 | "errors" 5 | "strconv" 6 | "strings" 7 | 8 | "github.com/jandedobbeleer/oh-my-posh/src/runtime/cmd" 9 | ) 10 | 11 | func Get() (*Info, error) { 12 | output, err := cmd.Run("envstat", "-s", "acpibat0:charge", "-n") 13 | if err != nil { 14 | return nil, err 15 | } 16 | percentage, err := strconv.Atoi(strings.TrimSpace(output)) 17 | if err != nil { 18 | return nil, errors.New("unable to parse battery percentage") 19 | } 20 | return &Info{ 21 | Percentage: percentage, 22 | State: Unknown, 23 | }, nil 24 | } 25 | -------------------------------------------------------------------------------- /src/runtime/battery/battery_openandfreebsd.go: -------------------------------------------------------------------------------- 1 | //go:build openbsd || freebsd 2 | 3 | package battery 4 | 5 | import ( 6 | "errors" 7 | "strconv" 8 | "strings" 9 | 10 | "github.com/jandedobbeleer/oh-my-posh/src/runtime/cmd" 11 | ) 12 | 13 | // See https://man.openbsd.org/man8/apm.8 14 | func mapMostLogicalState(state string) State { 15 | switch state { 16 | case "3": 17 | return Charging 18 | case "0", "1": 19 | return Discharging 20 | case "2": 21 | return Empty 22 | default: 23 | return Unknown 24 | } 25 | } 26 | 27 | func parseBatteryOutput(apm_percentage string, apm_status string) (*Info, error) { 28 | percentage, err := strconv.Atoi(strings.TrimSpace(apm_percentage)) 29 | if err != nil { 30 | return nil, errors.New("unable to parse battery percentage") 31 | } 32 | 33 | if percentage == 100 { 34 | return &Info{ 35 | Percentage: percentage, 36 | State: Full, 37 | }, nil 38 | } 39 | 40 | return &Info{ 41 | Percentage: percentage, 42 | State: mapMostLogicalState(apm_status), 43 | }, nil 44 | } 45 | 46 | func Get() (*Info, error) { 47 | apm_percentage, err := cmd.Run("apm", "-l") 48 | if err != nil { 49 | return nil, err 50 | } 51 | 52 | apm_status, err := cmd.Run("apm", "-b") 53 | if err != nil { 54 | return nil, err 55 | } 56 | 57 | return parseBatteryOutput(apm_percentage, apm_status) 58 | 59 | } 60 | -------------------------------------------------------------------------------- /src/runtime/cmd/run.go: -------------------------------------------------------------------------------- 1 | package cmd 2 | 3 | import ( 4 | "bytes" 5 | "context" 6 | "os/exec" 7 | "strings" 8 | "time" 9 | ) 10 | 11 | // Run is used to correctly run a command with a timeout. 12 | func Run(command string, args ...string) (string, error) { 13 | // set a timeout of 4 seconds 14 | ctx, cancel := context.WithTimeout(context.Background(), time.Second*4) 15 | defer cancel() 16 | cmd := exec.CommandContext(ctx, command, args...) 17 | var out bytes.Buffer 18 | var err bytes.Buffer 19 | cmd.Stdout = &out 20 | cmd.Stderr = &err 21 | cmdErr := cmd.Run() 22 | if cmdErr != nil { 23 | output := err.String() 24 | return output, cmdErr 25 | } 26 | // some silly commands return 0 and the output is in stderr instead of stdout 27 | result := out.String() 28 | if len(result) == 0 { 29 | result = err.String() 30 | } 31 | output := strings.TrimSpace(result) 32 | return output, nil 33 | } 34 | -------------------------------------------------------------------------------- /src/runtime/http/connection.go: -------------------------------------------------------------------------------- 1 | package http 2 | 3 | import ( 4 | "net" 5 | "time" 6 | ) 7 | 8 | // if we can connect to ohmyposh within 200ms, we are connected 9 | // otherwise, let's consider being offline 10 | func IsConnected() bool { 11 | timeout := 200 * time.Millisecond 12 | _, err := net.DialTimeout("tcp", "ohmyposh.dev:80", timeout) 13 | return err == nil 14 | } 15 | -------------------------------------------------------------------------------- /src/runtime/http/http.go: -------------------------------------------------------------------------------- 1 | package http 2 | 3 | import ( 4 | "net" 5 | "net/http" 6 | "time" 7 | ) 8 | 9 | // Inspired by: https://www.thegreatcodeadventure.com/mocking-http-requests-in-golang/ 10 | 11 | type httpClient interface { 12 | Do(req *http.Request) (*http.Response, error) 13 | } 14 | 15 | var ( 16 | defaultTransport http.RoundTripper = &http.Transport{ 17 | Proxy: http.ProxyFromEnvironment, 18 | Dial: (&net.Dialer{ 19 | Timeout: 10 * time.Second, 20 | }).Dial, 21 | TLSHandshakeTimeout: 10 * time.Second, 22 | ResponseHeaderTimeout: 10 * time.Second, 23 | } 24 | 25 | HTTPClient httpClient = &http.Client{Transport: defaultTransport} 26 | ) 27 | -------------------------------------------------------------------------------- /src/runtime/http/request.go: -------------------------------------------------------------------------------- 1 | package http 2 | 3 | import ( 4 | "encoding/json" 5 | "io" 6 | "net/http" 7 | 8 | "github.com/jandedobbeleer/oh-my-posh/src/cache" 9 | "github.com/jandedobbeleer/oh-my-posh/src/log" 10 | ) 11 | 12 | type RequestModifier func(request *http.Request) 13 | 14 | type Request struct { 15 | Env Environment 16 | HTTPTimeout int 17 | } 18 | 19 | type Environment interface { 20 | HTTPRequest(url string, body io.Reader, timeout int, requestModifiers ...RequestModifier) ([]byte, error) 21 | Cache() cache.Cache 22 | } 23 | 24 | func Do[a any](r *Request, url string, body io.Reader, requestModifiers ...RequestModifier) (a, error) { 25 | var data a 26 | httpTimeout := r.HTTPTimeout // r.props.GetInt(properties.HTTPTimeout, properties.DefaultHTTPTimeout) 27 | 28 | responseBody, err := r.Env.HTTPRequest(url, body, httpTimeout, requestModifiers...) 29 | if err != nil { 30 | log.Error(err) 31 | return data, err 32 | } 33 | 34 | err = json.Unmarshal(responseBody, &data) 35 | if err != nil { 36 | log.Error(err) 37 | return data, err 38 | } 39 | 40 | return data, nil 41 | } 42 | -------------------------------------------------------------------------------- /src/runtime/path/home.go: -------------------------------------------------------------------------------- 1 | package path 2 | 3 | import ( 4 | "os" 5 | 6 | "github.com/jandedobbeleer/oh-my-posh/src/log" 7 | ) 8 | 9 | func Home() string { 10 | home := os.Getenv("HOME") 11 | defer func() { 12 | log.Debug(home) 13 | }() 14 | 15 | if len(home) > 0 { 16 | return home 17 | } 18 | 19 | // fallback to older implemenations on Windows 20 | home = os.Getenv("HOMEDRIVE") + os.Getenv("HOMEPATH") 21 | 22 | if len(home) == 0 { 23 | home = os.Getenv("USERPROFILE") 24 | } 25 | 26 | return home 27 | } 28 | -------------------------------------------------------------------------------- /src/runtime/path/separator.go: -------------------------------------------------------------------------------- 1 | package path 2 | 3 | import ( 4 | "runtime" 5 | "time" 6 | 7 | "github.com/jandedobbeleer/oh-my-posh/src/log" 8 | ) 9 | 10 | const ( 11 | windows = "windows" 12 | ) 13 | 14 | func Separator() string { 15 | defer log.Trace(time.Now()) 16 | 17 | if runtime.GOOS == windows { 18 | return `\` 19 | } 20 | 21 | return "/" 22 | } 23 | 24 | func IsSeparator(c uint8) bool { 25 | if c == '/' { 26 | return true 27 | } 28 | 29 | if runtime.GOOS == windows && c == '\\' { 30 | return true 31 | } 32 | 33 | return false 34 | } 35 | -------------------------------------------------------------------------------- /src/runtime/terminal_windows_nix.go: -------------------------------------------------------------------------------- 1 | //go:build !darwin 2 | 3 | package runtime 4 | 5 | import ( 6 | "time" 7 | 8 | "github.com/jandedobbeleer/oh-my-posh/src/log" 9 | "github.com/jandedobbeleer/oh-my-posh/src/runtime/battery" 10 | ) 11 | 12 | func (term *Terminal) BatteryState() (*battery.Info, error) { 13 | defer log.Trace(time.Now()) 14 | info, err := battery.Get() 15 | if err != nil { 16 | log.Error(err) 17 | return nil, err 18 | } 19 | return info, nil 20 | } 21 | -------------------------------------------------------------------------------- /src/segments/angular.go: -------------------------------------------------------------------------------- 1 | package segments 2 | 3 | import ( 4 | "path/filepath" 5 | ) 6 | 7 | type Angular struct { 8 | language 9 | } 10 | 11 | func (a *Angular) Template() string { 12 | return languageTemplate 13 | } 14 | 15 | func (a *Angular) Enabled() bool { 16 | a.extensions = []string{"angular.json"} 17 | a.commands = []*cmd{ 18 | { 19 | regex: `(?:(?P((?P[0-9]+).(?P[0-9]+).(?P[0-9]+))))`, 20 | getVersion: a.getVersion, 21 | }, 22 | } 23 | a.versionURLTemplate = "https://github.com/angular/angular/releases/tag/{{.Full}}" 24 | 25 | return a.language.Enabled() 26 | } 27 | 28 | func (a *Angular) getVersion() (string, error) { 29 | return a.nodePackageVersion(filepath.Join("@angular", "core")) 30 | } 31 | -------------------------------------------------------------------------------- /src/segments/aurelia.go: -------------------------------------------------------------------------------- 1 | package segments 2 | 3 | type Aurelia struct { 4 | language 5 | } 6 | 7 | func (a *Aurelia) Template() string { 8 | return languageTemplate 9 | } 10 | 11 | func (a *Aurelia) Enabled() bool { 12 | a.extensions = []string{"package.json"} 13 | a.commands = []*cmd{ 14 | { 15 | regex: `(?:(?P((?P[0-9]+).(?P[0-9]+).(?P[0-9]+)(-(?P[a-z]+).(?P[0-9]+))?)))`, 16 | getVersion: a.getVersion, 17 | }, 18 | } 19 | a.versionURLTemplate = "https://github.com/aurelia/aurelia/releases/tag/v{{ .Full }}" 20 | 21 | if !a.hasNodePackage("aurelia") { 22 | return false 23 | } 24 | 25 | return a.language.Enabled() 26 | } 27 | 28 | func (a *Aurelia) getVersion() (string, error) { 29 | return a.nodePackageVersion("aurelia") 30 | } 31 | -------------------------------------------------------------------------------- /src/segments/az_functions.go: -------------------------------------------------------------------------------- 1 | package segments 2 | 3 | type AzFunc struct { 4 | language 5 | } 6 | 7 | func (az *AzFunc) Template() string { 8 | return languageTemplate 9 | } 10 | 11 | func (az *AzFunc) Enabled() bool { 12 | az.extensions = []string{"host.json", "local.settings.json", "function.json"} 13 | az.commands = []*cmd{ 14 | { 15 | 16 | executable: "func", 17 | args: []string{"--version"}, 18 | regex: `(?P[0-9.]+)`, 19 | }, 20 | } 21 | 22 | return az.language.Enabled() 23 | } 24 | -------------------------------------------------------------------------------- /src/segments/base.go: -------------------------------------------------------------------------------- 1 | package segments 2 | 3 | import ( 4 | "github.com/jandedobbeleer/oh-my-posh/src/properties" 5 | "github.com/jandedobbeleer/oh-my-posh/src/runtime" 6 | ) 7 | 8 | type base struct { 9 | props properties.Properties 10 | env runtime.Environment 11 | 12 | Segment *Segment `json:"Segment"` 13 | } 14 | 15 | type Segment struct { 16 | Text string `json:"Text"` 17 | Index int `json:"Index"` 18 | } 19 | 20 | func (s *base) Text() string { 21 | return s.Segment.Text 22 | } 23 | 24 | func (s *base) SetText(text string) { 25 | s.Segment.Text = text 26 | } 27 | 28 | func (s *base) SetIndex(index int) { 29 | s.Segment.Index = index 30 | } 31 | 32 | func (s *base) Init(props properties.Properties, env runtime.Environment) { 33 | s.Segment = &Segment{} 34 | s.props = props 35 | s.env = env 36 | } 37 | -------------------------------------------------------------------------------- /src/segments/bazel.go: -------------------------------------------------------------------------------- 1 | package segments 2 | 3 | import ( 4 | "github.com/jandedobbeleer/oh-my-posh/src/properties" 5 | ) 6 | 7 | type Bazel struct { 8 | Icon string 9 | language 10 | } 11 | 12 | const ( 13 | // Bazel's icon 14 | Icon properties.Property = "icon" 15 | ) 16 | 17 | func (b *Bazel) Template() string { 18 | return " {{ if .Error }}{{ .Icon }} {{ .Error }}{{ else }}{{ url .Icon .URL }} {{ .Full }}{{ end }} " 19 | } 20 | 21 | func (b *Bazel) Enabled() bool { 22 | b.extensions = []string{"*.bazel", "*.bzl", "BUILD", "WORKSPACE", ".bazelrc", ".bazelversion"} 23 | b.folders = []string{"bazel-bin", "bazel-out", "bazel-testlogs"} 24 | b.commands = []*cmd{ 25 | { 26 | executable: "bazel", 27 | args: []string{"--version"}, 28 | regex: `bazel (?P((?P[0-9]+).(?P[0-9]+).(?P[0-9]+)))`, 29 | }, 30 | } 31 | // Use the correct URL for Bazel >5.4.1, since they do not have the docs subdomain. 32 | b.versionURLTemplate = "https://{{ if lt .Major 6 }}docs.{{ end }}bazel.build/versions/{{ .Major }}.{{ .Minor }}.{{ .Patch }}" 33 | 34 | b.Icon = b.props.GetString(Icon, "\ue63a") 35 | 36 | return b.language.Enabled() 37 | } 38 | -------------------------------------------------------------------------------- /src/segments/buf.go: -------------------------------------------------------------------------------- 1 | package segments 2 | 3 | type Buf struct { 4 | language 5 | } 6 | 7 | func (b *Buf) Template() string { 8 | return languageTemplate 9 | } 10 | 11 | func (b *Buf) Enabled() bool { 12 | b.extensions = []string{"buf.yaml", "buf.gen.yaml", "buf.work.yaml"} 13 | b.commands = []*cmd{ 14 | { 15 | executable: "buf", 16 | args: []string{"--version"}, 17 | regex: `(?:(?P((?P[0-9]+).(?P[0-9]+).(?P[0-9]+))))`, 18 | }, 19 | } 20 | b.versionURLTemplate = "https://github.com/bufbuild/buf/releases/tag/v{{.Full}}" 21 | 22 | return b.language.Enabled() 23 | } 24 | -------------------------------------------------------------------------------- /src/segments/buf_test.go: -------------------------------------------------------------------------------- 1 | package segments 2 | 3 | import ( 4 | "fmt" 5 | "testing" 6 | 7 | "github.com/stretchr/testify/assert" 8 | ) 9 | 10 | func TestBuf(t *testing.T) { 11 | cases := []struct { 12 | Case string 13 | ExpectedString string 14 | Version string 15 | }{ 16 | {Case: "Buf 1.12.0", ExpectedString: "1.12.0", Version: "1.12.0"}, 17 | } 18 | for _, tc := range cases { 19 | params := &mockedLanguageParams{ 20 | cmd: "buf", 21 | versionParam: "--version", 22 | versionOutput: tc.Version, 23 | extension: "buf.yaml", 24 | } 25 | env, props := getMockedLanguageEnv(params) 26 | b := &Buf{} 27 | b.Init(props, env) 28 | assert.True(t, b.Enabled(), fmt.Sprintf("Failed in case: %s", tc.Case)) 29 | assert.Equal(t, tc.ExpectedString, renderTemplate(env, b.Template(), b), fmt.Sprintf("Failed in case: %s", tc.Case)) 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /src/segments/bun.go: -------------------------------------------------------------------------------- 1 | package segments 2 | 3 | type Bun struct { 4 | language 5 | } 6 | 7 | func (b *Bun) Template() string { 8 | return languageTemplate 9 | } 10 | 11 | func (b *Bun) Enabled() bool { 12 | b.extensions = []string{"bun.lockb", "bun.lock"} 13 | b.commands = []*cmd{ 14 | { 15 | executable: "bun", 16 | args: []string{"--version"}, 17 | regex: `(?:(?P((?P[0-9]+).(?P[0-9]+).(?P[0-9]+))))`, 18 | }, 19 | } 20 | b.versionURLTemplate = "https://github.com/oven-sh/bun/releases/tag/bun-v{{.Full}}" 21 | 22 | return b.language.Enabled() 23 | } 24 | -------------------------------------------------------------------------------- /src/segments/bun_test.go: -------------------------------------------------------------------------------- 1 | package segments 2 | 3 | import ( 4 | "fmt" 5 | "testing" 6 | 7 | "github.com/stretchr/testify/assert" 8 | ) 9 | 10 | func TestBun(t *testing.T) { 11 | cases := []struct { 12 | Case string 13 | ExpectedString string 14 | Version string 15 | }{ 16 | {Case: "Bun 1.1.8", ExpectedString: "1.1.8", Version: "1.1.8"}, 17 | } 18 | for _, tc := range cases { 19 | params := &mockedLanguageParams{ 20 | cmd: "bun", 21 | versionParam: "--version", 22 | versionOutput: tc.Version, 23 | extension: "bun.lockb", 24 | } 25 | env, props := getMockedLanguageEnv(params) 26 | b := &Bun{} 27 | b.Init(props, env) 28 | assert.True(t, b.Enabled(), fmt.Sprintf("Failed in case: %s", tc.Case)) 29 | assert.Equal(t, tc.ExpectedString, renderTemplate(env, b.Template(), b), fmt.Sprintf("Failed in case: %s", tc.Case)) 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /src/segments/cds.go: -------------------------------------------------------------------------------- 1 | package segments 2 | 3 | type Cds struct { 4 | language 5 | HasDependency bool 6 | } 7 | 8 | func (c *Cds) Template() string { 9 | return languageTemplate 10 | } 11 | 12 | func (c *Cds) Enabled() bool { 13 | c.extensions = []string{".cdsrc.json", ".cdsrc-private.json", "*.cds"} 14 | c.commands = []*cmd{ 15 | { 16 | executable: "cds", 17 | args: []string{"--version"}, 18 | regex: `@sap/cds: (?:(?P((?P[0-9]+).(?P[0-9]+).(?P[0-9]+))))`, 19 | }, 20 | } 21 | c.language.loadContext = c.loadContext 22 | c.language.inContext = c.inContext 23 | c.displayMode = c.props.GetString(DisplayMode, DisplayModeContext) 24 | 25 | return c.language.Enabled() 26 | } 27 | 28 | func (c *Cds) loadContext() { 29 | if !c.hasNodePackage("@sap/cds") { 30 | return 31 | } 32 | 33 | c.HasDependency = true 34 | } 35 | 36 | func (c *Cds) inContext() bool { 37 | return c.HasDependency 38 | } 39 | -------------------------------------------------------------------------------- /src/segments/cf.go: -------------------------------------------------------------------------------- 1 | package segments 2 | 3 | type Cf struct { 4 | language 5 | } 6 | 7 | func (c *Cf) Template() string { 8 | return languageTemplate 9 | } 10 | 11 | func (c *Cf) Enabled() bool { 12 | c.extensions = []string{"manifest.yml", "mta.yaml"} 13 | c.commands = []*cmd{ 14 | { 15 | executable: "cf", 16 | args: []string{"version"}, 17 | regex: `(?:(?P((?P[0-9]+).(?P[0-9]+).(?P[0-9]+))))`, 18 | }, 19 | } 20 | c.displayMode = c.props.GetString(DisplayMode, DisplayModeFiles) 21 | c.versionURLTemplate = "https://github.com/cloudfoundry/cli/releases/tag/v{{ .Full }}" 22 | 23 | return c.language.Enabled() 24 | } 25 | -------------------------------------------------------------------------------- /src/segments/cmake.go: -------------------------------------------------------------------------------- 1 | package segments 2 | 3 | type Cmake struct { 4 | language 5 | } 6 | 7 | func (c *Cmake) Template() string { 8 | return languageTemplate 9 | } 10 | 11 | func (c *Cmake) Enabled() bool { 12 | c.extensions = []string{"*.cmake", "CMakeLists.txt"} 13 | c.commands = []*cmd{ 14 | { 15 | executable: "cmake", 16 | args: []string{"--version"}, 17 | regex: `cmake version (?P((?P[0-9]+).(?P[0-9]+).(?P[0-9]+)))`, 18 | }, 19 | } 20 | c.versionURLTemplate = "https://cmake.org/cmake/help/v{{ .Major }}.{{ .Minor }}" 21 | 22 | return c.language.Enabled() 23 | } 24 | -------------------------------------------------------------------------------- /src/segments/cmake_test.go: -------------------------------------------------------------------------------- 1 | package segments 2 | 3 | import ( 4 | "fmt" 5 | "testing" 6 | 7 | "github.com/stretchr/testify/assert" 8 | ) 9 | 10 | func TestCmake(t *testing.T) { 11 | cases := []struct { 12 | Case string 13 | ExpectedString string 14 | Version string 15 | }{ 16 | {Case: "Cmake 3.23.2", ExpectedString: "3.23.2", Version: "cmake version 3.23.2"}, 17 | {Case: "Cmake 2.3.13", ExpectedString: "2.3.12", Version: "cmake version 2.3.12"}, 18 | {Case: "", ExpectedString: "err parsing info from cmake with", Version: ""}, 19 | } 20 | for _, tc := range cases { 21 | params := &mockedLanguageParams{ 22 | cmd: "cmake", 23 | versionParam: "--version", 24 | versionOutput: tc.Version, 25 | extension: "*.cmake", 26 | } 27 | env, props := getMockedLanguageEnv(params) 28 | c := &Cmake{} 29 | c.Init(props, env) 30 | failMsg := fmt.Sprintf("Failed in case: %s", tc.Case) 31 | assert.True(t, c.Enabled(), failMsg) 32 | assert.Equal(t, tc.ExpectedString, renderTemplate(env, c.Template(), c), failMsg) 33 | } 34 | } 35 | -------------------------------------------------------------------------------- /src/segments/connection.go: -------------------------------------------------------------------------------- 1 | package segments 2 | 3 | import ( 4 | "strings" 5 | 6 | "github.com/jandedobbeleer/oh-my-posh/src/properties" 7 | "github.com/jandedobbeleer/oh-my-posh/src/runtime" 8 | ) 9 | 10 | type Connection struct { 11 | base 12 | 13 | runtime.Connection 14 | } 15 | 16 | const ( 17 | Type properties.Property = "type" 18 | ) 19 | 20 | func (c *Connection) Template() string { 21 | return " {{ if eq .Type \"wifi\"}}\uf1eb{{ else if eq .Type \"ethernet\"}}\ueba9{{ end }} " 22 | } 23 | 24 | func (c *Connection) Enabled() bool { 25 | types := c.props.GetString(Type, "wifi|ethernet") 26 | connectionTypes := strings.SplitSeq(types, "|") 27 | for connectionType := range connectionTypes { 28 | network, err := c.env.Connection(runtime.ConnectionType(connectionType)) 29 | if err != nil { 30 | continue 31 | } 32 | c.Connection = *network 33 | return true 34 | } 35 | return false 36 | } 37 | -------------------------------------------------------------------------------- /src/segments/crystal.go: -------------------------------------------------------------------------------- 1 | package segments 2 | 3 | type Crystal struct { 4 | language 5 | } 6 | 7 | func (c *Crystal) Template() string { 8 | return languageTemplate 9 | } 10 | 11 | func (c *Crystal) Enabled() bool { 12 | c.extensions = []string{"*.cr", "shard.yml"} 13 | c.commands = []*cmd{ 14 | { 15 | executable: "crystal", 16 | args: []string{"--version"}, 17 | regex: `Crystal (?P((?P[0-9]+).(?P[0-9]+).(?P[0-9]+)))`, 18 | }, 19 | } 20 | c.versionURLTemplate = "https://github.com/crystal-lang/crystal/releases/tag/{{ .Full }}" 21 | 22 | return c.language.Enabled() 23 | } 24 | -------------------------------------------------------------------------------- /src/segments/crystal_test.go: -------------------------------------------------------------------------------- 1 | package segments 2 | 3 | import ( 4 | "fmt" 5 | "testing" 6 | 7 | "github.com/stretchr/testify/assert" 8 | ) 9 | 10 | func TestCrystal(t *testing.T) { 11 | cases := []struct { 12 | Case string 13 | ExpectedString string 14 | Version string 15 | }{ 16 | {Case: "Crystal 1.0.0", ExpectedString: "1.0.0", Version: "Crystal 1.0.0 (2021-03-22)"}, 17 | } 18 | for _, tc := range cases { 19 | params := &mockedLanguageParams{ 20 | cmd: "crystal", 21 | versionParam: "--version", 22 | versionOutput: tc.Version, 23 | extension: "*.cr", 24 | } 25 | env, props := getMockedLanguageEnv(params) 26 | c := &Crystal{} 27 | c.Init(props, env) 28 | assert.True(t, c.Enabled(), fmt.Sprintf("Failed in case: %s", tc.Case)) 29 | assert.Equal(t, tc.ExpectedString, renderTemplate(env, c.Template(), c), fmt.Sprintf("Failed in case: %s", tc.Case)) 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /src/segments/dart.go: -------------------------------------------------------------------------------- 1 | package segments 2 | 3 | var ( 4 | dartExtensions = []string{"*.dart", "pubspec.yaml", "pubspec.yml", "pubspec.lock"} 5 | dartFolders = []string{".dart_tool"} 6 | ) 7 | 8 | type Dart struct { 9 | language 10 | } 11 | 12 | func (d *Dart) Template() string { 13 | return languageTemplate 14 | } 15 | 16 | func (d *Dart) Enabled() bool { 17 | d.extensions = dartExtensions 18 | d.folders = dartFolders 19 | d.commands = []*cmd{ 20 | { 21 | executable: "fvm", 22 | args: []string{"dart", "--version"}, 23 | regex: `Dart SDK version: (?P((?P[0-9]+).(?P[0-9]+).(?P[0-9]+)))`, 24 | }, 25 | { 26 | executable: "dart", 27 | args: []string{"--version"}, 28 | regex: `Dart SDK version: (?P((?P[0-9]+).(?P[0-9]+).(?P[0-9]+)))`, 29 | }, 30 | } 31 | d.versionURLTemplate = "https://dart.dev/guides/language/evolution#dart-{{ .Major }}{{ .Minor }}" 32 | 33 | return d.language.Enabled() 34 | } 35 | -------------------------------------------------------------------------------- /src/segments/dart_test.go: -------------------------------------------------------------------------------- 1 | package segments 2 | 3 | import ( 4 | "fmt" 5 | "testing" 6 | 7 | "github.com/stretchr/testify/assert" 8 | ) 9 | 10 | func TestDart(t *testing.T) { 11 | cases := []struct { 12 | Case string 13 | ExpectedString string 14 | Version string 15 | }{ 16 | {Case: "Dart 2.12.4", ExpectedString: "2.12.4", Version: "Dart SDK version: 2.12.4 (stable) (Thu Apr 15 12:26:53 2021 +0200) on \"macos_x64\""}, 17 | } 18 | for _, tc := range cases { 19 | params := &mockedLanguageParams{ 20 | cmd: "dart", 21 | versionParam: "--version", 22 | versionOutput: tc.Version, 23 | extension: "*.dart", 24 | } 25 | 26 | env, props := getMockedLanguageEnv(params) 27 | env.On("HasCommand", "fvm").Return(false) 28 | 29 | d := &Dart{} 30 | d.Init(props, env) 31 | 32 | assert.True(t, d.Enabled(), fmt.Sprintf("Failed in case: %s", tc.Case)) 33 | assert.Equal(t, tc.ExpectedString, renderTemplate(env, d.Template(), d), fmt.Sprintf("Failed in case: %s", tc.Case)) 34 | } 35 | } 36 | -------------------------------------------------------------------------------- /src/segments/deno.go: -------------------------------------------------------------------------------- 1 | package segments 2 | 3 | type Deno struct { 4 | language 5 | } 6 | 7 | func (d *Deno) Template() string { 8 | return languageTemplate 9 | } 10 | 11 | func (d *Deno) Enabled() bool { 12 | d.extensions = []string{"*.js", "*.ts", "deno.json"} 13 | d.commands = []*cmd{ 14 | { 15 | executable: "deno", 16 | args: []string{"--version"}, 17 | regex: `(?:(?P((?P[0-9]+).(?P[0-9]+).(?P[0-9]+))))`, 18 | }, 19 | } 20 | d.versionURLTemplate = "https://github.com/denoland/deno/releases/tag/v{{.Full}}" 21 | 22 | return d.language.Enabled() 23 | } 24 | -------------------------------------------------------------------------------- /src/segments/deno_test.go: -------------------------------------------------------------------------------- 1 | package segments 2 | 3 | import ( 4 | "fmt" 5 | "testing" 6 | 7 | "github.com/stretchr/testify/assert" 8 | ) 9 | 10 | func TestDeno(t *testing.T) { 11 | cases := []struct { 12 | Case string 13 | ExpectedString string 14 | Version string 15 | }{ 16 | {Case: "Deno 1.25.2", ExpectedString: "1.25.2", Version: "deno 1.25.2 (release, aarch64-apple-darwin)\nv8 10.6.194.5\ntypescript 4.7.4"}, 17 | } 18 | for _, tc := range cases { 19 | params := &mockedLanguageParams{ 20 | cmd: "deno", 21 | versionParam: "--version", 22 | versionOutput: tc.Version, 23 | extension: "*.js", 24 | } 25 | env, props := getMockedLanguageEnv(params) 26 | d := &Deno{} 27 | d.Init(props, env) 28 | assert.True(t, d.Enabled(), fmt.Sprintf("Failed in case: %s", tc.Case)) 29 | assert.Equal(t, tc.ExpectedString, renderTemplate(env, d.Template(), d), fmt.Sprintf("Failed in case: %s", tc.Case)) 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /src/segments/elixir.go: -------------------------------------------------------------------------------- 1 | package segments 2 | 3 | type Elixir struct { 4 | language 5 | } 6 | 7 | func (e *Elixir) Template() string { 8 | return languageTemplate 9 | } 10 | 11 | func (e *Elixir) Enabled() bool { 12 | e.extensions = []string{"*.ex", "*.exs"} 13 | e.commands = []*cmd{ 14 | { 15 | executable: "asdf", 16 | args: []string{"current", "elixir"}, 17 | regex: `elixir\s+(?P((?P[0-9]+).(?P[0-9]+).(?P[0-9]+)))[^\s]*\s+`, 18 | }, 19 | { 20 | executable: "elixir", 21 | args: []string{"--version"}, 22 | regex: `Elixir (?P((?P[0-9]+).(?P[0-9]+).(?P[0-9]+)))`, 23 | }, 24 | } 25 | e.versionURLTemplate = "https://github.com/elixir-lang/elixir/releases/tag/v{{ .Full }}" 26 | 27 | return e.language.Enabled() 28 | } 29 | -------------------------------------------------------------------------------- /src/segments/flutter.go: -------------------------------------------------------------------------------- 1 | package segments 2 | 3 | type Flutter struct { 4 | language 5 | } 6 | 7 | func (f *Flutter) Template() string { 8 | return languageTemplate 9 | } 10 | 11 | func (f *Flutter) Enabled() bool { 12 | f.extensions = dartExtensions 13 | f.folders = dartFolders 14 | f.commands = []*cmd{ 15 | { 16 | executable: "fvm", 17 | args: []string{"flutter", "--version"}, 18 | regex: `Flutter (?P((?P[0-9]+).(?P[0-9]+).(?P[0-9]+)))`, 19 | }, 20 | { 21 | executable: "flutter", 22 | args: []string{"--version"}, 23 | regex: `Flutter (?P((?P[0-9]+).(?P[0-9]+).(?P[0-9]+)))`, 24 | }, 25 | } 26 | f.versionURLTemplate = "https://github.com/flutter/flutter/releases/tag/{{ .Major }}.{{ .Minor }}.{{ .Patch }}" 27 | 28 | return f.language.Enabled() 29 | } 30 | -------------------------------------------------------------------------------- /src/segments/flutter_test.go: -------------------------------------------------------------------------------- 1 | package segments 2 | 3 | import ( 4 | "fmt" 5 | "testing" 6 | 7 | "github.com/stretchr/testify/assert" 8 | ) 9 | 10 | func TestFlutter(t *testing.T) { 11 | cases := []struct { 12 | Case string 13 | ExpectedString string 14 | Version string 15 | }{ 16 | {Case: "Flutter 2.10.4", ExpectedString: "2.10.4", Version: "Flutter 2.10.4 • channel stable • https://github.com/flutter/flutter.git"}, 17 | } 18 | for _, tc := range cases { 19 | params := &mockedLanguageParams{ 20 | cmd: "flutter", 21 | versionParam: "--version", 22 | versionOutput: tc.Version, 23 | extension: "*.dart", 24 | } 25 | 26 | env, props := getMockedLanguageEnv(params) 27 | env.On("HasCommand", "fvm").Return(false) 28 | 29 | d := &Flutter{} 30 | d.Init(props, env) 31 | 32 | assert.True(t, d.Enabled(), fmt.Sprintf("Failed in case: %s", tc.Case)) 33 | assert.Equal(t, tc.ExpectedString, renderTemplate(env, d.Template(), d), fmt.Sprintf("Failed in case: %s", tc.Case)) 34 | } 35 | } 36 | -------------------------------------------------------------------------------- /src/segments/fortran.go: -------------------------------------------------------------------------------- 1 | package segments 2 | 3 | type Fortran struct { 4 | language 5 | } 6 | 7 | func (f *Fortran) Template() string { 8 | return languageTemplate 9 | } 10 | 11 | func (f *Fortran) Enabled() bool { 12 | f.extensions = []string{ 13 | "*.f", "*.for", "*.fpp", 14 | "*.f77", "*.f90", "*.f95", 15 | "*.f03", "*.f08", 16 | "*.F", "*.FOR", "*.FPP", 17 | "*.F77", "*.F90", "*.F95", 18 | "*.F03", "*.F08", 19 | "fpm.toml", 20 | } 21 | f.commands = []*cmd{ 22 | { 23 | executable: "gfortran", 24 | args: []string{"--version"}, 25 | regex: `GNU Fortran \(.*\) (?P((?P[0-9]+).(?P[0-9]+).(?P[0-9]+)))`, 26 | }, 27 | } 28 | 29 | return f.language.Enabled() 30 | } 31 | -------------------------------------------------------------------------------- /src/segments/git_unix.go: -------------------------------------------------------------------------------- 1 | //go:build !windows 2 | 3 | package segments 4 | 5 | import "path/filepath" 6 | 7 | // resolveGitPath resolves path relative to base. 8 | func resolveGitPath(base, path string) string { 9 | if filepath.IsAbs(path) { 10 | return path 11 | } 12 | 13 | return filepath.Join(base, path) 14 | } 15 | -------------------------------------------------------------------------------- /src/segments/git_unix_test.go: -------------------------------------------------------------------------------- 1 | //go:build !windows 2 | 3 | package segments 4 | 5 | import ( 6 | "testing" 7 | 8 | "github.com/stretchr/testify/assert" 9 | ) 10 | 11 | const TestRootPath = "/" 12 | 13 | func TestResolveGitPath(t *testing.T) { 14 | cases := []struct { 15 | Case string 16 | Base string 17 | Path string 18 | Expected string 19 | }{ 20 | { 21 | Case: "relative path", 22 | Base: "dir/", 23 | Path: "sub", 24 | Expected: "dir/sub", 25 | }, 26 | { 27 | Case: "absolute path", 28 | Base: "/base", 29 | Path: "/absolute/path", 30 | Expected: "/absolute/path", 31 | }, 32 | } 33 | for _, tc := range cases { 34 | assert.Equal(t, tc.Expected, resolveGitPath(tc.Base, tc.Path), tc.Case) 35 | } 36 | } 37 | -------------------------------------------------------------------------------- /src/segments/git_windows.go: -------------------------------------------------------------------------------- 1 | package segments 2 | 3 | import "path/filepath" 4 | 5 | // resolveGitPath resolves path relative to base. 6 | func resolveGitPath(base, path string) string { 7 | if len(path) == 0 { 8 | return base 9 | } 10 | 11 | if filepath.IsAbs(path) { 12 | return path 13 | } 14 | 15 | // Note that git on Windows uses slashes exclusively. And it's okay 16 | // because Windows actually accepts both directory separators. More 17 | // importantly, however, parts of the git segment depend on those 18 | // slashes. 19 | if path[0] == '/' { 20 | // path is a disk-relative path. 21 | return filepath.VolumeName(base) + path 22 | } 23 | 24 | return filepath.ToSlash(filepath.Join(base, path)) 25 | } 26 | -------------------------------------------------------------------------------- /src/segments/git_windows_test.go: -------------------------------------------------------------------------------- 1 | //go:build windows 2 | 3 | package segments 4 | 5 | import ( 6 | "testing" 7 | 8 | "github.com/stretchr/testify/assert" 9 | ) 10 | 11 | const TestRootPath = "C:/" 12 | 13 | func TestResolveGitPath(t *testing.T) { 14 | cases := []struct { 15 | Case string 16 | Base string 17 | Path string 18 | Expected string 19 | }{ 20 | { 21 | Case: "relative path", 22 | Base: "dir\\", 23 | Path: "sub", 24 | Expected: "dir/sub", 25 | }, 26 | { 27 | Case: "absolute path", 28 | Base: "C:\\base", 29 | Path: "C:/absolute/path", 30 | Expected: "C:/absolute/path", 31 | }, 32 | { 33 | Case: "disk-relative path", 34 | Base: "C:\\base", 35 | Path: "/absolute/path", 36 | Expected: "C:/absolute/path", 37 | }, 38 | } 39 | for _, tc := range cases { 40 | assert.Equal(t, tc.Expected, resolveGitPath(tc.Base, tc.Path), tc.Case) 41 | } 42 | } 43 | -------------------------------------------------------------------------------- /src/segments/helm.go: -------------------------------------------------------------------------------- 1 | package segments 2 | 3 | type Helm struct { 4 | base 5 | 6 | Version string 7 | } 8 | 9 | func (h *Helm) Enabled() bool { 10 | displayMode := h.props.GetString(DisplayMode, DisplayModeAlways) 11 | if displayMode != DisplayModeFiles { 12 | return h.getVersion() 13 | } 14 | 15 | inChart := false 16 | files := []string{"Chart.yml", "Chart.yaml", "helmfile.yaml", "helmfile.yml"} 17 | for _, file := range files { 18 | if _, err := h.env.HasParentFilePath(file, false); err == nil { 19 | inChart = true 20 | break 21 | } 22 | } 23 | 24 | return inChart && h.getVersion() 25 | } 26 | 27 | func (h *Helm) Template() string { 28 | return " Helm {{.Version}}" 29 | } 30 | 31 | func (h *Helm) getVersion() bool { 32 | cmd := "helm" 33 | if !h.env.HasCommand(cmd) { 34 | return false 35 | } 36 | 37 | result, err := h.env.RunCommand(cmd, "version", "--short", "--template={{.Version}}") 38 | if err != nil { 39 | return false 40 | } 41 | 42 | h.Version = result[1:] 43 | return true 44 | } 45 | -------------------------------------------------------------------------------- /src/segments/java.go: -------------------------------------------------------------------------------- 1 | package segments 2 | 3 | import ( 4 | "fmt" 5 | ) 6 | 7 | type Java struct { 8 | language 9 | } 10 | 11 | func (j *Java) Template() string { 12 | return languageTemplate 13 | } 14 | 15 | func (j *Java) Enabled() bool { 16 | j.init() 17 | 18 | return j.language.Enabled() 19 | } 20 | 21 | func (j *Java) init() { 22 | javaRegex := `(?: JRE)(?: \(.*\))? \((?P(?P[0-9]+)(?:\.(?P[0-9]+))?(?:\.(?P[0-9]+))?).*\),` 23 | javaCmd := &cmd{ 24 | executable: "java", 25 | args: []string{"-Xinternalversion"}, 26 | regex: javaRegex, 27 | } 28 | 29 | j.extensions = []string{ 30 | "pom.xml", 31 | "build.gradle.kts", 32 | "build.sbt", 33 | ".java-version", 34 | ".deps.edn", 35 | "project.clj", 36 | "build.boot", 37 | "*.java", 38 | "*.class", 39 | "*.gradle", 40 | "*.jar", 41 | "*.clj", 42 | "*.cljc", 43 | } 44 | 45 | javaHome := j.env.Getenv("JAVA_HOME") 46 | if len(javaHome) > 0 { 47 | java := fmt.Sprintf("%s/bin/java", javaHome) 48 | j.commands = []*cmd{ 49 | { 50 | executable: java, 51 | args: []string{"-Xinternalversion"}, 52 | regex: javaRegex, 53 | }, 54 | javaCmd, 55 | } 56 | return 57 | } 58 | 59 | j.commands = []*cmd{javaCmd} 60 | } 61 | -------------------------------------------------------------------------------- /src/segments/julia.go: -------------------------------------------------------------------------------- 1 | package segments 2 | 3 | type Julia struct { 4 | language 5 | } 6 | 7 | func (j *Julia) Template() string { 8 | return languageTemplate 9 | } 10 | 11 | func (j *Julia) Enabled() bool { 12 | j.extensions = []string{"*.jl"} 13 | j.commands = []*cmd{ 14 | { 15 | executable: "julia", 16 | args: []string{"--version"}, 17 | regex: `julia version (?P((?P[0-9]+).(?P[0-9]+).(?P[0-9]+)))`, 18 | }, 19 | } 20 | j.versionURLTemplate = "https://github.com/JuliaLang/julia/releases/tag/v{{ .Full }}" 21 | 22 | return j.language.Enabled() 23 | } 24 | -------------------------------------------------------------------------------- /src/segments/julia_test.go: -------------------------------------------------------------------------------- 1 | package segments 2 | 3 | import ( 4 | "fmt" 5 | "testing" 6 | 7 | "github.com/stretchr/testify/assert" 8 | ) 9 | 10 | func TestJulia(t *testing.T) { 11 | cases := []struct { 12 | Case string 13 | ExpectedString string 14 | Version string 15 | }{ 16 | {Case: "Julia 1.6.0", ExpectedString: "1.6.0", Version: "julia version 1.6.0"}, 17 | {Case: "Julia 1.6.1", ExpectedString: "1.6.1", Version: "julia version 1.6.1"}, 18 | } 19 | for _, tc := range cases { 20 | params := &mockedLanguageParams{ 21 | cmd: "julia", 22 | versionParam: "--version", 23 | versionOutput: tc.Version, 24 | extension: "*.jl", 25 | } 26 | env, props := getMockedLanguageEnv(params) 27 | j := &Julia{} 28 | j.Init(props, env) 29 | assert.True(t, j.Enabled(), fmt.Sprintf("Failed in case: %s", tc.Case)) 30 | assert.Equal(t, tc.ExpectedString, renderTemplate(env, j.Template(), j), fmt.Sprintf("Failed in case: %s", tc.Case)) 31 | } 32 | } 33 | -------------------------------------------------------------------------------- /src/segments/kotlin.go: -------------------------------------------------------------------------------- 1 | package segments 2 | 3 | type Kotlin struct { 4 | language 5 | } 6 | 7 | func (k *Kotlin) Template() string { 8 | return languageTemplate 9 | } 10 | 11 | func (k *Kotlin) Enabled() bool { 12 | k.extensions = []string{"*.kt", "*.kts", "*.ktm"} 13 | k.commands = []*cmd{ 14 | { 15 | executable: "kotlin", 16 | args: []string{"-version"}, 17 | regex: `Kotlin version (?P((?P[0-9]+).(?P[0-9]+).(?P[0-9]+)))`, 18 | }, 19 | } 20 | k.versionURLTemplate = "https://github.com/JetBrains/kotlin/releases/tag/v{{ .Full }}" 21 | 22 | return k.language.Enabled() 23 | } 24 | -------------------------------------------------------------------------------- /src/segments/kotlin_test.go: -------------------------------------------------------------------------------- 1 | package segments 2 | 3 | import ( 4 | "fmt" 5 | "testing" 6 | 7 | "github.com/stretchr/testify/assert" 8 | ) 9 | 10 | func TestKotlin(t *testing.T) { 11 | cases := []struct { 12 | Case string 13 | ExpectedString string 14 | Version string 15 | }{ 16 | {Case: "Kotlin 1.6.10", ExpectedString: "1.6.10", Version: "Kotlin version 1.6.10-release-923 (JRE 17.0.2+0)"}, 17 | {Case: "Kotlin 1.6.0", ExpectedString: "1.6.0", Version: "Kotlin version 1.6.0-release-915 (JRE 17.0.2+0)"}, 18 | } 19 | for _, tc := range cases { 20 | params := &mockedLanguageParams{ 21 | cmd: "kotlin", 22 | versionParam: "-version", 23 | versionOutput: tc.Version, 24 | extension: "*.kt", 25 | } 26 | env, props := getMockedLanguageEnv(params) 27 | k := &Kotlin{} 28 | k.Init(props, env) 29 | assert.True(t, k.Enabled(), fmt.Sprintf("Failed in case: %s", tc.Case)) 30 | assert.Equal(t, tc.ExpectedString, renderTemplate(env, k.Template(), k), fmt.Sprintf("Failed in case: %s", tc.Case)) 31 | } 32 | } 33 | -------------------------------------------------------------------------------- /src/segments/lua.go: -------------------------------------------------------------------------------- 1 | package segments 2 | 3 | import ( 4 | "github.com/jandedobbeleer/oh-my-posh/src/properties" 5 | ) 6 | 7 | type Lua struct { 8 | language 9 | } 10 | 11 | const ( 12 | PreferredExecutable properties.Property = "preferred_executable" 13 | ) 14 | 15 | func (l *Lua) Template() string { 16 | return languageTemplate 17 | } 18 | 19 | func (l *Lua) Enabled() bool { 20 | l.extensions = []string{"*.lua", "*.rockspec"} 21 | l.folders = []string{"lua"} 22 | l.commands = []*cmd{ 23 | { 24 | executable: "lua", 25 | args: []string{"-v"}, 26 | regex: `Lua (?P((?P[0-9]+).(?P[0-9]+)(.(?P[0-9]+))?))`, 27 | versionURLTemplate: "https://www.lua.org/manual/{{ .Major }}.{{ .Minor }}/readme.html#changes", 28 | }, 29 | { 30 | executable: "luajit", 31 | args: []string{"-v"}, 32 | regex: `LuaJIT (?P((?P[0-9]+).(?P[0-9]+)(.(?P[0-9]+))?))`, 33 | versionURLTemplate: "https://github.com/LuaJIT/LuaJIT/tree/v{{ .Major}}.{{ .Minor}}", 34 | }, 35 | } 36 | 37 | if l.props.GetString(PreferredExecutable, "lua") == "luajit" { 38 | l.commands = []*cmd{l.commands[1], l.commands[0]} 39 | } 40 | 41 | return l.language.Enabled() 42 | } 43 | -------------------------------------------------------------------------------- /src/segments/mvn.go: -------------------------------------------------------------------------------- 1 | package segments 2 | 3 | type Mvn struct { 4 | language 5 | } 6 | 7 | func (m *Mvn) Enabled() bool { 8 | m.extensions = []string{"pom.xml"} 9 | m.commands = []*cmd{ 10 | { 11 | executable: "mvn", 12 | args: []string{"--version"}, 13 | regex: `(?P((?P[0-9]+).(?P[0-9]+).(?P[0-9]+)(?:-(?P[a-z]+-[0-9]+))?))`, 14 | }, 15 | } 16 | m.versionURLTemplate = "https://github.com/apache/maven/releases/tag/maven-{{ .Full }}" 17 | 18 | mvnw, err := m.env.HasParentFilePath("mvnw", false) 19 | if err == nil { 20 | m.commands[0].executable = mvnw.Path 21 | } 22 | 23 | return m.language.Enabled() 24 | } 25 | 26 | func (m *Mvn) Template() string { 27 | return languageTemplate 28 | } 29 | -------------------------------------------------------------------------------- /src/segments/nbgv.go: -------------------------------------------------------------------------------- 1 | package segments 2 | 3 | import ( 4 | "encoding/json" 5 | ) 6 | 7 | type Nbgv struct { 8 | base 9 | 10 | VersionInfo 11 | } 12 | 13 | type VersionInfo struct { 14 | Version string `json:"Version"` 15 | AssemblyVersion string `json:"AssemblyVersion"` 16 | AssemblyInformationalVersion string `json:"AssemblyInformationalVersion"` 17 | NuGetPackageVersion string `json:"NuGetPackageVersion"` 18 | ChocolateyPackageVersion string `json:"ChocolateyPackageVersion"` 19 | NpmPackageVersion string `json:"NpmPackageVersion"` 20 | SimpleVersion string `json:"SimpleVersion"` 21 | VersionFileFound bool `json:"VersionFileFound"` 22 | } 23 | 24 | func (n *Nbgv) Template() string { 25 | return " {{ .Version }} " 26 | } 27 | 28 | func (n *Nbgv) Enabled() bool { 29 | nbgv := "nbgv" 30 | if !n.env.HasCommand(nbgv) { 31 | return false 32 | } 33 | 34 | response, err := n.env.RunCommand(nbgv, "get-version", "--format=json") 35 | if err != nil { 36 | return false 37 | } 38 | 39 | n.VersionInfo = VersionInfo{} 40 | err = json.Unmarshal([]byte(response), &n.VersionInfo) 41 | 42 | if err != nil { 43 | return false 44 | } 45 | 46 | return n.VersionFileFound 47 | } 48 | -------------------------------------------------------------------------------- /src/segments/nim.go: -------------------------------------------------------------------------------- 1 | package segments 2 | 3 | type Nim struct { 4 | language 5 | } 6 | 7 | func (n *Nim) Template() string { 8 | return languageTemplate 9 | } 10 | 11 | func (n *Nim) Enabled() bool { 12 | n.extensions = []string{"*.nim", "*.nims"} 13 | 14 | n.commands = []*cmd{ 15 | { 16 | executable: "nim", 17 | args: []string{"--version"}, 18 | regex: `Nim Compiler Version (?P(?P\d+)\.(?P\d+)\.(?P\d+))`, 19 | }, 20 | } 21 | return n.language.Enabled() 22 | } 23 | -------------------------------------------------------------------------------- /src/segments/nixshell.go: -------------------------------------------------------------------------------- 1 | package segments 2 | 3 | import ( 4 | "path/filepath" 5 | "strings" 6 | ) 7 | 8 | const ( 9 | NONE = "none" 10 | ) 11 | 12 | type NixShell struct { 13 | base 14 | 15 | Type string 16 | } 17 | 18 | func (n *NixShell) Template() string { 19 | return "via {{ .Type }}-shell" 20 | } 21 | 22 | func (n *NixShell) DetectType() string { 23 | shellType := n.env.Getenv("IN_NIX_SHELL") 24 | 25 | switch shellType { 26 | case "pure", "impure": 27 | return shellType 28 | default: 29 | if n.InNewNixShell() { 30 | return UNKNOWN 31 | } 32 | 33 | return NONE 34 | } 35 | } 36 | 37 | // Hack to detect if we're in a `nix shell` (in contrast to a `nix-shell`). 38 | // A better way to do this will be enabled by https://github.com/NixOS/nix/issues/6677 39 | // so we check if the PATH contains a nix store. 40 | func (n *NixShell) InNewNixShell() bool { 41 | paths := filepath.SplitList(n.env.Getenv("PATH")) 42 | 43 | for _, p := range paths { 44 | if strings.Contains(p, "/nix/store") { 45 | return true 46 | } 47 | } 48 | 49 | return false 50 | } 51 | 52 | func (n *NixShell) Enabled() bool { 53 | n.Type = n.DetectType() 54 | 55 | return n.Type != NONE 56 | } 57 | -------------------------------------------------------------------------------- /src/segments/npm.go: -------------------------------------------------------------------------------- 1 | package segments 2 | 3 | type Npm struct { 4 | language 5 | } 6 | 7 | func (n *Npm) Enabled() bool { 8 | n.extensions = []string{"package.json", "package-lock.json"} 9 | n.commands = []*cmd{ 10 | { 11 | executable: "npm", 12 | args: []string{"--version"}, 13 | regex: `(?P((?P[0-9]+).(?P[0-9]+).(?P[0-9]+)))`, 14 | }, 15 | } 16 | n.versionURLTemplate = "https://github.com/npm/cli/releases/tag/v{{ .Full }}" 17 | 18 | return n.language.Enabled() 19 | } 20 | 21 | func (n *Npm) Template() string { 22 | return " \ue71e {{.Full}} " 23 | } 24 | -------------------------------------------------------------------------------- /src/segments/npm_test.go: -------------------------------------------------------------------------------- 1 | package segments 2 | 3 | import ( 4 | "fmt" 5 | "testing" 6 | 7 | "github.com/alecthomas/assert" 8 | ) 9 | 10 | func TestNpm(t *testing.T) { 11 | cases := []struct { 12 | Case string 13 | ExpectedString string 14 | Version string 15 | }{ 16 | {Case: "1.0.0", ExpectedString: "\ue71e 1.0.0", Version: "1.0.0"}, 17 | } 18 | for _, tc := range cases { 19 | params := &mockedLanguageParams{ 20 | cmd: "npm", 21 | versionParam: "--version", 22 | versionOutput: tc.Version, 23 | extension: "package.json", 24 | } 25 | env, props := getMockedLanguageEnv(params) 26 | npm := &Npm{} 27 | npm.Init(props, env) 28 | assert.True(t, npm.Enabled(), fmt.Sprintf("Failed in case: %s", tc.Case)) 29 | assert.Equal(t, tc.ExpectedString, renderTemplate(env, npm.Template(), npm), fmt.Sprintf("Failed in case: %s", tc.Case)) 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /src/segments/nx.go: -------------------------------------------------------------------------------- 1 | package segments 2 | 3 | type Nx struct { 4 | language 5 | } 6 | 7 | func (a *Nx) Template() string { 8 | return languageTemplate 9 | } 10 | 11 | func (a *Nx) Enabled() bool { 12 | a.extensions = []string{"workspace.json", "nx.json"} 13 | a.commands = []*cmd{ 14 | { 15 | regex: `(?:(?P((?P[0-9]+).(?P[0-9]+).(?P[0-9]+))))`, 16 | getVersion: a.getVersion, 17 | }, 18 | } 19 | a.versionURLTemplate = "https://github.com/nrwl/nx/releases/tag/{{.Full}}" 20 | 21 | return a.language.Enabled() 22 | } 23 | 24 | func (a *Nx) getVersion() (string, error) { 25 | return a.nodePackageVersion("nx") 26 | } 27 | -------------------------------------------------------------------------------- /src/segments/ocaml.go: -------------------------------------------------------------------------------- 1 | package segments 2 | 3 | type OCaml struct { 4 | language 5 | } 6 | 7 | func (o *OCaml) Template() string { 8 | return languageTemplate 9 | } 10 | 11 | func (o *OCaml) Enabled() bool { 12 | o.extensions = []string{"*.ml", "*.mli", "dune", "dune-project", "dune-workspace"} 13 | o.commands = []*cmd{ 14 | { 15 | executable: "ocaml", 16 | args: []string{"-version"}, 17 | regex: `The OCaml toplevel, version (?P((?P[0-9]+).(?P[0-9]+).(?P[0-9]+))(-(?P[a-z]+))?)`, 18 | }, 19 | } 20 | 21 | return o.language.Enabled() 22 | } 23 | -------------------------------------------------------------------------------- /src/segments/ocaml_test.go: -------------------------------------------------------------------------------- 1 | package segments 2 | 3 | import ( 4 | "fmt" 5 | "testing" 6 | 7 | "github.com/stretchr/testify/assert" 8 | ) 9 | 10 | func TestOCaml(t *testing.T) { 11 | cases := []struct { 12 | Case string 13 | ExpectedString string 14 | Version string 15 | }{ 16 | {Case: "OCaml 4.12.0", ExpectedString: "4.12.0", Version: "The OCaml toplevel, version 4.12.0"}, 17 | {Case: "OCaml 4.11.0", ExpectedString: "4.11.0", Version: "The OCaml toplevel, version 4.11.0"}, 18 | {Case: "OCaml 4.13.0", ExpectedString: "4.13.0", Version: "The OCaml toplevel, version 4.13.0"}, 19 | } 20 | for _, tc := range cases { 21 | params := &mockedLanguageParams{ 22 | cmd: "ocaml", 23 | versionParam: "-version", 24 | versionOutput: tc.Version, 25 | extension: "*.ml", 26 | } 27 | env, props := getMockedLanguageEnv(params) 28 | o := &OCaml{} 29 | o.Init(props, env) 30 | assert.True(t, o.Enabled(), fmt.Sprintf("Failed in case: %s", tc.Case)) 31 | assert.Equal(t, tc.ExpectedString, renderTemplate(env, o.Template(), o), fmt.Sprintf("Failed in case: %s", tc.Case)) 32 | } 33 | } 34 | -------------------------------------------------------------------------------- /src/segments/perl.go: -------------------------------------------------------------------------------- 1 | package segments 2 | 3 | type Perl struct { 4 | language 5 | } 6 | 7 | func (p *Perl) Template() string { 8 | return languageTemplate 9 | } 10 | 11 | func (p *Perl) Enabled() bool { 12 | perlRegex := `This is perl.*v(?P(?P[0-9]+)(?:\.(?P[0-9]+))(?:\.(?P[0-9]+))?).* built for .+` 13 | p.extensions = []string{ 14 | ".perl-version", 15 | "*.pl", 16 | "*.pm", 17 | "*.t", 18 | } 19 | p.commands = []*cmd{ 20 | { 21 | executable: "perl", 22 | args: []string{"-version"}, 23 | regex: perlRegex, 24 | }, 25 | } 26 | 27 | return p.language.Enabled() 28 | } 29 | -------------------------------------------------------------------------------- /src/segments/perl_test.go: -------------------------------------------------------------------------------- 1 | package segments 2 | 3 | import ( 4 | "fmt" 5 | "testing" 6 | 7 | "github.com/stretchr/testify/assert" 8 | ) 9 | 10 | func TestPerl(t *testing.T) { 11 | cases := []struct { 12 | Case string 13 | ExpectedString string 14 | Version string 15 | PerlHomeVersion string 16 | PerlHomeEnabled bool 17 | }{ 18 | { 19 | Case: "v5.12+", 20 | ExpectedString: "5.32.1", 21 | Version: "This is perl 5, version 32, subversion 1 (v5.32.1) built for MSWin32-x64-multi-thread", 22 | }, 23 | { 24 | Case: "v5.6 - v5.10", 25 | ExpectedString: "5.6.1", 26 | Version: "This is perl, v5.6.1 built for MSWin32-x86-multi-thread", 27 | }, 28 | } 29 | for _, tc := range cases { 30 | params := &mockedLanguageParams{ 31 | cmd: "perl", 32 | versionParam: "-version", 33 | versionOutput: tc.Version, 34 | extension: ".perl-version", 35 | } 36 | env, props := getMockedLanguageEnv(params) 37 | 38 | p := &Perl{} 39 | p.Init(props, env) 40 | assert.True(t, p.Enabled(), fmt.Sprintf("Failed in case: %s", tc.Case)) 41 | assert.Equal(t, tc.ExpectedString, renderTemplate(env, p.Template(), p), fmt.Sprintf("Failed in case: %s", tc.Case)) 42 | } 43 | } 44 | -------------------------------------------------------------------------------- /src/segments/php.go: -------------------------------------------------------------------------------- 1 | package segments 2 | 3 | type Php struct { 4 | language 5 | } 6 | 7 | func (p *Php) Template() string { 8 | return languageTemplate 9 | } 10 | 11 | func (p *Php) Enabled() bool { 12 | p.extensions = []string{"*.php", "composer.json", "composer.lock", ".php-version", "blade.php"} 13 | p.commands = []*cmd{ 14 | { 15 | executable: "php", 16 | args: []string{"--version"}, 17 | regex: `(?:PHP (?P((?P[0-9]+).(?P[0-9]+).(?P[0-9]+))))`, 18 | }, 19 | } 20 | p.versionURLTemplate = "https://www.php.net/ChangeLog-{{ .Major }}.php#PHP_{{ .Major }}_{{ .Minor }}" 21 | 22 | return p.language.Enabled() 23 | } 24 | -------------------------------------------------------------------------------- /src/segments/php_test.go: -------------------------------------------------------------------------------- 1 | package segments 2 | 3 | import ( 4 | "fmt" 5 | "testing" 6 | 7 | "github.com/stretchr/testify/assert" 8 | ) 9 | 10 | func TestPhp(t *testing.T) { 11 | cases := []struct { 12 | Case string 13 | ExpectedString string 14 | Version string 15 | }{ 16 | {Case: "PHP 6.1.0", ExpectedString: "6.1.0", Version: "PHP 6.1.0(cli) (built: Jul 2 2021 03:59:48) ( NTS )"}, 17 | {Case: "php 7.4.21", ExpectedString: "7.4.21", Version: "PHP 7.4.21 (cli) (built: Jul 2 2021 03:59:48) ( NTS )"}, 18 | } 19 | for _, tc := range cases { 20 | params := &mockedLanguageParams{ 21 | cmd: "php", 22 | versionParam: "--version", 23 | versionOutput: tc.Version, 24 | extension: "*.php", 25 | } 26 | env, props := getMockedLanguageEnv(params) 27 | j := &Php{} 28 | j.Init(props, env) 29 | assert.True(t, j.Enabled(), fmt.Sprintf("Failed in case: %s", tc.Case)) 30 | assert.Equal(t, tc.ExpectedString, renderTemplate(env, j.Template(), j), fmt.Sprintf("Failed in case: %s", tc.Case)) 31 | } 32 | } 33 | -------------------------------------------------------------------------------- /src/segments/pnpm.go: -------------------------------------------------------------------------------- 1 | package segments 2 | 3 | type Pnpm struct { 4 | language 5 | } 6 | 7 | func (n *Pnpm) Enabled() bool { 8 | n.extensions = []string{"package.json", "pnpm-lock.yaml"} 9 | n.commands = []*cmd{ 10 | { 11 | executable: "pnpm", 12 | args: []string{"--version"}, 13 | regex: `(?P((?P[0-9]+).(?P[0-9]+).(?P[0-9]+)))`, 14 | }, 15 | } 16 | n.versionURLTemplate = "https://github.com/pnpm/pnpm/releases/tag/v{{ .Full }}" 17 | 18 | return n.language.Enabled() 19 | } 20 | 21 | func (n *Pnpm) Template() string { 22 | return " \U000F02C1 {{.Full}} " 23 | } 24 | -------------------------------------------------------------------------------- /src/segments/pnpm_test.go: -------------------------------------------------------------------------------- 1 | package segments 2 | 3 | import ( 4 | "fmt" 5 | "testing" 6 | 7 | "github.com/alecthomas/assert" 8 | ) 9 | 10 | func TestPnpm(t *testing.T) { 11 | cases := []struct { 12 | Case string 13 | ExpectedString string 14 | Version string 15 | }{ 16 | {Case: "1.0.0", ExpectedString: "\U000F02C1 1.0.0", Version: "1.0.0"}, 17 | } 18 | for _, tc := range cases { 19 | params := &mockedLanguageParams{ 20 | cmd: "pnpm", 21 | versionParam: "--version", 22 | versionOutput: tc.Version, 23 | extension: "package.json", 24 | } 25 | env, props := getMockedLanguageEnv(params) 26 | pnpm := &Pnpm{} 27 | pnpm.Init(props, env) 28 | assert.True(t, pnpm.Enabled(), fmt.Sprintf("Failed in case: %s", tc.Case)) 29 | assert.Equal(t, tc.ExpectedString, renderTemplate(env, pnpm.Template(), pnpm), fmt.Sprintf("Failed in case: %s", tc.Case)) 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /src/segments/r.go: -------------------------------------------------------------------------------- 1 | package segments 2 | 3 | type R struct { 4 | language 5 | } 6 | 7 | func (r *R) Template() string { 8 | return languageTemplate 9 | } 10 | 11 | func (r *R) Enabled() bool { 12 | rRegex := `version (?P((?P[0-9]+).(?P[0-9]+).(?P[0-9]+)))` 13 | r.extensions = []string{"*.R", "*.Rmd", "*.Rsx", "*.Rda", "*.Rd", "*.Rproj", ".Rproj.user"} 14 | r.commands = []*cmd{ 15 | { 16 | executable: "Rscript", 17 | args: []string{"--version"}, 18 | regex: rRegex, 19 | }, 20 | { 21 | executable: "R", 22 | args: []string{"--version"}, 23 | regex: rRegex, 24 | }, 25 | { 26 | executable: "R.exe", 27 | args: []string{"--version"}, 28 | regex: rRegex, 29 | }, 30 | } 31 | r.versionURLTemplate = "https://www.r-project.org/" 32 | 33 | return r.language.Enabled() 34 | } 35 | -------------------------------------------------------------------------------- /src/segments/react.go: -------------------------------------------------------------------------------- 1 | package segments 2 | 3 | type React struct { 4 | language 5 | } 6 | 7 | func (r *React) Template() string { 8 | return languageTemplate 9 | } 10 | 11 | func (r *React) Enabled() bool { 12 | r.extensions = []string{"package.json"} 13 | r.commands = []*cmd{ 14 | { 15 | regex: `(?:(?P((?P[0-9]+).(?P[0-9]+).(?P[0-9]+))))`, 16 | getVersion: r.getVersion, 17 | }, 18 | } 19 | r.versionURLTemplate = "https://github.com/facebook/react/releases/tag/v{{.Full}}" 20 | 21 | if !r.hasNodePackage("react") { 22 | return false 23 | } 24 | 25 | return r.language.Enabled() 26 | } 27 | 28 | func (r *React) getVersion() (string, error) { 29 | return r.nodePackageVersion("react") 30 | } 31 | -------------------------------------------------------------------------------- /src/segments/root.go: -------------------------------------------------------------------------------- 1 | package segments 2 | 3 | type Root struct { 4 | base 5 | } 6 | 7 | func (rt *Root) Template() string { 8 | return " \uF0E7 " 9 | } 10 | 11 | func (rt *Root) Enabled() bool { 12 | return rt.env.Root() 13 | } 14 | -------------------------------------------------------------------------------- /src/segments/ruby.go: -------------------------------------------------------------------------------- 1 | package segments 2 | 3 | type Ruby struct { 4 | language 5 | } 6 | 7 | func (r *Ruby) Template() string { 8 | return languageTemplate 9 | } 10 | 11 | func (r *Ruby) Enabled() bool { 12 | r.extensions = []string{"*.rb", "Rakefile", "Gemfile"} 13 | r.commands = []*cmd{ 14 | { 15 | executable: "rbenv", 16 | args: []string{"version-name"}, 17 | regex: `(?P.+)`, 18 | }, 19 | { 20 | executable: "rvm-prompt", 21 | args: []string{"i", "v", "g"}, 22 | regex: `(?P.+)`, 23 | }, 24 | { 25 | executable: "chruby", 26 | args: []string(nil), 27 | regex: `\* (?P.+)\n`, 28 | }, 29 | { 30 | executable: "asdf", 31 | args: []string{"current", "ruby"}, 32 | regex: `ruby\s+(?P[^\s]+)\s+`, 33 | }, 34 | { 35 | executable: "ruby", 36 | args: []string{"--version"}, 37 | regex: `ruby\s+(?P[^\s]+)\s+`, 38 | }, 39 | } 40 | 41 | enabled := r.language.Enabled() 42 | 43 | // this happens when no version is set 44 | if r.Full == "______" { 45 | r.Full = "" 46 | } 47 | 48 | return enabled 49 | } 50 | -------------------------------------------------------------------------------- /src/segments/rust.go: -------------------------------------------------------------------------------- 1 | package segments 2 | 3 | type Rust struct { 4 | language 5 | } 6 | 7 | func (r *Rust) Template() string { 8 | return languageTemplate 9 | } 10 | 11 | func (r *Rust) Enabled() bool { 12 | r.extensions = []string{"*.rs", "Cargo.toml", "Cargo.lock"} 13 | r.commands = []*cmd{ 14 | { 15 | executable: "rustc", 16 | args: []string{"--version"}, 17 | regex: `rustc (?P((?P[0-9]+).(?P[0-9]+).(?P[0-9]+))(-(?P[a-z]+))?)(( \((?P[0-9a-f]+ [0-9]+-[0-9]+-[0-9]+)\))?)`, 18 | }, 19 | } 20 | 21 | return r.language.Enabled() 22 | } 23 | -------------------------------------------------------------------------------- /src/segments/rust_test.go: -------------------------------------------------------------------------------- 1 | package segments 2 | 3 | import ( 4 | "fmt" 5 | "testing" 6 | 7 | "github.com/stretchr/testify/assert" 8 | ) 9 | 10 | func TestRust(t *testing.T) { 11 | cases := []struct { 12 | Case string 13 | ExpectedString string 14 | Version string 15 | }{ 16 | {Case: "Rust 1.64.0", ExpectedString: "1.64.0", Version: "rustc 1.64.0"}, 17 | {Case: "Rust 1.53.0", ExpectedString: "1.53.0", Version: "rustc 1.53.0 (4369396ce 2021-04-27)"}, 18 | {Case: "Rust 1.66.0", ExpectedString: "1.66.0-nightly", Version: "rustc 1.66.0-nightly (01af5040f 2022-10-04)"}, 19 | } 20 | for _, tc := range cases { 21 | params := &mockedLanguageParams{ 22 | cmd: "rustc", 23 | versionParam: "--version", 24 | versionOutput: tc.Version, 25 | extension: "*.rs", 26 | } 27 | env, props := getMockedLanguageEnv(params) 28 | r := &Rust{} 29 | r.Init(props, env) 30 | assert.True(t, r.Enabled(), fmt.Sprintf("Failed in case: %s", tc.Case)) 31 | assert.Equal(t, tc.ExpectedString, renderTemplate(env, r.Template(), r), fmt.Sprintf("Failed in case: %s", tc.Case)) 32 | } 33 | } 34 | -------------------------------------------------------------------------------- /src/segments/session.go: -------------------------------------------------------------------------------- 1 | package segments 2 | 3 | import ( 4 | "github.com/jandedobbeleer/oh-my-posh/src/regex" 5 | "github.com/jandedobbeleer/oh-my-posh/src/runtime" 6 | ) 7 | 8 | type Session struct { 9 | base 10 | 11 | SSHSession bool 12 | } 13 | 14 | func (s *Session) Enabled() bool { 15 | s.SSHSession = s.activeSSHSession() 16 | return true 17 | } 18 | 19 | func (s *Session) Template() string { 20 | return " {{ if .SSHSession }}\ueba9 {{ end }}{{ .UserName }}@{{ .HostName }} " 21 | } 22 | 23 | func (s *Session) activeSSHSession() bool { 24 | keys := []string{ 25 | "SSH_CONNECTION", 26 | "SSH_CLIENT", 27 | } 28 | 29 | for _, key := range keys { 30 | content := s.env.Getenv(key) 31 | if content != "" { 32 | return true 33 | } 34 | } 35 | 36 | if s.env.Platform() == runtime.WINDOWS { 37 | return false 38 | } 39 | 40 | whoAmI, err := s.env.RunCommand("who", "am", "i") 41 | if err != nil { 42 | return false 43 | } 44 | 45 | return regex.MatchString(`\(\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3}\)`, whoAmI) 46 | } 47 | -------------------------------------------------------------------------------- /src/segments/shell.go: -------------------------------------------------------------------------------- 1 | package segments 2 | 3 | import ( 4 | "strings" 5 | 6 | "github.com/jandedobbeleer/oh-my-posh/src/properties" 7 | ) 8 | 9 | type Shell struct { 10 | base 11 | 12 | Name string 13 | Version string 14 | } 15 | 16 | const ( 17 | // MappedShellNames allows for custom text in place of shell names 18 | MappedShellNames properties.Property = "mapped_shell_names" 19 | ) 20 | 21 | func (s *Shell) Template() string { 22 | return NameTemplate 23 | } 24 | 25 | func (s *Shell) Enabled() bool { 26 | mappedNames := s.props.GetKeyValueMap(MappedShellNames, make(map[string]string)) 27 | s.Name = s.env.Shell() 28 | s.Version = s.env.Flags().ShellVersion 29 | for key, val := range mappedNames { 30 | if strings.EqualFold(s.Name, key) { 31 | s.Name = val 32 | break 33 | } 34 | } 35 | return true 36 | } 37 | -------------------------------------------------------------------------------- /src/segments/spotify.go: -------------------------------------------------------------------------------- 1 | package segments 2 | 3 | import ( 4 | "github.com/jandedobbeleer/oh-my-posh/src/properties" 5 | ) 6 | 7 | type Spotify struct { 8 | base 9 | 10 | MusicPlayer 11 | } 12 | 13 | type MusicPlayer struct { 14 | Status string 15 | Artist string 16 | Track string 17 | Icon string 18 | } 19 | 20 | const ( 21 | // PlayingIcon indicates a song is playing 22 | PlayingIcon properties.Property = "playing_icon" 23 | // PausedIcon indicates a song is paused 24 | PausedIcon properties.Property = "paused_icon" 25 | // StoppedIcon indicates a song is stopped 26 | StoppedIcon properties.Property = "stopped_icon" 27 | 28 | playing = "playing" 29 | stopped = "stopped" 30 | paused = "paused" 31 | ) 32 | 33 | func (s *Spotify) Template() string { 34 | return " {{ .Icon }}{{ if ne .Status \"stopped\" }}{{ .Artist }} - {{ .Track }}{{ end }} " 35 | } 36 | 37 | func (s *Spotify) resolveIcon() { 38 | switch s.Status { 39 | case stopped: 40 | // in this case, no artist or track info 41 | s.Icon = s.props.GetString(StoppedIcon, "\uF04D ") 42 | case paused: 43 | s.Icon = s.props.GetString(PausedIcon, "\uF8E3 ") 44 | case playing: 45 | s.Icon = s.props.GetString(PlayingIcon, "\uE602 ") 46 | } 47 | } 48 | -------------------------------------------------------------------------------- /src/segments/spotify_darwin.go: -------------------------------------------------------------------------------- 1 | //go:build darwin 2 | 3 | package segments 4 | 5 | func (s *Spotify) Enabled() bool { 6 | // Check if running 7 | running := s.runAppleScriptCommand("application \"Spotify\" is running") 8 | if running == "false" || running == "" { 9 | s.Status = stopped 10 | return false 11 | } 12 | 13 | s.Status = s.runAppleScriptCommand("tell application \"Spotify\" to player state as string") 14 | 15 | if len(s.Status) == 0 { 16 | s.Status = stopped 17 | return false 18 | } 19 | 20 | if s.Status == stopped { 21 | return false 22 | } 23 | 24 | s.Artist = s.runAppleScriptCommand("tell application \"Spotify\" to artist of current track as string") 25 | s.Track = s.runAppleScriptCommand("tell application \"Spotify\" to name of current track as string") 26 | 27 | s.resolveIcon() 28 | 29 | return true 30 | } 31 | 32 | func (s *Spotify) runAppleScriptCommand(command string) string { 33 | val, _ := s.env.RunCommand("osascript", "-e", command) 34 | return val 35 | } 36 | -------------------------------------------------------------------------------- /src/segments/spotify_noop.go: -------------------------------------------------------------------------------- 1 | //go:build !linux && !darwin && !windows 2 | 3 | package segments 4 | 5 | func (s *Spotify) Enabled() bool { 6 | return false 7 | } 8 | -------------------------------------------------------------------------------- /src/segments/spotify_test.go: -------------------------------------------------------------------------------- 1 | package segments 2 | 3 | import ( 4 | "testing" 5 | 6 | "github.com/jandedobbeleer/oh-my-posh/src/properties" 7 | "github.com/jandedobbeleer/oh-my-posh/src/runtime/mock" 8 | 9 | "github.com/stretchr/testify/assert" 10 | ) 11 | 12 | func TestSpotifyStringPlayingSong(t *testing.T) { 13 | expected := "\ue602 Candlemass - Spellbreaker" 14 | env := new(mock.Environment) 15 | 16 | s := &Spotify{ 17 | MusicPlayer: MusicPlayer{ 18 | Artist: "Candlemass", 19 | Track: "Spellbreaker", 20 | Status: "playing", 21 | Icon: "\ue602 ", 22 | }, 23 | } 24 | s.Init(properties.Map{}, env) 25 | 26 | assert.Equal(t, expected, renderTemplate(env, s.Template(), s)) 27 | } 28 | 29 | func TestSpotifyStringStoppedSong(t *testing.T) { 30 | expected := "\uf04d" 31 | env := new(mock.Environment) 32 | 33 | s := &Spotify{ 34 | MusicPlayer: MusicPlayer{ 35 | Artist: "Candlemass", 36 | Track: "Spellbreaker", 37 | Status: "stopped", 38 | Icon: "\uf04d ", 39 | }, 40 | } 41 | s.Init(properties.Map{}, env) 42 | 43 | assert.Equal(t, expected, renderTemplate(env, s.Template(), s)) 44 | } 45 | -------------------------------------------------------------------------------- /src/segments/svelte.go: -------------------------------------------------------------------------------- 1 | package segments 2 | 3 | type Svelte struct { 4 | language 5 | } 6 | 7 | func (s *Svelte) Template() string { 8 | return languageTemplate 9 | } 10 | 11 | func (s *Svelte) Enabled() bool { 12 | s.extensions = []string{"svelte.config.js"} 13 | s.commands = []*cmd{ 14 | { 15 | regex: `(?:(?P((?P[0-9]+).(?P[0-9]+).(?P[0-9]+))))`, 16 | getVersion: s.getVersion, 17 | }, 18 | } 19 | s.versionURLTemplate = "https://github.com/sveltejs/svelte/releases/tag/svelte%40{{.Full}}" 20 | 21 | return s.language.Enabled() 22 | } 23 | 24 | func (s *Svelte) getVersion() (string, error) { 25 | return s.nodePackageVersion("svelte") 26 | } 27 | -------------------------------------------------------------------------------- /src/segments/swift.go: -------------------------------------------------------------------------------- 1 | package segments 2 | 3 | type Swift struct { 4 | language 5 | } 6 | 7 | func (s *Swift) Template() string { 8 | return languageTemplate 9 | } 10 | 11 | func (s *Swift) Enabled() bool { 12 | s.extensions = []string{"*.swift", "*.SWIFT", "Podfile"} 13 | s.commands = []*cmd{ 14 | { 15 | executable: "swift", 16 | args: []string{"--version"}, 17 | regex: `Swift version (?P((?P[0-9]+).(?P[0-9]+)((.|-)(?P[0-9]+|dev))?))`, 18 | }, 19 | } 20 | s.versionURLTemplate = "https://github.com/apple/swift/releases/tag/swift-{{ .Full }}-RELEASE" 21 | 22 | return s.language.Enabled() 23 | } 24 | -------------------------------------------------------------------------------- /src/segments/sysinfo.go: -------------------------------------------------------------------------------- 1 | package segments 2 | 3 | import ( 4 | "github.com/jandedobbeleer/oh-my-posh/src/properties" 5 | "github.com/jandedobbeleer/oh-my-posh/src/runtime" 6 | ) 7 | 8 | type SystemInfo struct { 9 | base 10 | 11 | runtime.SystemInfo 12 | Precision int 13 | } 14 | 15 | const ( 16 | // Precision number of decimal places to show 17 | Precision properties.Property = "precision" 18 | ) 19 | 20 | func (s *SystemInfo) Template() string { 21 | return " {{ round .PhysicalPercentUsed .Precision }} " 22 | } 23 | 24 | func (s *SystemInfo) Enabled() bool { 25 | s.Precision = s.props.GetInt(Precision, 2) 26 | 27 | sysInfo, err := s.env.SystemInfo() 28 | if err != nil { 29 | return false 30 | } 31 | 32 | s.SystemInfo = *sysInfo 33 | 34 | if s.PhysicalPercentUsed == 0 && s.SwapPercentUsed == 0 { 35 | return false 36 | } 37 | 38 | return true 39 | } 40 | -------------------------------------------------------------------------------- /src/segments/talosctl.go: -------------------------------------------------------------------------------- 1 | package segments 2 | 3 | import ( 4 | "errors" 5 | "path/filepath" 6 | 7 | "github.com/jandedobbeleer/oh-my-posh/src/log" 8 | "gopkg.in/yaml.v3" 9 | ) 10 | 11 | type TalosCTL struct { 12 | base 13 | 14 | Context string `yaml:"context"` 15 | } 16 | 17 | func (t *TalosCTL) Template() string { 18 | return " {{ .Context}} " 19 | } 20 | 21 | func (t *TalosCTL) Enabled() bool { 22 | cfgDir := filepath.Join(t.env.Home(), ".talos") 23 | configFile, err := t.getActiveConfig(cfgDir) 24 | if err != nil { 25 | log.Error(err) 26 | return false 27 | } 28 | 29 | err = yaml.Unmarshal([]byte(configFile), t) 30 | if err != nil { 31 | log.Error(err) 32 | return false 33 | } 34 | 35 | if len(t.Context) == 0 { 36 | return false 37 | } 38 | 39 | return true 40 | } 41 | 42 | func (t *TalosCTL) getActiveConfig(cfgDir string) (string, error) { 43 | activeConfigFile := filepath.Join(cfgDir, "config") 44 | activeConfigData := t.env.FileContent(activeConfigFile) 45 | if len(activeConfigData) == 0 { 46 | return "", errors.New("no active config found") 47 | } 48 | return activeConfigData, nil 49 | } 50 | -------------------------------------------------------------------------------- /src/segments/tauri.go: -------------------------------------------------------------------------------- 1 | package segments 2 | 3 | import ( 4 | "path/filepath" 5 | ) 6 | 7 | type Tauri struct { 8 | language 9 | } 10 | 11 | func (t *Tauri) Template() string { 12 | return languageTemplate 13 | } 14 | 15 | func (t *Tauri) Enabled() bool { 16 | t.extensions = []string{"tauri.conf.json"} 17 | t.folders = []string{"src-tauri"} 18 | t.commands = []*cmd{ 19 | { 20 | regex: `(?:(?P((?P[0-9]+).(?P[0-9]+).(?P[0-9]+))))`, 21 | getVersion: t.getVersion, 22 | }, 23 | } 24 | t.versionURLTemplate = "https://github.com/tauri-apps/tauri/releases/tag/tauri-v{{.Full}}" 25 | 26 | return t.language.Enabled() 27 | } 28 | 29 | func (t *Tauri) getVersion() (string, error) { 30 | return t.nodePackageVersion(filepath.Join("@tauri-apps", "api")) 31 | } 32 | -------------------------------------------------------------------------------- /src/segments/text.go: -------------------------------------------------------------------------------- 1 | package segments 2 | 3 | type Text struct { 4 | base 5 | } 6 | 7 | func (t *Text) Template() string { 8 | return " " 9 | } 10 | 11 | func (t *Text) Enabled() bool { 12 | return true 13 | } 14 | -------------------------------------------------------------------------------- /src/segments/time.go: -------------------------------------------------------------------------------- 1 | package segments 2 | 3 | import ( 4 | "time" 5 | 6 | "github.com/jandedobbeleer/oh-my-posh/src/properties" 7 | ) 8 | 9 | type Time struct { 10 | base 11 | 12 | CurrentDate time.Time 13 | Format string 14 | } 15 | 16 | const ( 17 | // TimeFormat uses the reference time Mon Jan 2 15:04:05 MST 2006 to show the pattern with which to format the current time 18 | TimeFormat properties.Property = "time_format" 19 | ) 20 | 21 | func (t *Time) Template() string { 22 | return " {{ .CurrentDate | date .Format }} " 23 | } 24 | 25 | func (t *Time) Enabled() bool { 26 | // if no date set, use now(unit testing) 27 | t.Format = t.props.GetString(TimeFormat, "15:04:05") 28 | if t.CurrentDate.IsZero() { 29 | t.CurrentDate = time.Now() 30 | } 31 | return true 32 | } 33 | -------------------------------------------------------------------------------- /src/segments/ui5tooling.go: -------------------------------------------------------------------------------- 1 | package segments 2 | 3 | const UI5ToolingYamlPattern = "*ui5*.y*ml" 4 | 5 | type UI5Tooling struct { 6 | language 7 | HasUI5YamlInParentDir bool 8 | } 9 | 10 | func (u *UI5Tooling) Template() string { 11 | return languageTemplate 12 | } 13 | 14 | func (u *UI5Tooling) Enabled() bool { 15 | u.extensions = []string{UI5ToolingYamlPattern} 16 | u.displayMode = u.props.GetString(DisplayMode, DisplayModeContext) 17 | u.commands = []*cmd{ 18 | { 19 | executable: "ui5", 20 | args: []string{"--version"}, 21 | regex: `(?:(?P((?P[0-9]+).(?P[0-9]+).(?P[0-9]+))))`, 22 | }, 23 | } 24 | u.versionURLTemplate = "https://github.com/SAP/ui5-cli/releases/tag/v{{ .Full }}" 25 | u.language.loadContext = u.loadContext 26 | u.language.inContext = u.inContext 27 | 28 | return u.language.Enabled() 29 | } 30 | 31 | func (u *UI5Tooling) loadContext() { 32 | // for searching ui5 yaml from subdirectories of UI5 project root - up to 4 levels 33 | u.HasUI5YamlInParentDir = u.env.HasFileInParentDirs(UI5ToolingYamlPattern, 4) 34 | } 35 | 36 | func (u *UI5Tooling) inContext() bool { 37 | return u.HasUI5YamlInParentDir 38 | } 39 | -------------------------------------------------------------------------------- /src/segments/v.go: -------------------------------------------------------------------------------- 1 | package segments 2 | 3 | type V struct { 4 | language 5 | } 6 | 7 | func (v *V) Template() string { 8 | return languageTemplate 9 | } 10 | 11 | func (v *V) Enabled() bool { 12 | v.extensions = []string{"*.v"} 13 | 14 | v.commands = []*cmd{ 15 | { 16 | executable: "v", 17 | args: []string{"--version"}, 18 | regex: `V (?P(?P\d+)\.(?P\d+)\.(?P\d+)) [a-f0-9]+`, 19 | }, 20 | } 21 | return v.language.Enabled() 22 | } 23 | -------------------------------------------------------------------------------- /src/segments/v_test.go: -------------------------------------------------------------------------------- 1 | package segments 2 | 3 | import ( 4 | "fmt" 5 | "testing" 6 | 7 | "github.com/stretchr/testify/assert" 8 | ) 9 | 10 | func TestV(t *testing.T) { 11 | cases := []struct { 12 | Case string 13 | ExpectedString string 14 | Version string 15 | }{ 16 | { 17 | Case: "V 0.4.9", 18 | ExpectedString: "0.4.9", 19 | Version: "V 0.4.9 b487986", 20 | }, 21 | { 22 | Case: "V 0.4.8", 23 | ExpectedString: "0.4.8", 24 | Version: "V 0.4.8 a123456", 25 | }, 26 | { 27 | Case: "V 0.4.7", 28 | ExpectedString: "0.4.7", 29 | Version: "V 0.4.7 f789012", 30 | }, 31 | } 32 | 33 | for _, tc := range cases { 34 | params := &mockedLanguageParams{ 35 | cmd: "v", 36 | versionParam: "--version", 37 | versionOutput: tc.Version, 38 | extension: "*.v", 39 | } 40 | env, props := getMockedLanguageEnv(params) 41 | v := &V{} 42 | v.Init(props, env) 43 | assert.True(t, v.Enabled(), fmt.Sprintf("Failed in case: %s", tc.Case)) 44 | assert.Equal(t, tc.ExpectedString, renderTemplate(env, v.Template(), v), fmt.Sprintf("Failed in case: %s", tc.Case)) 45 | } 46 | } 47 | -------------------------------------------------------------------------------- /src/segments/vala.go: -------------------------------------------------------------------------------- 1 | package segments 2 | 3 | type Vala struct { 4 | language 5 | } 6 | 7 | func (v *Vala) Template() string { 8 | return languageTemplate 9 | } 10 | 11 | func (v *Vala) Enabled() bool { 12 | v.extensions = []string{"*.vala"} 13 | v.commands = []*cmd{ 14 | { 15 | executable: "vala", 16 | args: []string{"--version"}, 17 | regex: `Vala (?P((?P[0-9]+).(?P[0-9]+).(?P[0-9]+)))`, 18 | }, 19 | } 20 | v.versionURLTemplate = "https://gitlab.gnome.org/GNOME/vala/raw/{{ .Major }}.{{ .Minor }}/NEWS" 21 | 22 | return v.language.Enabled() 23 | } 24 | -------------------------------------------------------------------------------- /src/segments/vala_test.go: -------------------------------------------------------------------------------- 1 | package segments 2 | 3 | import ( 4 | "fmt" 5 | "testing" 6 | 7 | "github.com/stretchr/testify/assert" 8 | ) 9 | 10 | func TestVala(t *testing.T) { 11 | cases := []struct { 12 | Case string 13 | ExpectedString string 14 | Version string 15 | }{ 16 | { 17 | Case: "vala 0.48.17", 18 | ExpectedString: "0.48.17", 19 | Version: "Vala 0.48.17", 20 | }, 21 | } 22 | for _, tc := range cases { 23 | params := &mockedLanguageParams{ 24 | cmd: "vala", 25 | versionParam: "--version", 26 | versionOutput: tc.Version, 27 | extension: "*.vala", 28 | } 29 | env, props := getMockedLanguageEnv(params) 30 | v := &Vala{} 31 | v.Init(props, env) 32 | assert.True(t, v.Enabled(), fmt.Sprintf("Failed in case: %s", tc.Case)) 33 | assert.Equal(t, tc.ExpectedString, renderTemplate(env, v.Template(), v), fmt.Sprintf("Failed in case: %s", tc.Case)) 34 | } 35 | } 36 | -------------------------------------------------------------------------------- /src/segments/winreg.go: -------------------------------------------------------------------------------- 1 | package segments 2 | 3 | import ( 4 | "github.com/jandedobbeleer/oh-my-posh/src/properties" 5 | "github.com/jandedobbeleer/oh-my-posh/src/runtime" 6 | ) 7 | 8 | type WindowsRegistry struct { 9 | base 10 | 11 | Value string 12 | } 13 | 14 | const ( 15 | // full path to the key; if ends in \, gets "(Default)" key in that path 16 | RegistryPath properties.Property = "path" 17 | // Fallback is the text to display if the key is not found 18 | Fallback properties.Property = "fallback" 19 | ) 20 | 21 | func (wr *WindowsRegistry) Template() string { 22 | return " {{ .Value }} " 23 | } 24 | 25 | func (wr *WindowsRegistry) Enabled() bool { 26 | if wr.env.GOOS() != runtime.WINDOWS { 27 | return false 28 | } 29 | 30 | registryPath := wr.props.GetString(RegistryPath, "") 31 | wr.Value = wr.props.GetString(Fallback, "") 32 | 33 | regValue, err := wr.env.WindowsRegistryKeyValue(registryPath) 34 | if err == nil { 35 | wr.Value = regValue.String 36 | return true 37 | } 38 | if len(wr.Value) > 0 { 39 | // we have fallback value 40 | return true 41 | } 42 | return false 43 | } 44 | -------------------------------------------------------------------------------- /src/segments/xmake.go: -------------------------------------------------------------------------------- 1 | package segments 2 | 3 | type XMake struct { 4 | language 5 | } 6 | 7 | func (x *XMake) Template() string { 8 | return languageTemplate 9 | } 10 | 11 | func (x *XMake) Enabled() bool { 12 | x.extensions = []string{"xmake.lua"} 13 | x.commands = []*cmd{ 14 | { 15 | executable: "xmake", 16 | args: []string{"--version"}, 17 | regex: `xmake v(?P((?P[0-9]+).(?P[0-9]+).(?P[0-9]+)))`, 18 | }, 19 | } 20 | 21 | return x.language.Enabled() 22 | } 23 | -------------------------------------------------------------------------------- /src/segments/xmake_test.go: -------------------------------------------------------------------------------- 1 | package segments 2 | 3 | import ( 4 | "fmt" 5 | "testing" 6 | 7 | "github.com/stretchr/testify/assert" 8 | ) 9 | 10 | func TestXMake(t *testing.T) { 11 | cases := []struct { 12 | Case string 13 | ExpectedString string 14 | Version string 15 | }{ 16 | {Case: "XMake 2.7.2-dev", ExpectedString: "2.7.2", Version: "xmake v2.7.2+dev.605b8e3e0"}, 17 | } 18 | for _, tc := range cases { 19 | params := &mockedLanguageParams{ 20 | cmd: "xmake", 21 | versionParam: "--version", 22 | versionOutput: tc.Version, 23 | extension: "xmake.lua", 24 | } 25 | env, props := getMockedLanguageEnv(params) 26 | x := &XMake{} 27 | x.Init(props, env) 28 | assert.True(t, x.Enabled(), fmt.Sprintf("Failed in case: %s", tc.Case)) 29 | assert.Equal(t, tc.ExpectedString, renderTemplate(env, x.Template(), x), fmt.Sprintf("Failed in case: %s", tc.Case)) 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /src/segments/yarn.go: -------------------------------------------------------------------------------- 1 | package segments 2 | 3 | type Yarn struct { 4 | language 5 | } 6 | 7 | func (n *Yarn) Template() string { 8 | return " \U000F011B {{.Full}} " 9 | } 10 | 11 | func (n *Yarn) Enabled() bool { 12 | n.extensions = []string{"package.json", "yarn.lock"} 13 | n.commands = []*cmd{ 14 | { 15 | executable: "yarn", 16 | args: []string{"--version"}, 17 | regex: `(?P((?P[0-9]+).(?P[0-9]+).(?P[0-9]+)))`, 18 | }, 19 | } 20 | n.versionURLTemplate = "https://github.com/yarnpkg/berry/releases/tag/v{{ .Full }}" 21 | 22 | return n.language.Enabled() 23 | } 24 | -------------------------------------------------------------------------------- /src/segments/yarn_test.go: -------------------------------------------------------------------------------- 1 | package segments 2 | 3 | import ( 4 | "fmt" 5 | "testing" 6 | 7 | "github.com/alecthomas/assert" 8 | ) 9 | 10 | func TestYarn(t *testing.T) { 11 | cases := []struct { 12 | Case string 13 | ExpectedString string 14 | Version string 15 | }{ 16 | {Case: "1.0.0", ExpectedString: "\U000F011B 1.0.0", Version: "1.0.0"}, 17 | } 18 | for _, tc := range cases { 19 | params := &mockedLanguageParams{ 20 | cmd: "yarn", 21 | versionParam: "--version", 22 | versionOutput: tc.Version, 23 | extension: "package.json", 24 | } 25 | env, props := getMockedLanguageEnv(params) 26 | yarn := &Yarn{} 27 | yarn.Init(props, env) 28 | assert.True(t, yarn.Enabled(), fmt.Sprintf("Failed in case: %s", tc.Case)) 29 | assert.Equal(t, tc.ExpectedString, renderTemplate(env, yarn.Template(), yarn), fmt.Sprintf("Failed in case: %s", tc.Case)) 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /src/segments/zig.go: -------------------------------------------------------------------------------- 1 | package segments 2 | 3 | type Zig struct { 4 | language 5 | } 6 | 7 | func (zig *Zig) Template() string { 8 | return languageTemplate 9 | } 10 | 11 | func (zig *Zig) Enabled() bool { 12 | zig.extensions = []string{"*.zig", "*.zon"} 13 | zig.projectFiles = []string{"build.zig"} 14 | zig.commands = []*cmd{ 15 | { 16 | executable: "zig", 17 | args: []string{"version"}, 18 | regex: `(?P(?P0|[1-9]\d*)\.(?P0|[1-9]\d*)\.(?P0|[1-9]\d*)(?:-(?P(?:0|[1-9]\d*|\d*[a-zA-Z-][0-9a-zA-Z-]*)(?:\.(?:0|[1-9]\d*|\d*[a-zA-Z-][0-9a-zA-Z-]*))*))?(?:\+(?P[0-9a-zA-Z-]+(?:\.[0-9a-zA-Z-]+)*))?)`, //nolint:lll 19 | }, 20 | } 21 | 22 | zig.versionURLTemplate = "https://ziglang.org/download/{{ .Major }}.{{ .Minor }}.{{ .Patch }}/release-notes.html" 23 | 24 | return zig.language.Enabled() 25 | } 26 | 27 | func (zig *Zig) InProjectDir() bool { 28 | return zig.projectRoot != nil 29 | } 30 | -------------------------------------------------------------------------------- /src/shell/cmd.go: -------------------------------------------------------------------------------- 1 | package shell 2 | 3 | import ( 4 | _ "embed" 5 | "strings" 6 | ) 7 | 8 | //go:embed scripts/omp.lua 9 | var cmdInit string 10 | 11 | func (f Feature) Cmd() Code { 12 | switch f { 13 | case Transient: 14 | return "transient_enabled = true" 15 | case RPrompt: 16 | return "rprompt_enabled = true" 17 | case FTCSMarks: 18 | return "ftcs_marks_enabled = true" 19 | case Tooltips: 20 | return "enable_tooltips()" 21 | case Upgrade: 22 | return `os.execute(string.format('"%s" upgrade', omp_executable))` 23 | case Notice: 24 | return `os.execute(string.format('"%s" notice', omp_executable))` 25 | case PromptMark, PoshGit, Azure, LineError, Jobs, CursorPositioning, Async: 26 | fallthrough 27 | default: 28 | return "" 29 | } 30 | } 31 | 32 | func escapeLuaStr(str string) string { 33 | if len(str) == 0 { 34 | return str 35 | } 36 | // We only replace a minimal set of special characters with corresponding escape sequences, without adding surrounding quotes. 37 | // That way the result can be later quoted with either single or double quotes in a Lua script. 38 | return strings.NewReplacer( 39 | `\`, `\\`, 40 | "'", `\'`, 41 | `"`, `\"`, 42 | "\n", `\n`, 43 | "\r", `\r`, 44 | ).Replace(str) 45 | } 46 | -------------------------------------------------------------------------------- /src/shell/cmd_test.go: -------------------------------------------------------------------------------- 1 | package shell 2 | 3 | import ( 4 | "fmt" 5 | "testing" 6 | 7 | "github.com/stretchr/testify/assert" 8 | ) 9 | 10 | func TestCmdFeatures(t *testing.T) { 11 | got := allFeatures.Lines(CMD).String("// these are the features") 12 | 13 | want := `// these are the features 14 | enable_tooltips() 15 | transient_enabled = true 16 | ftcs_marks_enabled = true 17 | os.execute(string.format('"%s" upgrade', omp_executable)) 18 | os.execute(string.format('"%s" notice', omp_executable)) 19 | rprompt_enabled = true` 20 | 21 | assert.Equal(t, want, got) 22 | } 23 | 24 | func TestEscapeLuaStr(t *testing.T) { 25 | tests := []struct { 26 | str string 27 | expected string 28 | }{ 29 | {str: "", expected: ""}, 30 | {str: `/tmp/"omp's dir"/oh-my-posh`, expected: `/tmp/\"omp\'s dir\"/oh-my-posh`}, 31 | {str: `C:/tmp\omp's dir/oh-my-posh.exe`, expected: `C:/tmp\\omp\'s dir/oh-my-posh.exe`}, 32 | } 33 | for _, tc := range tests { 34 | assert.Equal(t, tc.expected, escapeLuaStr(tc.str), fmt.Sprintf("escapeLuaStr: %s", tc.str)) 35 | } 36 | } 37 | -------------------------------------------------------------------------------- /src/shell/code.go: -------------------------------------------------------------------------------- 1 | package shell 2 | 3 | import "strings" 4 | 5 | type Code string 6 | 7 | const ( 8 | unixFTCSMarks Code = "_omp_ftcs_marks=1" 9 | unixCursorPositioning Code = "_omp_cursor_positioning=1" 10 | unixUpgrade Code = `"$_omp_executable" upgrade` 11 | unixNotice Code = `"$_omp_executable" notice` 12 | ) 13 | 14 | func (c Code) Indent(spaces int) Code { 15 | return Code(strings.Repeat(" ", spaces) + string(c)) 16 | } 17 | 18 | type Lines []Code 19 | 20 | func (l Lines) String(script string) string { 21 | var builder strings.Builder 22 | 23 | builder.WriteString(script) 24 | builder.WriteString("\n") 25 | 26 | for i, line := range l { 27 | builder.WriteString(string(line)) 28 | 29 | // add newline if not last line 30 | if i < len(l)-1 { 31 | builder.WriteString("\n") 32 | } 33 | } 34 | 35 | return builder.String() 36 | } 37 | -------------------------------------------------------------------------------- /src/shell/constants.go: -------------------------------------------------------------------------------- 1 | package shell 2 | 3 | const ( 4 | ZSH = "zsh" 5 | BASH = "bash" 6 | PWSH = "pwsh" 7 | FISH = "fish" 8 | PWSH5 = "powershell" 9 | CMD = "cmd" 10 | NU = "nu" 11 | GENERIC = "shell" 12 | ELVISH = "elvish" 13 | XONSH = "xonsh" 14 | ) 15 | -------------------------------------------------------------------------------- /src/shell/elvish.go: -------------------------------------------------------------------------------- 1 | package shell 2 | 3 | import ( 4 | _ "embed" 5 | ) 6 | 7 | //go:embed scripts/omp.elv 8 | var elvishInit string 9 | 10 | func (f Feature) Elvish() Code { 11 | switch f { 12 | case Upgrade: 13 | return "$_omp_executable upgrade" 14 | case Notice: 15 | return "$_omp_executable notice" 16 | case PromptMark, RPrompt, PoshGit, Azure, LineError, Jobs, CursorPositioning, Tooltips, Transient, FTCSMarks, Async: 17 | fallthrough 18 | default: 19 | return "" 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /src/shell/elvish_test.go: -------------------------------------------------------------------------------- 1 | package shell 2 | 3 | import ( 4 | "testing" 5 | 6 | "github.com/stretchr/testify/assert" 7 | ) 8 | 9 | func TestElvishFeatures(t *testing.T) { 10 | got := allFeatures.Lines(ELVISH).String("// these are the features") 11 | 12 | want := `// these are the features 13 | $_omp_executable upgrade 14 | $_omp_executable notice` 15 | 16 | assert.Equal(t, want, got) 17 | } 18 | -------------------------------------------------------------------------------- /src/shell/features.go: -------------------------------------------------------------------------------- 1 | package shell 2 | 3 | import ( 4 | "strconv" 5 | "strings" 6 | ) 7 | 8 | type Feature byte 9 | 10 | const ( 11 | Jobs Feature = iota 12 | Azure 13 | PoshGit 14 | LineError 15 | Tooltips 16 | Transient 17 | FTCSMarks 18 | Upgrade 19 | Notice 20 | PromptMark 21 | RPrompt 22 | CursorPositioning 23 | Async 24 | ) 25 | 26 | type Features []Feature 27 | 28 | func (f Features) Lines(shell string) Lines { 29 | var lines Lines 30 | 31 | for _, feature := range f { 32 | var code Code 33 | 34 | switch shell { 35 | case PWSH, PWSH5: 36 | code = feature.Pwsh() 37 | case ZSH: 38 | code = feature.Zsh() 39 | case BASH: 40 | code = feature.Bash() 41 | case ELVISH: 42 | code = feature.Elvish() 43 | case FISH: 44 | code = feature.Fish() 45 | case CMD: 46 | code = feature.Cmd() 47 | case NU: 48 | code = feature.Nu() 49 | case XONSH: 50 | code = feature.Xonsh() 51 | } 52 | 53 | if len(code) > 0 { 54 | lines = append(lines, code) 55 | } 56 | } 57 | 58 | return lines 59 | } 60 | 61 | func (f Features) Hash() string { 62 | var sb strings.Builder 63 | 64 | for _, feature := range f { 65 | sb.WriteString(strconv.Itoa(int(feature))) 66 | } 67 | 68 | return sb.String() 69 | } 70 | -------------------------------------------------------------------------------- /src/shell/fish.go: -------------------------------------------------------------------------------- 1 | package shell 2 | 3 | import ( 4 | _ "embed" 5 | "fmt" 6 | "strings" 7 | ) 8 | 9 | //go:embed scripts/omp.fish 10 | var fishInit string 11 | 12 | func (f Feature) Fish() Code { 13 | switch f { 14 | case Transient: 15 | return "set --global _omp_transient_prompt 1" 16 | case FTCSMarks: 17 | return "set --global _omp_ftcs_marks 1" 18 | case PromptMark: 19 | return "set --global _omp_prompt_mark 1" 20 | case Tooltips: 21 | return "enable_poshtooltips" 22 | case Upgrade: 23 | return unixUpgrade 24 | case Notice: 25 | return unixNotice 26 | case RPrompt, PoshGit, Azure, LineError, Jobs, CursorPositioning, Async: 27 | fallthrough 28 | default: 29 | return "" 30 | } 31 | } 32 | 33 | func quoteFishStr(str string) string { 34 | if len(str) == 0 { 35 | return "''" 36 | } 37 | 38 | return fmt.Sprintf("'%s'", strings.NewReplacer(`\`, `\\`, "'", `\'`).Replace(str)) 39 | } 40 | -------------------------------------------------------------------------------- /src/shell/fish_test.go: -------------------------------------------------------------------------------- 1 | package shell 2 | 3 | import ( 4 | "fmt" 5 | "testing" 6 | 7 | "github.com/stretchr/testify/assert" 8 | ) 9 | 10 | func TestFishFeatures(t *testing.T) { 11 | got := allFeatures.Lines(FISH).String("// these are the features") 12 | 13 | want := `// these are the features 14 | enable_poshtooltips 15 | set --global _omp_transient_prompt 1 16 | set --global _omp_ftcs_marks 1 17 | "$_omp_executable" upgrade 18 | "$_omp_executable" notice 19 | set --global _omp_prompt_mark 1` 20 | 21 | assert.Equal(t, want, got) 22 | } 23 | 24 | func TestQuoteFishStr(t *testing.T) { 25 | tests := []struct { 26 | str string 27 | expected string 28 | }{ 29 | {str: "", expected: "''"}, 30 | {str: `/tmp/"omp's dir"/oh-my-posh`, expected: `'/tmp/"omp\'s dir"/oh-my-posh'`}, 31 | {str: `C:/tmp\omp's dir/oh-my-posh.exe`, expected: `'C:/tmp\\omp\'s dir/oh-my-posh.exe'`}, 32 | } 33 | for _, tc := range tests { 34 | assert.Equal(t, tc.expected, quoteFishStr(tc.str), fmt.Sprintf("quoteFishStr: %s", tc.str)) 35 | } 36 | } 37 | -------------------------------------------------------------------------------- /src/shell/nu.go: -------------------------------------------------------------------------------- 1 | package shell 2 | 3 | import ( 4 | _ "embed" 5 | "fmt" 6 | "strings" 7 | ) 8 | 9 | //go:embed scripts/omp.nu 10 | var nuInit string 11 | 12 | func (f Feature) Nu() Code { 13 | switch f { 14 | case Transient: 15 | return `$env.TRANSIENT_PROMPT_COMMAND = {|| _omp_get_prompt transient }` 16 | case Upgrade: 17 | return "^$_omp_executable upgrade" 18 | case Notice: 19 | return "^$_omp_executable notice" 20 | case PromptMark, RPrompt, PoshGit, Azure, LineError, Jobs, Tooltips, FTCSMarks, CursorPositioning, Async: 21 | fallthrough 22 | default: 23 | return "" 24 | } 25 | } 26 | 27 | func quoteNuStr(str string) string { 28 | if len(str) == 0 { 29 | return "''" 30 | } 31 | 32 | return fmt.Sprintf(`"%s"`, strings.NewReplacer(`\`, `\\`, `"`, `\"`).Replace(str)) 33 | } 34 | -------------------------------------------------------------------------------- /src/shell/nu_test.go: -------------------------------------------------------------------------------- 1 | package shell 2 | 3 | import ( 4 | "fmt" 5 | "testing" 6 | 7 | "github.com/stretchr/testify/assert" 8 | ) 9 | 10 | func TestNuFeatures(t *testing.T) { 11 | got := allFeatures.Lines(NU).String("// these are the features") 12 | 13 | want := `// these are the features 14 | $env.TRANSIENT_PROMPT_COMMAND = {|| _omp_get_prompt transient } 15 | ^$_omp_executable upgrade 16 | ^$_omp_executable notice` 17 | 18 | assert.Equal(t, want, got) 19 | } 20 | 21 | func TestQuoteNuStr(t *testing.T) { 22 | tests := []struct { 23 | str string 24 | expected string 25 | }{ 26 | {str: "", expected: "''"}, 27 | {str: `/tmp/"omp's dir"/oh-my-posh`, expected: `"/tmp/\"omp's dir\"/oh-my-posh"`}, 28 | {str: `C:/tmp\omp's dir/oh-my-posh.exe`, expected: `"C:/tmp\\omp's dir/oh-my-posh.exe"`}, 29 | } 30 | for _, tc := range tests { 31 | assert.Equal(t, tc.expected, quoteNuStr(tc.str), fmt.Sprintf("quoteNuStr: %s", tc.str)) 32 | } 33 | } 34 | -------------------------------------------------------------------------------- /src/shell/pwsh.go: -------------------------------------------------------------------------------- 1 | package shell 2 | 3 | import ( 4 | _ "embed" 5 | "fmt" 6 | "strings" 7 | ) 8 | 9 | //go:embed scripts/omp.ps1 10 | var pwshInit string 11 | 12 | func (f Feature) Pwsh() Code { 13 | switch f { 14 | case Tooltips: 15 | return "Enable-PoshTooltips" 16 | case LineError: 17 | return "Enable-PoshLineError" 18 | case Transient: 19 | return "Enable-PoshTransientPrompt" 20 | case Jobs: 21 | return "$global:_ompJobCount = $true" 22 | case Azure: 23 | return "$global:_ompAzure = $true" 24 | case PoshGit: 25 | return "$global:_ompPoshGit = $true" 26 | case FTCSMarks: 27 | return "$global:_ompFTCSMarks = $true" 28 | case Upgrade: 29 | return "& $global:_ompExecutable upgrade" 30 | case Notice: 31 | return "& $global:_ompExecutable notice" 32 | case PromptMark, RPrompt, CursorPositioning, Async: 33 | fallthrough 34 | default: 35 | return "" 36 | } 37 | } 38 | 39 | func quotePwshOrElvishStr(str string) string { 40 | if len(str) == 0 { 41 | return "''" 42 | } 43 | 44 | return fmt.Sprintf("'%s'", strings.ReplaceAll(str, "'", "''")) 45 | } 46 | -------------------------------------------------------------------------------- /src/shell/pwsh_test.go: -------------------------------------------------------------------------------- 1 | package shell 2 | 3 | import ( 4 | "fmt" 5 | "testing" 6 | 7 | "github.com/stretchr/testify/assert" 8 | ) 9 | 10 | var allFeatures = Features{Tooltips, LineError, Transient, Jobs, Azure, PoshGit, FTCSMarks, Upgrade, Notice, PromptMark, RPrompt, CursorPositioning} 11 | 12 | func TestPwshFeatures(t *testing.T) { 13 | got := allFeatures.Lines(PWSH).String("") 14 | 15 | want := ` 16 | Enable-PoshTooltips 17 | Enable-PoshLineError 18 | Enable-PoshTransientPrompt 19 | $global:_ompJobCount = $true 20 | $global:_ompAzure = $true 21 | $global:_ompPoshGit = $true 22 | $global:_ompFTCSMarks = $true 23 | & $global:_ompExecutable upgrade 24 | & $global:_ompExecutable notice` 25 | 26 | assert.Equal(t, want, got) 27 | } 28 | 29 | func TestQuotePwshOrElvishStr(t *testing.T) { 30 | tests := []struct { 31 | str string 32 | expected string 33 | }{ 34 | {str: "", expected: "''"}, 35 | {str: `/tmp/"omp's dir"/oh-my-posh`, expected: `'/tmp/"omp''s dir"/oh-my-posh'`}, 36 | {str: `C:/tmp\omp's dir/oh-my-posh.exe`, expected: `'C:/tmp\omp''s dir/oh-my-posh.exe'`}, 37 | } 38 | for _, tc := range tests { 39 | assert.Equal(t, tc.expected, quotePwshOrElvishStr(tc.str), fmt.Sprintf("quotePwshOrElvishStr: %s", tc.str)) 40 | } 41 | } 42 | -------------------------------------------------------------------------------- /src/shell/xonsh.go: -------------------------------------------------------------------------------- 1 | package shell 2 | 3 | import ( 4 | _ "embed" 5 | "fmt" 6 | "strings" 7 | ) 8 | 9 | //go:embed scripts/omp.xsh 10 | var xonshInit string 11 | 12 | func (f Feature) Xonsh() Code { 13 | switch f { 14 | case Upgrade: 15 | return "@(_omp_executable) upgrade" 16 | case Notice: 17 | return "@(_omp_executable) notice" 18 | case PromptMark, RPrompt, PoshGit, Azure, LineError, Jobs, Tooltips, Transient, CursorPositioning, FTCSMarks, Async: 19 | fallthrough 20 | default: 21 | return "" 22 | } 23 | } 24 | 25 | func quotePythonStr(str string) string { 26 | if len(str) == 0 { 27 | return "''" 28 | } 29 | 30 | return fmt.Sprintf("'%s'", strings.NewReplacer( 31 | "'", `'"'"'`, 32 | `\`, `\\`, 33 | "\n", `\n`, 34 | ).Replace(str)) 35 | } 36 | -------------------------------------------------------------------------------- /src/shell/xonsh_test.go: -------------------------------------------------------------------------------- 1 | package shell 2 | 3 | import ( 4 | "fmt" 5 | "testing" 6 | 7 | "github.com/stretchr/testify/assert" 8 | ) 9 | 10 | func TestXonshFeatures(t *testing.T) { 11 | got := allFeatures.Lines(XONSH).String("// these are the features") 12 | 13 | want := `// these are the features 14 | @(_omp_executable) upgrade 15 | @(_omp_executable) notice` 16 | 17 | assert.Equal(t, want, got) 18 | } 19 | 20 | func TestQuotePythonStr(t *testing.T) { 21 | tests := []struct { 22 | str string 23 | expected string 24 | }{ 25 | {str: "", expected: "''"}, 26 | {str: `/tmp/"omp's dir"/oh-my-posh`, expected: `'/tmp/"omp'"'"'s dir"/oh-my-posh'`}, 27 | {str: `C:/tmp\omp's dir/oh-my-posh.exe`, expected: `'C:/tmp\\omp'"'"'s dir/oh-my-posh.exe'`}, 28 | } 29 | for _, tc := range tests { 30 | assert.Equal(t, tc.expected, quotePythonStr(tc.str), fmt.Sprintf("quotePythonStr: %s", tc.str)) 31 | } 32 | } 33 | -------------------------------------------------------------------------------- /src/shell/zsh.go: -------------------------------------------------------------------------------- 1 | package shell 2 | 3 | import ( 4 | _ "embed" 5 | ) 6 | 7 | //go:embed scripts/omp.zsh 8 | var zshInit string 9 | 10 | func (f Feature) Zsh() Code { 11 | switch f { 12 | case CursorPositioning: 13 | return unixCursorPositioning 14 | case Tooltips: 15 | return "enable_poshtooltips" 16 | case Transient: 17 | return "_omp_create_widget zle-line-init _omp_zle-line-init" 18 | case FTCSMarks: 19 | return unixFTCSMarks 20 | case Upgrade: 21 | return unixUpgrade 22 | case Notice: 23 | return unixNotice 24 | case PromptMark, RPrompt, PoshGit, Azure, LineError, Jobs, Async: 25 | fallthrough 26 | default: 27 | return "" 28 | } 29 | } 30 | -------------------------------------------------------------------------------- /src/shell/zsh_test.go: -------------------------------------------------------------------------------- 1 | package shell 2 | 3 | import ( 4 | "testing" 5 | 6 | "github.com/stretchr/testify/assert" 7 | ) 8 | 9 | func TestZshFeatures(t *testing.T) { 10 | got := allFeatures.Lines(ZSH).String("// these are the features") 11 | 12 | want := `// these are the features 13 | enable_poshtooltips 14 | _omp_create_widget zle-line-init _omp_zle-line-init 15 | _omp_ftcs_marks=1 16 | "$_omp_executable" upgrade 17 | "$_omp_executable" notice 18 | _omp_cursor_positioning=1` 19 | 20 | assert.Equal(t, want, got) 21 | } 22 | -------------------------------------------------------------------------------- /src/template/compare.go: -------------------------------------------------------------------------------- 1 | package template 2 | 3 | import ( 4 | "errors" 5 | "strconv" 6 | ) 7 | 8 | func toIntOrZero(e any) int { 9 | if value, err := toInt(e); err == nil { 10 | return value 11 | } 12 | 13 | return 0 14 | } 15 | 16 | func toInt(integer any) (int, error) { 17 | switch seconds := integer.(type) { 18 | default: 19 | return 0, errors.New("invalid integer type") 20 | case string: 21 | return strconv.Atoi(seconds) 22 | case int: 23 | return seconds, nil 24 | case int64: 25 | return int(seconds), nil 26 | case uint64: 27 | return int(seconds), nil 28 | case float64: 29 | return int(seconds), nil 30 | } 31 | } 32 | 33 | func toFloat64(e any) float64 { 34 | if val, OK := e.(float64); OK { 35 | return val 36 | } 37 | if val, OK := e.(int); OK { 38 | return float64(val) 39 | } 40 | if val, OK := e.(int64); OK { 41 | return float64(val) 42 | } 43 | return 0 44 | } 45 | 46 | func gt(e1, e2 any) bool { 47 | if val, OK := e1.(int); OK { 48 | return val > toIntOrZero(e2) 49 | } 50 | if val, OK := e1.(int64); OK { 51 | return val > int64(toIntOrZero(e2)) 52 | } 53 | if val, OK := e1.(float64); OK { 54 | return val > toFloat64(e2) 55 | } 56 | return false 57 | } 58 | 59 | func lt(e1, e2 any) bool { 60 | return gt(e2, e1) 61 | } 62 | -------------------------------------------------------------------------------- /src/template/files.go: -------------------------------------------------------------------------------- 1 | package template 2 | 3 | import ( 4 | "os" 5 | "os/exec" 6 | "path/filepath" 7 | 8 | "github.com/jandedobbeleer/oh-my-posh/src/log" 9 | ) 10 | 11 | func glob(pattern string) (bool, error) { 12 | matches, err := filepath.Glob(pattern) 13 | if err != nil { 14 | return false, err 15 | } 16 | return len(matches) > 0, nil 17 | } 18 | 19 | func readFile(path string) string { 20 | content, _ := os.ReadFile(path) 21 | return string(content) 22 | } 23 | 24 | func stat(path string) string { 25 | fullPath, err := exec.LookPath(path) 26 | if err != nil { 27 | log.Error(err) 28 | return "" 29 | } 30 | 31 | return fullPath 32 | } 33 | -------------------------------------------------------------------------------- /src/template/files_test.go: -------------------------------------------------------------------------------- 1 | package template 2 | 3 | import ( 4 | "testing" 5 | 6 | "github.com/jandedobbeleer/oh-my-posh/src/cache" 7 | "github.com/jandedobbeleer/oh-my-posh/src/runtime/mock" 8 | 9 | "github.com/stretchr/testify/assert" 10 | ) 11 | 12 | func TestGlob(t *testing.T) { 13 | cases := []struct { 14 | Case string 15 | Expected string 16 | Template string 17 | ShouldError bool 18 | }{ 19 | {Case: "valid glob", Expected: "OK", Template: `{{ if glob "*.go" }}OK{{ else }}NOK{{ end }}`}, 20 | {Case: "invalid glob", Expected: "NOK", Template: `{{ if glob "package.json" }}OK{{ else }}NOK{{ end }}`}, 21 | {Case: "multiple glob", Expected: "NOK", Template: `{{ if or (glob "package.json") (glob "node_modules") }}OK{{ else }}NOK{{ end }}`}, 22 | } 23 | 24 | env := &mock.Environment{} 25 | env.On("Shell").Return("foo") 26 | 27 | Cache = new(cache.Template) 28 | Init(env, nil, nil) 29 | 30 | for _, tc := range cases { 31 | tmpl := &Text{ 32 | Template: tc.Template, 33 | Context: nil, 34 | } 35 | 36 | text, err := tmpl.Render() 37 | if tc.ShouldError { 38 | assert.Error(t, err) 39 | continue 40 | } 41 | 42 | assert.Equal(t, tc.Expected, text, tc.Case) 43 | } 44 | } 45 | -------------------------------------------------------------------------------- /src/template/func_map.go: -------------------------------------------------------------------------------- 1 | package template 2 | 3 | import ( 4 | "path/filepath" 5 | "text/template" 6 | 7 | "github.com/Masterminds/sprig/v3" 8 | ) 9 | 10 | func funcMap() template.FuncMap { 11 | funcMap := map[string]any{ 12 | "secondsRound": secondsRound, 13 | "url": url, 14 | "path": filePath, 15 | "glob": glob, 16 | "matchP": matchP, 17 | "findP": findP, 18 | "replaceP": replaceP, 19 | "gt": gt, 20 | "lt": lt, 21 | "random": random, 22 | "reason": GetReasonFromStatus, 23 | "hresult": hresult, 24 | "trunc": trunc, 25 | "truncE": truncE, 26 | "readFile": readFile, 27 | "stat": stat, 28 | "dir": filepath.Dir, 29 | "base": filepath.Base, 30 | } 31 | 32 | for key, fun := range sprig.TxtFuncMap() { 33 | if _, ok := funcMap[key]; !ok { 34 | funcMap[key] = fun 35 | } 36 | } 37 | 38 | return template.FuncMap(funcMap) 39 | } 40 | -------------------------------------------------------------------------------- /src/template/init.go: -------------------------------------------------------------------------------- 1 | package template 2 | 3 | import ( 4 | "sync" 5 | 6 | "github.com/jandedobbeleer/oh-my-posh/src/maps" 7 | "github.com/jandedobbeleer/oh-my-posh/src/runtime" 8 | ) 9 | 10 | const ( 11 | // Errors to show when the template handling fails 12 | InvalidTemplate = "invalid template text" 13 | IncorrectTemplate = "unable to create text based on template" 14 | 15 | globalRef = ".$" 16 | 17 | elvish = "elvish" 18 | xonsh = "xonsh" 19 | ) 20 | 21 | var ( 22 | shell string 23 | env runtime.Environment 24 | knownFields sync.Map 25 | ) 26 | 27 | func Init(environment runtime.Environment, vars maps.Simple, aliases *maps.Config) { 28 | env = environment 29 | shell = env.Shell() 30 | knownFields = sync.Map{} 31 | 32 | renderPool = sync.Pool{ 33 | New: func() any { 34 | return newTextPoolObject() 35 | }, 36 | } 37 | 38 | if Cache != nil { 39 | return 40 | } 41 | 42 | loadCache(vars, aliases) 43 | } 44 | -------------------------------------------------------------------------------- /src/template/link.go: -------------------------------------------------------------------------------- 1 | package template 2 | 3 | import ( 4 | "fmt" 5 | link "net/url" 6 | "slices" 7 | ) 8 | 9 | // url builds an hyperlink if url is not empty, otherwise returns the text only 10 | func url(text, url string) (string, error) { 11 | unsupported := []string{elvish, xonsh} 12 | if slices.Contains(unsupported, shell) { 13 | return text, nil 14 | } 15 | 16 | if url == "" { 17 | return text, nil 18 | } 19 | _, err := link.ParseRequestURI(url) 20 | if err != nil { 21 | return "", err 22 | } 23 | return fmt.Sprintf("%s%s", url, text), nil 24 | } 25 | 26 | func filePath(text, path string) (string, error) { 27 | unsupported := []string{elvish, xonsh} 28 | if slices.Contains(unsupported, shell) { 29 | return text, nil 30 | } 31 | 32 | return fmt.Sprintf("file:%s%s", path, text), nil 33 | } 34 | -------------------------------------------------------------------------------- /src/template/numbers.go: -------------------------------------------------------------------------------- 1 | package template 2 | 3 | import "fmt" 4 | 5 | func hresult(number int) string { 6 | return fmt.Sprintf("0x%04X", uint32(number)) 7 | } 8 | -------------------------------------------------------------------------------- /src/template/numbers_test.go: -------------------------------------------------------------------------------- 1 | package template 2 | 3 | import ( 4 | "testing" 5 | 6 | "github.com/stretchr/testify/assert" 7 | ) 8 | 9 | func TestHResult(t *testing.T) { 10 | cases := []struct { 11 | Case string 12 | Expected string 13 | Template string 14 | ShouldError bool 15 | }{ 16 | {Case: "Windows exit code", Expected: "0x8A150014", Template: `{{ hresult -1978335212 }}`}, 17 | {Case: "Not a number", Template: `{{ hresult "no number" }}`, ShouldError: true}, 18 | } 19 | 20 | for _, tc := range cases { 21 | tmpl := &Text{ 22 | Template: tc.Template, 23 | Context: nil, 24 | } 25 | 26 | text, err := tmpl.Render() 27 | if tc.ShouldError { 28 | assert.Error(t, err) 29 | continue 30 | } 31 | 32 | assert.Equal(t, tc.Expected, text, tc.Case) 33 | } 34 | } 35 | -------------------------------------------------------------------------------- /src/template/random.go: -------------------------------------------------------------------------------- 1 | package template 2 | 3 | import ( 4 | "errors" 5 | "fmt" 6 | "math/rand" 7 | "reflect" 8 | ) 9 | 10 | func random(list interface{}) (string, error) { 11 | v := reflect.ValueOf(list) 12 | 13 | if v.Kind() != reflect.Slice && v.Kind() != reflect.Array { 14 | return "", errors.New("input must be a slice or array") 15 | } 16 | 17 | if v.Len() == 0 { 18 | return "", errors.New("input slice or array is empty") 19 | } 20 | 21 | return fmt.Sprintf("%v", v.Index(rand.Intn(v.Len()))), nil 22 | } 23 | -------------------------------------------------------------------------------- /src/template/regex.go: -------------------------------------------------------------------------------- 1 | package template 2 | 3 | import "github.com/jandedobbeleer/oh-my-posh/src/regex" 4 | 5 | func matchP(pattern, text string) bool { 6 | return regex.MatchString(pattern, text) 7 | } 8 | 9 | func replaceP(pattern, text, replaceText string) string { 10 | return regex.ReplaceAllString(pattern, text, replaceText) 11 | } 12 | 13 | func findP(pattern, text string, index int) string { 14 | match, _ := regex.FindStringMatch(pattern, text, index) 15 | return match 16 | } 17 | -------------------------------------------------------------------------------- /src/template/round.go: -------------------------------------------------------------------------------- 1 | package template 2 | 3 | import ( 4 | "strconv" 5 | "strings" 6 | ) 7 | 8 | func secondsRound(seconds any) string { 9 | s, err := toInt(seconds) 10 | if err != nil { 11 | return err.Error() 12 | } 13 | if s == 0 { 14 | return "0s" 15 | } 16 | neg := s < 0 17 | if neg { 18 | s = -s 19 | } 20 | 21 | var ( 22 | second = 1 23 | minute = 60 24 | hour = 3600 25 | day = 86400 26 | month = 2629800 27 | year = 31560000 28 | ) 29 | var builder strings.Builder 30 | writePart := func(unit int, name string) { 31 | if s >= unit { 32 | builder.WriteString(" ") 33 | builder.WriteString(strconv.Itoa(s / unit)) 34 | builder.WriteString(name) 35 | s %= unit 36 | } 37 | } 38 | writePart(year, "y") 39 | writePart(month, "mo") 40 | writePart(day, "d") 41 | writePart(hour, "h") 42 | writePart(minute, "m") 43 | writePart(second, "s") 44 | return strings.Trim(builder.String(), " ") 45 | } 46 | -------------------------------------------------------------------------------- /src/template/strings.go: -------------------------------------------------------------------------------- 1 | package template 2 | 3 | import "unicode/utf8" 4 | 5 | func trunc(length any, s string) string { 6 | c, err := toInt(length) 7 | if err != nil { 8 | panic(err) 9 | } 10 | 11 | runes := []rune(s) 12 | if len(runes) <= c { 13 | return s 14 | } 15 | 16 | if c < 0 { 17 | return string(runes[len(runes)+c:]) 18 | } 19 | 20 | return string(runes[0:c]) 21 | } 22 | 23 | func truncE(length any, s string) string { 24 | c, err := toInt(length) 25 | if err != nil { 26 | panic(err) 27 | } 28 | 29 | truncateSymbol := "…" 30 | 31 | if c == 0 { 32 | return truncateSymbol 33 | } 34 | 35 | lenTruncateSymbol := utf8.RuneCountInString(truncateSymbol) 36 | if c < 0 { 37 | lenTruncateSymbol *= -1 38 | } 39 | c -= lenTruncateSymbol 40 | 41 | runes := []rune(s) 42 | if len(runes) <= c { 43 | return s 44 | } 45 | 46 | if c < 0 { 47 | return truncateSymbol + string(runes[len(runes)+c:]) 48 | } 49 | 50 | return string(runes[0:c]) + truncateSymbol 51 | } 52 | -------------------------------------------------------------------------------- /src/terminal/iterm.go: -------------------------------------------------------------------------------- 1 | package terminal 2 | 3 | import ( 4 | "fmt" 5 | "slices" 6 | "strings" 7 | 8 | "github.com/jandedobbeleer/oh-my-posh/src/shell" 9 | ) 10 | 11 | type iTermFeature string 12 | 13 | const ( 14 | PromptMark iTermFeature = "prompt_mark" 15 | CurrentDir iTermFeature = "current_dir" 16 | RemoteHost iTermFeature = "remote_host" 17 | ) 18 | 19 | type ITermFeatures []iTermFeature 20 | 21 | func (f ITermFeatures) Contains(feature iTermFeature) bool { 22 | for _, item := range f { 23 | if item == feature { 24 | return true 25 | } 26 | } 27 | 28 | return false 29 | } 30 | 31 | func RenderItermFeatures(features ITermFeatures, sh, pwd, user, host string) string { 32 | supportedShells := []string{shell.BASH, shell.ZSH} 33 | 34 | var result strings.Builder 35 | for _, feature := range features { 36 | switch feature { 37 | case PromptMark: 38 | if !slices.Contains(supportedShells, sh) { 39 | continue 40 | } 41 | 42 | result.WriteString(formats.ITermPromptMark) 43 | case CurrentDir: 44 | result.WriteString(fmt.Sprintf(formats.ITermCurrentDir, pwd)) 45 | case RemoteHost: 46 | result.WriteString(fmt.Sprintf(formats.ITermRemoteHost, user, host)) 47 | } 48 | } 49 | 50 | return result.String() 51 | } 52 | -------------------------------------------------------------------------------- /src/test/azureProfile.json: -------------------------------------------------------------------------------- 1 | { 2 | "installationId": "8d2919ac-a393-11eb-9100-acde48001122", 3 | "subscriptions": [ 4 | { 5 | "id": "g51ds77-a393-11eb-9100-acde48001122", 6 | "name": "AzureCli", 7 | "state": "Powerfull", 8 | "user": { "name": "Melinda", "type": "user" }, 9 | "isDefault": true, 10 | "tenantId": "6js98d-a393-11eb-9100-acde48001122", 11 | "environmentName": "AzureCliCloud", 12 | "homeTenantId": "8d934305-ac9f-46fe-b0e7-50fd32ad2acf", 13 | "managedByTenants": [] 14 | } 15 | ] 16 | } 17 | -------------------------------------------------------------------------------- /src/test/go.work: -------------------------------------------------------------------------------- 1 | go 1.21 -------------------------------------------------------------------------------- /src/test/invalid.nuspec: -------------------------------------------------------------------------------- 1 | {} 2 | -------------------------------------------------------------------------------- /src/test/kubectl.yml: -------------------------------------------------------------------------------- 1 | apiVersion: v1 2 | clusters: null 3 | contexts: 4 | - context: 5 | cluster: '%s' 6 | user: '%s' 7 | namespace: '%s' 8 | name: jan 9 | current-context: '%s' 10 | kind: Config 11 | preferences: {} 12 | users: null 13 | -------------------------------------------------------------------------------- /src/test/signing/checksums.txt: -------------------------------------------------------------------------------- 1 | 4ad2e70db23f6c0441df61a07370d352081e93fab32867e797d6dfa3f45814ce posh-windows-arm64.exe 2 | -------------------------------------------------------------------------------- /src/test/signing/checksums.txt.invalid.sig: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/JanDeDobbeleer/oh-my-posh/84cb3af96245c6f890e4273a3f6c2eaee692b04a/src/test/signing/checksums.txt.invalid.sig -------------------------------------------------------------------------------- /src/test/signing/checksums.txt.sig: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/JanDeDobbeleer/oh-my-posh/84cb3af96245c6f890e4273a3f6c2eaee692b04a/src/test/signing/checksums.txt.sig -------------------------------------------------------------------------------- /src/test/terraform.tfstate: -------------------------------------------------------------------------------- 1 | { 2 | "version": 4, 3 | "terraform_version": "0.12.24", 4 | "serial": 22, 5 | "lineage": "6cc278b4-0426-a833-9920-356a6635038c" 6 | } 7 | -------------------------------------------------------------------------------- /src/test/versions.tf: -------------------------------------------------------------------------------- 1 | terraform { 2 | required_version = ">= 1.0.10" 3 | } 4 | -------------------------------------------------------------------------------- /src/upgrade/cli_test.go: -------------------------------------------------------------------------------- 1 | package upgrade 2 | 3 | import ( 4 | "testing" 5 | 6 | "github.com/stretchr/testify/assert" 7 | ) 8 | 9 | func TestIsMajorUpgrade(t *testing.T) { 10 | cases := []struct { 11 | Case string 12 | CurrentVersion string 13 | LatestVersion string 14 | Expected bool 15 | }{ 16 | {Case: "Same version", Expected: false, CurrentVersion: "v3.0.0", LatestVersion: "v3.0.0"}, 17 | {Case: "Breaking change", Expected: true, CurrentVersion: "v3.0.0", LatestVersion: "v4.0.0"}, 18 | {Case: "Empty version, mostly development build", Expected: false, LatestVersion: "v4.0.0"}, 19 | } 20 | 21 | for _, tc := range cases { 22 | canUpgrade := IsMajorUpgrade(tc.CurrentVersion, tc.LatestVersion) 23 | assert.Equal(t, tc.Expected, canUpgrade, tc.Case) 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /src/upgrade/install_noop.go: -------------------------------------------------------------------------------- 1 | //go:build !windows 2 | 3 | package upgrade 4 | 5 | func hideFile(_ string) error { 6 | return nil 7 | } 8 | -------------------------------------------------------------------------------- /src/upgrade/install_windows.go: -------------------------------------------------------------------------------- 1 | package upgrade 2 | 3 | import ( 4 | "syscall" 5 | "unsafe" 6 | ) 7 | 8 | func hideFile(path string) error { 9 | kernel32 := syscall.NewLazyDLL("kernel32.dll") 10 | setFileAttributes := kernel32.NewProc("SetFileAttributesW") 11 | 12 | ptr, err := syscall.UTF16PtrFromString(path) 13 | if err != nil { 14 | return err 15 | } 16 | 17 | r1, _, err := setFileAttributes.Call(uintptr(unsafe.Pointer(ptr)), 2) 18 | 19 | if r1 == 0 { 20 | return err 21 | } 22 | 23 | return nil 24 | } 25 | -------------------------------------------------------------------------------- /src/upgrade/notice_test.go: -------------------------------------------------------------------------------- 1 | package upgrade 2 | 3 | import ( 4 | "os" 5 | "testing" 6 | 7 | "github.com/jandedobbeleer/oh-my-posh/src/build" 8 | cache_ "github.com/jandedobbeleer/oh-my-posh/src/cache/mock" 9 | "github.com/stretchr/testify/assert" 10 | testify_ "github.com/stretchr/testify/mock" 11 | ) 12 | 13 | func TestCanUpgrade(t *testing.T) { 14 | ugc := &Config{} 15 | latest, _ := ugc.FetchLatest() 16 | 17 | cases := []struct { 18 | Case string 19 | CurrentVersion string 20 | Installer string 21 | Expected bool 22 | Cache bool 23 | }{ 24 | {Case: "Up to date", CurrentVersion: latest}, 25 | {Case: "Outdated Linux", Expected: true, CurrentVersion: "3.0.0"}, 26 | {Case: "Outdated Darwin", Expected: true, CurrentVersion: "3.0.0"}, 27 | {Case: "Cached", Cache: true, CurrentVersion: latest}, 28 | {Case: "Windows Store", Installer: "ws"}, 29 | } 30 | 31 | for _, tc := range cases { 32 | build.Version = tc.CurrentVersion 33 | c := &cache_.Cache{} 34 | c.On("Get", CACHEKEY).Return("", tc.Cache) 35 | c.On("Set", testify_.Anything, testify_.Anything, testify_.Anything) 36 | ugc.Cache = c 37 | 38 | if len(tc.Installer) > 0 { 39 | os.Setenv("POSH_INSTALLER", tc.Installer) 40 | } 41 | 42 | _, canUpgrade := ugc.Notice() 43 | assert.Equal(t, tc.Expected, canUpgrade, tc.Case) 44 | 45 | os.Setenv("POSH_INSTALLER", "") 46 | } 47 | } 48 | -------------------------------------------------------------------------------- /src/upgrade/public_key.pem: -------------------------------------------------------------------------------- 1 | -----BEGIN PUBLIC KEY----- 2 | MCowBQYDK2VwAyEA98lHhNau5x0JtjSuwiWLuC2yKO6NA6/0bH2gE8tAq4c= 3 | -----END PUBLIC KEY----- 4 | -------------------------------------------------------------------------------- /src/upgrade/verify_test.go: -------------------------------------------------------------------------------- 1 | package upgrade 2 | 3 | import ( 4 | "os" 5 | "testing" 6 | 7 | "github.com/stretchr/testify/assert" 8 | ) 9 | 10 | func TestVerify(t *testing.T) { 11 | checksum, err := os.ReadFile("../test/signing/checksums.txt") 12 | assert.NoError(t, err) 13 | 14 | signature, err := os.ReadFile("../test/signing/checksums.txt.sig") 15 | assert.NoError(t, err) 16 | 17 | OK := validateSignature(checksum, signature) 18 | assert.True(t, OK) 19 | } 20 | 21 | func TestVerifyFail(t *testing.T) { 22 | checksum, err := os.ReadFile("../test/signing/checksums.txt") 23 | assert.NoError(t, err) 24 | 25 | signature, err := os.ReadFile("../test/signing/checksums.txt.invalid.sig") 26 | assert.NoError(t, err) 27 | 28 | OK := validateSignature(checksum, signature) 29 | assert.False(t, OK) 30 | } 31 | -------------------------------------------------------------------------------- /src/winres/icon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/JanDeDobbeleer/oh-my-posh/84cb3af96245c6f890e4273a3f6c2eaee692b04a/src/winres/icon.png -------------------------------------------------------------------------------- /src/winres/icon16.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/JanDeDobbeleer/oh-my-posh/84cb3af96245c6f890e4273a3f6c2eaee692b04a/src/winres/icon16.png -------------------------------------------------------------------------------- /src/winres/icon32.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/JanDeDobbeleer/oh-my-posh/84cb3af96245c6f890e4273a3f6c2eaee692b04a/src/winres/icon32.png -------------------------------------------------------------------------------- /src/winres/icon48.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/JanDeDobbeleer/oh-my-posh/84cb3af96245c6f890e4273a3f6c2eaee692b04a/src/winres/icon48.png -------------------------------------------------------------------------------- /src/winres/icon64.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/JanDeDobbeleer/oh-my-posh/84cb3af96245c6f890e4273a3f6c2eaee692b04a/src/winres/icon64.png -------------------------------------------------------------------------------- /src/winres/winres.json: -------------------------------------------------------------------------------- 1 | { 2 | "RT_GROUP_ICON": { 3 | "APP": { 4 | "0000": [ 5 | "icon.png", 6 | "icon64.png", 7 | "icon48.png", 8 | "icon32.png", 9 | "icon16.png" 10 | ] 11 | } 12 | }, 13 | "RT_MANIFEST": { 14 | "#1": { 15 | "0409": { 16 | "description": "A prompt theme engine for any shell.", 17 | "minimum-os": "win8", 18 | "execution-level": "as invoker", 19 | "long-path-aware": true, 20 | "segment-heap": true 21 | } 22 | } 23 | }, 24 | "RT_VERSION": { 25 | "#1": { 26 | "0000": { 27 | "fixed": { 28 | "file_version": "0.0.0.0", 29 | "product_version": "0.0.0.0" 30 | }, 31 | "info": { 32 | "0409": { 33 | "CompanyName": "github.com/jandedobbeleer", 34 | "FileDescription": "A prompt theme engine for any shell.", 35 | "InternalName": "oh-my-posh", 36 | "LegalCopyright": "© Jan De Dobbeleer. Licensed under MIT.", 37 | "OriginalFilename": "oh-my-posh.exe", 38 | "ProductName": "Oh My Posh" 39 | } 40 | } 41 | } 42 | } 43 | } 44 | } 45 | -------------------------------------------------------------------------------- /themes/avit.omp.json: -------------------------------------------------------------------------------- 1 | { 2 | "$schema": "https://raw.githubusercontent.com/JanDeDobbeleer/oh-my-posh/main/themes/schema.json", 3 | "blocks": [ 4 | { 5 | "alignment": "left", 6 | "segments": [ 7 | { 8 | "foreground": "#ffffff", 9 | "properties": { 10 | "style": "full" 11 | }, 12 | "style": "plain", 13 | "template": "{{ .Path }} ", 14 | "type": "path" 15 | }, 16 | { 17 | "foreground": "#C2C206", 18 | "style": "plain", 19 | "template": "{{ .HEAD }} ", 20 | "type": "git" 21 | }, 22 | { 23 | "foreground": "#B5B50D", 24 | "style": "plain", 25 | "template": " \uf0e7 ", 26 | "type": "root" 27 | }, 28 | { 29 | "foreground": "#C94A16", 30 | "style": "plain", 31 | "template": "x{{ reason .Code }} ", 32 | "type": "status" 33 | } 34 | ], 35 | "type": "prompt" 36 | }, 37 | { 38 | "alignment": "left", 39 | "newline": true, 40 | "segments": [ 41 | { 42 | "foreground": "#007ACC", 43 | "style": "plain", 44 | "template": "\ue602", 45 | "type": "text" 46 | } 47 | ], 48 | "type": "prompt" 49 | } 50 | ], 51 | "final_space": true, 52 | "version": 3 53 | } 54 | -------------------------------------------------------------------------------- /themes/json.omp.json: -------------------------------------------------------------------------------- 1 | { 2 | "$schema": "https://raw.githubusercontent.com/JanDeDobbeleer/oh-my-posh/main/themes/schema.json", 3 | "blocks": [ 4 | { 5 | "alignment": "left", 6 | "segments": [ 7 | { 8 | "foreground": "#F1FA8C", 9 | "properties": { 10 | "style": "folder" 11 | }, 12 | "style": "diamond", 13 | "template": "<#42E66C>\ueaf7 {{ .Path }}", 14 | "type": "path" 15 | }, 16 | { 17 | "foreground": "#43CCEA", 18 | "properties": { 19 | "branch_icon": "" 20 | }, 21 | "style": "diamond", 22 | "template": "<#ffffff> \u26A1 {{ .HEAD }}", 23 | "type": "git" 24 | }, 25 | { 26 | "foreground": "#ff0000", 27 | "style": "diamond", 28 | "template": "<#ff0000> \u25C9", 29 | "type": "text" 30 | } 31 | ], 32 | "type": "prompt" 33 | }, 34 | { 35 | "alignment": "left", 36 | "newline": true, 37 | "segments": [ 38 | { 39 | "foreground": "#FFFFFF", 40 | "style": "plain", 41 | "template": ">", 42 | "type": "text" 43 | } 44 | ], 45 | "type": "prompt" 46 | } 47 | ], 48 | "final_space": true, 49 | "version": 3 50 | } 51 | -------------------------------------------------------------------------------- /themes/lambda.omp.json: -------------------------------------------------------------------------------- 1 | { 2 | "$schema": "https://raw.githubusercontent.com/JanDeDobbeleer/oh-my-posh/main/themes/schema.json", 3 | "blocks": [ 4 | { 5 | "alignment": "left", 6 | "segments": [ 7 | { 8 | "foreground": "#F5F5F5", 9 | "style": "plain", 10 | "template": "\ue7ad ", 11 | "type": "text" 12 | }, 13 | { 14 | "foreground": "#B80101", 15 | "properties": { 16 | "style": "agnoster" 17 | }, 18 | "style": "plain", 19 | "template": " {{ .Path }} ", 20 | "type": "path" 21 | }, 22 | { 23 | "foreground": "#B80101", 24 | "style": "plain", 25 | "template": " <#F5F5F5>git:{{ .HEAD }} ", 26 | "type": "git" 27 | } 28 | ], 29 | "type": "prompt" 30 | } 31 | ], 32 | "version": 3 33 | } 34 | -------------------------------------------------------------------------------- /themes/powerlevel10k_lean.omp.json: -------------------------------------------------------------------------------- 1 | { 2 | "$schema": "https://raw.githubusercontent.com/JanDeDobbeleer/oh-my-posh/main/themes/schema.json", 3 | "blocks": [ 4 | { 5 | "segments": [ 6 | { 7 | "foreground": "#00C5C7", 8 | "properties": { 9 | "time_format": "15:04:05" 10 | }, 11 | "style": "plain", 12 | "template": " {{ .CurrentDate | date .Format }} ", 13 | "type": "time" 14 | } 15 | ], 16 | "type": "rprompt" 17 | }, 18 | { 19 | "alignment": "left", 20 | "segments": [ 21 | { 22 | "foreground": "#77E4F7", 23 | "properties": { 24 | "style": "full" 25 | }, 26 | "style": "plain", 27 | "template": "{{ .Path }} ", 28 | "type": "path" 29 | }, 30 | { 31 | "foreground": "#FFE700", 32 | "style": "plain", 33 | "template": "{{ .HEAD }} ", 34 | "type": "git" 35 | }, 36 | { 37 | "foreground": "#43D426", 38 | "style": "plain", 39 | "template": "\u276f ", 40 | "type": "text" 41 | } 42 | ], 43 | "type": "prompt" 44 | } 45 | ], 46 | "version": 3 47 | } 48 | -------------------------------------------------------------------------------- /themes/robbyrussell.omp.json: -------------------------------------------------------------------------------- 1 | { 2 | "$schema": "https://raw.githubusercontent.com/JanDeDobbeleer/oh-my-posh/main/themes/schema.json", 3 | "blocks": [ 4 | { 5 | "alignment": "left", 6 | "segments": [ 7 | { 8 | "foreground": "#98C379", 9 | "style": "plain", 10 | "template": "\u279c", 11 | "type": "text" 12 | }, 13 | { 14 | "foreground": "#56B6C2", 15 | "properties": { 16 | "style": "folder" 17 | }, 18 | "style": "plain", 19 | "template": " {{ .Path }}", 20 | "type": "path" 21 | }, 22 | { 23 | "foreground": "#D0666F", 24 | "properties": { 25 | "branch_icon": "" 26 | }, 27 | "style": "plain", 28 | "template": " <#5FAAE8>git:({{ .HEAD }}<#5FAAE8>)", 29 | "type": "git" 30 | }, 31 | { 32 | "foreground": "#BF616A", 33 | "style": "plain", 34 | "template": " \u2717", 35 | "type": "status" 36 | } 37 | ], 38 | "type": "prompt" 39 | } 40 | ], 41 | "final_space": true, 42 | "version": 3 43 | } 44 | -------------------------------------------------------------------------------- /themes/zash.omp.json: -------------------------------------------------------------------------------- 1 | { 2 | "$schema": "https://raw.githubusercontent.com/JanDeDobbeleer/oh-my-posh/main/themes/schema.json", 3 | "blocks": [ 4 | { 5 | "alignment": "left", 6 | "segments": [ 7 | { 8 | "foreground": "#E36464", 9 | "style": "plain", 10 | "template": "@{{ .UserName }} ", 11 | "type": "session" 12 | }, 13 | { 14 | "foreground": "#62ED8B", 15 | "style": "plain", 16 | "template": "\u279c", 17 | "type": "text" 18 | }, 19 | { 20 | "foreground": "#56B6C2", 21 | "properties": { 22 | "style": "folder" 23 | }, 24 | "style": "plain", 25 | "template": " {{ .Path }}", 26 | "type": "path" 27 | }, 28 | { 29 | "foreground": "#D4AAFC", 30 | "properties": { 31 | "branch_icon": "" 32 | }, 33 | "style": "plain", 34 | "template": " <#DDB15F>git({{ .HEAD }}<#DDB15F>)", 35 | "type": "git" 36 | }, 37 | { 38 | "foreground": "#DCB977", 39 | "style": "plain", 40 | "template": " \uf119", 41 | "type": "status" 42 | } 43 | ], 44 | "type": "prompt" 45 | } 46 | ], 47 | "final_space": true, 48 | "version": 3 49 | } 50 | -------------------------------------------------------------------------------- /website/.gitignore: -------------------------------------------------------------------------------- 1 | # Dependencies 2 | /node_modules 3 | 4 | # Production 5 | /build 6 | 7 | # Generated files 8 | .docusaurus 9 | .cache-loader 10 | 11 | # Misc 12 | .DS_Store 13 | .env.local 14 | .env.development.local 15 | .env.test.local 16 | .env.production.local 17 | 18 | npm-debug.log* 19 | yarn-debug.log* 20 | yarn-error.log* 21 | -------------------------------------------------------------------------------- /website/README.md: -------------------------------------------------------------------------------- 1 | # Website 2 | 3 | This website is built using [Docusaurus](https://docusaurus.io/), a modern static website generator. 4 | 5 | ## Installation 6 | 7 | ```shell 8 | $ npm install 9 | ``` 10 | 11 | ## Local Development 12 | 13 | ```shell 14 | $ npm run start 15 | ``` 16 | 17 | This command starts a local development server and open up a browser window. 18 | Most changes are reflected live without having to restart the server. 19 | -------------------------------------------------------------------------------- /website/api/.funcignore: -------------------------------------------------------------------------------- 1 | *.js.map 2 | *.ts 3 | .git* 4 | .vscode 5 | local.settings.json 6 | test 7 | tsconfig.json -------------------------------------------------------------------------------- /website/api/auth/function.json: -------------------------------------------------------------------------------- 1 | { 2 | "bindings": [ 3 | { 4 | "authLevel": "anonymous", 5 | "type": "httpTrigger", 6 | "direction": "in", 7 | "name": "req", 8 | "methods": [ 9 | "get", 10 | "post" 11 | ] 12 | }, 13 | { 14 | "type": "http", 15 | "direction": "out", 16 | "name": "res" 17 | } 18 | ] 19 | } 20 | -------------------------------------------------------------------------------- /website/api/host.json: -------------------------------------------------------------------------------- 1 | { 2 | "version": "2.0", 3 | "logging": { 4 | "applicationInsights": { 5 | "samplingSettings": { 6 | "isEnabled": true, 7 | "excludedTypes": "Request" 8 | } 9 | } 10 | }, 11 | "extensionBundle": { 12 | "id": "Microsoft.Azure.Functions.ExtensionBundle", 13 | "version": "[2.*, 3.0.0)" 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /website/api/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "api", 3 | "version": "1.0.0", 4 | "description": "", 5 | "scripts": { 6 | "start": "func start", 7 | "test": "echo \"No tests yet...\"" 8 | }, 9 | "dependencies": { 10 | "axios": "^1.7.9" 11 | } 12 | } 13 | -------------------------------------------------------------------------------- /website/api/proxies.json: -------------------------------------------------------------------------------- 1 | { 2 | "$schema": "http://json.schemastore.org/proxies", 3 | "proxies": { 4 | "callback": { 5 | "debug": true, 6 | "matchCondition": { 7 | "methods": ["GET", "POST"], 8 | "route": "/api/auth" 9 | }, 10 | "backendUri": "https://localhost/api/auth", 11 | "requestOverrides": { 12 | "backend.request.querystring.code": "", 13 | "backend.request.querystring._code": "{request.querystring.code}" 14 | } 15 | } 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /website/api/refresh/function.json: -------------------------------------------------------------------------------- 1 | { 2 | "bindings": [ 3 | { 4 | "authLevel": "anonymous", 5 | "type": "httpTrigger", 6 | "direction": "in", 7 | "name": "req", 8 | "methods": [ 9 | "get", 10 | "post" 11 | ] 12 | }, 13 | { 14 | "type": "http", 15 | "direction": "out", 16 | "name": "res" 17 | } 18 | ] 19 | } 20 | -------------------------------------------------------------------------------- /website/api/shared/strava.js: -------------------------------------------------------------------------------- 1 | const axios = require('axios'); 2 | 3 | async function getToken(code) { 4 | const params = { 5 | client_id: process.env['STRAVA_CLIENT_ID'], 6 | client_secret: process.env['STRAVA_CLIENT_SECRET'], 7 | code: code, 8 | grant_type: 'authorization_code', 9 | }; 10 | const resp = await axios.post('https://www.strava.com/api/v3/oauth/token', null, { params: params }); 11 | 12 | return { 13 | access_token: resp.data.access_token, 14 | refresh_token: resp.data.refresh_token, 15 | expires_in: resp.data.expires_in 16 | }; 17 | } 18 | 19 | async function refreshToken(refresh_token) { 20 | const params = { 21 | client_id: process.env['STRAVA_CLIENT_ID'], 22 | client_secret: process.env['STRAVA_CLIENT_SECRET'], 23 | refresh_token: refresh_token, 24 | grant_type: 'refresh_token', 25 | }; 26 | const resp = await axios.post('https://www.strava.com/api/v3/oauth/token', null, { params: params }); 27 | 28 | return { 29 | access_token: resp.data.access_token, 30 | refresh_token: resp.data.refresh_token, 31 | expires_in: resp.data.expires_in 32 | }; 33 | } 34 | 35 | module.exports = { 36 | getToken: getToken, 37 | refreshToken: refreshToken, 38 | } 39 | -------------------------------------------------------------------------------- /website/docs/auth.mdx: -------------------------------------------------------------------------------- 1 | --- 2 | id: auth 3 | title: Segment Authentication 4 | sidebar_label: Authentication 5 | --- 6 | 7 | import CodeBlock from '@site/src/components/Auth.js'; 8 | 9 | 10 | -------------------------------------------------------------------------------- /website/docs/configuration/title.mdx: -------------------------------------------------------------------------------- 1 | --- 2 | id: title 3 | title: Console title 4 | sidebar_label: Console title 5 | --- 6 | 7 | import Config from "@site/src/components/Config.js"; 8 | 9 | 15 | 16 | ### Console Title Template 17 | 18 | The following examples illustrate possible contents for `console_title_template`, provided 19 | the current working directory is `/usr/home/omp` and the shell is `zsh`. 20 | 21 | To learn more about templates and their possibilities, have a look at the [template][templates] section. 22 | 23 | ```json 24 | { 25 | "console_title_template": "{{.Folder}}{{if .Root}} :: root{{end}} :: {{.Shell}}", 26 | // outputs: 27 | // when root == false: omp :: zsh 28 | // when root == true: omp :: root :: zsh 29 | "console_title_template": "{{.Folder}}", // outputs: omp 30 | "console_title_template": "{{.Shell}} in {{.PWD}}", // outputs: zsh in /usr/home/omp 31 | "console_title_template": "{{.UserName}}@{{.HostName}} {{.Shell}} in {{.PWD}}", // outputs: MyUser@MyMachine zsh in /usr/home/omp 32 | "console_title_template": "{{.Env.USERDOMAIN}} {{.Shell}} in {{.PWD}}" // outputs: MyCompany zsh in /usr/home/omp 33 | } 34 | ``` 35 | 36 | [sprig]: https://masterminds.github.io/sprig/ 37 | [templates]: /docs/configuration/templates 38 | -------------------------------------------------------------------------------- /website/docs/installation/homebrew.mdx: -------------------------------------------------------------------------------- 1 | A [Homebrew][brew] Formula and Cask (macOS only) are available for easy installation. 2 | 3 | ```bash 4 | brew install jandedobbeleer/oh-my-posh/oh-my-posh 5 | ``` 6 | 7 | This installs two things: 8 | 9 | - `oh-my-posh` - Executable, added to `$(brew --prefix)/bin` 10 | - `themes` - The latest Oh My Posh [themes][themes] 11 | 12 | If you want to use a predefined theme, you can find them in `$(brew --prefix oh-my-posh)/themes`, referencing them as such 13 | will always keep them compatible when updating Oh My Posh. 14 | 15 | ## Updating 16 | 17 | ```bash 18 | brew update && brew upgrade oh-my-posh 19 | ``` 20 | 21 | :::tip 22 | In case you see [strange behaviour][strange] in your shell, reload it after upgrading Oh My Posh. 23 | For example in zsh: 24 | 25 | ```bash 26 | brew update && brew upgrade && exec zsh 27 | ``` 28 | ::: 29 | 30 | [brew]: https://brew.sh 31 | [themes]: https://ohmyposh.dev/docs/themes 32 | [strange]: https://github.com/JanDeDobbeleer/oh-my-posh/issues/1287 33 | -------------------------------------------------------------------------------- /website/docs/installation/next.mdx: -------------------------------------------------------------------------------- 1 | ## Next 2 | 3 | Now that Oh My Posh is installed, you can go ahead and configure your terminal and shell to 4 | get the prompt to look exactly like you want. 5 | 6 | - install a [font][fonts] 7 | - configure your terminal/editor to use the installed font 8 | - configure your shell to [use Oh My Posh][prompt] 9 | - (optional) configure a theme or [custom prompt configuration][customize] 10 | 11 | [fonts]: fonts.mdx 12 | [prompt]: prompt.mdx 13 | [customize]: customize.mdx -------------------------------------------------------------------------------- /website/docs/segments/cli/argocd.mdx: -------------------------------------------------------------------------------- 1 | --- 2 | id: argocd 3 | title: ArgoCD Context 4 | sidebar_label: ArgoCD 5 | --- 6 | 7 | ## What 8 | 9 | Display the current ArgoCD context name, user and/or server. 10 | 11 | ## Sample Configuration 12 | 13 | import Config from "@site/src/components/Config.js"; 14 | 15 | 25 | 26 | ## Template ([info][templates]) 27 | 28 | :::note default template 29 | 30 | ```template 31 | {{ .Name }} 32 | ``` 33 | 34 | ### Properties 35 | 36 | | Name | Type | Description | 37 | | --------- | -------- | --------------------------------- | 38 | | `.Name` | `string` | the current context name | 39 | | `.Server` | `string` | the server of the current context | 40 | | `.User` | `string` | the user of the current context | 41 | 42 | [templates]: /docs/configuration/templates 43 | -------------------------------------------------------------------------------- /website/docs/segments/cli/firebase.mdx: -------------------------------------------------------------------------------- 1 | --- 2 | id: firebase 3 | title: Firebase 4 | sidebar_label: Firebase 5 | --- 6 | 7 | ## What 8 | 9 | Display the current active Firebase project. 10 | 11 | This segment leverages the `firebase-tools.json` file generated by the [Firebase CLI][firebase-cli-docs]. 12 | The active files listed there are used to determine the current project against the current directory. 13 | 14 | ## Sample Configuration 15 | 16 | import Config from '@site/src/components/Config.js'; 17 | 18 | 26 | 27 | ## Template ([info][templates]) 28 | 29 | :::note default template 30 | 31 | ```template 32 | {{ .Project }} 33 | ``` 34 | 35 | ::: 36 | 37 | ### Properties 38 | 39 | | Name | Type | Description | 40 | | ---------- | -------- | ------------------------------------------------------------------------ | 41 | | `.Project` | `string` | the currently active project | 42 | 43 | [templates]: /docs/configuration/templates 44 | [firebase-cli-docs]: https://firebase.google.com/docs/cli 45 | -------------------------------------------------------------------------------- /website/docs/segments/cli/gitversion.mdx: -------------------------------------------------------------------------------- 1 | --- 2 | id: gitversion 3 | title: GitVersion 4 | sidebar_label: GitVersion 5 | --- 6 | 7 | ## What 8 | 9 | Display the [GitVersion][gitversion] version. 10 | We _strongly_ recommend using [GitVersion Portable][gitversion-portable] for this. 11 | 12 | :::caution 13 | The GitVersion CLI can be a bit slow, causing the prompt to feel slow. This is why we cache 14 | the value for 30 minutes by default. 15 | ::: 16 | 17 | ## Sample Configuration 18 | 19 | import Config from '@site/src/components/Config.js'; 20 | 21 | 29 | 30 | ## Template ([info][templates]) 31 | 32 | :::note default template 33 | 34 | ```template 35 | {{ .MajorMinorPatch }} 36 | ``` 37 | 38 | ::: 39 | 40 | ### Properties 41 | 42 | You can leverage all variables from the [GitVersion][gitversion] CLI. Have a look at their [documentation][docs] for more information. 43 | 44 | [gitversion]: https://github.com/GitTools/GitVersion 45 | [gitversion-portable]: http://chocolatey.org/packages/GitVersion.Portable 46 | [templates]: /docs/configuration/templates 47 | [docs]: https://gitversion.net/docs/reference/variables 48 | -------------------------------------------------------------------------------- /website/docs/segments/cli/nix-shell.mdx: -------------------------------------------------------------------------------- 1 | --- 2 | id: nix-shell 3 | title: Nix Shell 4 | sidebar_label: Nix Shell 5 | --- 6 | 7 | ## What 8 | 9 | Displays the [nix shell] status if inside a nix-shell environment. 10 | 11 | ## Sample Configuration 12 | 13 | import Config from "@site/src/components/Config.js"; 14 | 15 | 24 | 25 | ## Template ([info][templates]) 26 | 27 | :::note default template 28 | 29 | ```template 30 | via {{ .Type }}-shell" 31 | ``` 32 | 33 | ::: 34 | 35 | ### Properties 36 | 37 | | Name | Type | Description | 38 | | ------- | -------- | ----------------------------------------------------------- | 39 | | `.Type` | `string` | the type of nix shell, can be `pure`, `impure` or `unknown` | 40 | 41 | [nix shell]: https://nixos.org/guides/nix-pills/developing-with-nix-shell.html 42 | [templates]: /docs/configuration/templates 43 | -------------------------------------------------------------------------------- /website/docs/segments/cli/talosctl.mdx: -------------------------------------------------------------------------------- 1 | --- 2 | id: talosctl 3 | title: Talosctl Context 4 | sidebar_label: Talosctl 5 | --- 6 | 7 | ## What 8 | 9 | Displays the currently active Talosctl context name. 10 | 11 | This segment leverages the `.talos/config` file generated by the [Talosctl CLI][talosctl]. 12 | The CLI docs can be found by going to Documentation > Reference > CLI. 13 | For example, [v1.7 CLI][CLI]. 14 | 15 | ## Sample Configuration 16 | 17 | import Config from '@site/src/components/Config.js'; 18 | 19 | 27 | 28 | ## Template ([info][templates]) 29 | 30 | :::note default template 31 | 32 | ```template 33 | {{ .Context }} 34 | ``` 35 | 36 | ::: 37 | 38 | ### Properties 39 | 40 | | Name | Type | Description | 41 | | ------------ | -------- | ------------------------------------- | 42 | | `.Context` | `string` | the current talosctl context | 43 | 44 | 45 | [templates]: /docs/configuration/templates 46 | [talosctl]: https://www.talos.dev/ 47 | [CLI]: https://www.talos.dev/v1.7/reference/cli/ 48 | -------------------------------------------------------------------------------- /website/docs/segments/cloud/azd.mdx: -------------------------------------------------------------------------------- 1 | --- 2 | id: azd 3 | title: Azure Developer CLI 4 | sidebar_label: Azure Dev CLI 5 | --- 6 | 7 | ## What 8 | 9 | Display the currently active environment in the Azure Developer CLI. 10 | 11 | ## Sample Configuration 12 | 13 | import Config from "@site/src/components/Config.js"; 14 | 15 | 25 | 26 | ## Template ([info][templates]) 27 | 28 | :::note default template 29 | 30 | ```template 31 | \uebd8 {{ .DefaultEnvironment }} 32 | ``` 33 | 34 | ::: 35 | 36 | ### Properties 37 | 38 | | Name | Type | Description | 39 | | --------------------- | -------- | ------------------------------------ | 40 | | `.DefaultEnvironment` | `string` | Azure Developer CLI environment name | 41 | | `.Version` | `number` | Config version number | 42 | 43 | [templates]: /docs/configuration/templates 44 | [azd]: https://aka.ms/azd 45 | -------------------------------------------------------------------------------- /website/docs/segments/system/root.mdx: -------------------------------------------------------------------------------- 1 | --- 2 | id: root 3 | title: Root 4 | sidebar_label: Root 5 | --- 6 | 7 | ## What 8 | 9 | Show when the current user is root or when in an elevated shell (Windows). 10 | 11 | ## Sample Configuration 12 | 13 | import Config from '@site/src/components/Config.js'; 14 | 15 | 23 | 24 | ## Template ([info][templates]) 25 | 26 | :::note default template 27 | 28 | ``` template 29 | \uF0E7 30 | ``` 31 | 32 | ::: 33 | 34 | [templates]: /docs/configuration/templates 35 | -------------------------------------------------------------------------------- /website/docs/segments/system/session.mdx: -------------------------------------------------------------------------------- 1 | --- 2 | id: session 3 | title: Session 4 | sidebar_label: Session 5 | --- 6 | 7 | ## What 8 | 9 | Show the current user and host name. 10 | 11 | ## Sample Configuration 12 | 13 | import Config from '@site/src/components/Config.js'; 14 | 15 | 24 | 25 | ## Template ([info][templates]) 26 | 27 | :::note default template 28 | 29 | ```template 30 | {{ if .SSHSession }}\ueba9 {{ end }}{{ .UserName }}@{{ .HostName }} 31 | ``` 32 | 33 | ::: 34 | 35 | ### Properties 36 | 37 | | Name | Type | Description | 38 | | ------------- | --------- | -------------------------------- | 39 | | `.UserName` | `string` | the current user's name | 40 | | `.HostName` | `string` | the current computer's name | 41 | | `.SSHSession` | `boolean` | active SSH session or not | 42 | | `.Root` | `boolean` | are you a root/admin user or not | 43 | 44 | [templates]: /docs/configuration/templates 45 | -------------------------------------------------------------------------------- /website/docs/segments/system/text.mdx: -------------------------------------------------------------------------------- 1 | --- 2 | id: text 3 | title: Text 4 | sidebar_label: Text 5 | --- 6 | 7 | ## What 8 | 9 | Display text. 10 | 11 | ## Sample Configuration 12 | 13 | import Config from '@site/src/components/Config.js'; 14 | 15 | 21 | 22 | ## Template ([info][templates]) 23 | 24 | ### Properties 25 | 26 | Text segments have no special properties. See ([info][templates]) for globally available properties. 27 | 28 | [coloring]: /docs/configuration/colors 29 | [templates]: /docs/configuration/templates 30 | -------------------------------------------------------------------------------- /website/docs/share-theme.md: -------------------------------------------------------------------------------- 1 | --- 2 | id: share 3 | title: Share theme 4 | sidebar_label: 📸 Share theme 5 | --- 6 | 7 | You can export your prompt to an image which you can share online. You have the ability to align 8 | it correctly and add your name for credits too. 9 | 10 | :::caution 11 | Some glyphs aren't rendered correctly, that's not you but the limitations of the renderer. 12 | Depending on your config, you might have to tweak the output a little bit. 13 | ::: 14 | 15 | The oh-my-posh executable has the `config export image` command to export your current theme configuration 16 | to a PNG image file (if no other options are specified this will be the name of the config file, or `prompt.png`). 17 | 18 | ```powershell 19 | oh-my-posh config export image 20 | ``` 21 | 22 | There are a couple of additional flags you can use to tweak the image rendering: 23 | 24 | - `--author`: the name of the creator, added after `ohmyposh.dev` 25 | - `--background-color`: the hex background color to use (e.g. `#222222`) 26 | - `--output`: the file to export to (e.g. `mytheme.png`) 27 | 28 | For all options, and additional examples, use `oh-my-posh config export image --help` 29 | -------------------------------------------------------------------------------- /website/docs/themes.md: -------------------------------------------------------------------------------- 1 | --- 2 | id: themes 3 | title: Themes 4 | sidebar_label: 🎨 Themes 5 | --- 6 | 7 | Oh My Posh comes with many themes included out-of-the-box. Below are some screenshots of the more common themes. 8 | For the full updated list of themes, [view the themes][themes] in Github. 9 | 10 | Once you're ready to swap to a theme, follow the steps described in [🚀 Get started > Customize][installation-customize]. 11 | 12 | Themes with `minimal` in their names do not require a Nerd Font. Read about [🆎Fonts][fonts] for more information. 13 | 14 | [themes]: https://github.com/JanDeDobbeleer/oh-my-posh/tree/main/themes 15 | [fonts]: /docs/installation/fonts 16 | [installation-customize]: /docs/installation/customize 17 | 18 | 19 | -------------------------------------------------------------------------------- /website/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "website", 3 | "version": "0.0.0", 4 | "private": true, 5 | "scripts": { 6 | "start": "cross-env NODE_ENV=development docusaurus start --poll 1000", 7 | "build": "docusaurus build", 8 | "serve": "docusaurus serve", 9 | "themes": "node export_themes.js", 10 | "clear": "docusaurus clear" 11 | }, 12 | "dependencies": { 13 | "@docusaurus/core": "^3.8.0", 14 | "@docusaurus/preset-classic": "^3.8.0", 15 | "@docusaurus/theme-search-algolia": "^3.8.0", 16 | "@iarna/toml": "^2.2.5", 17 | "@mdx-js/react": "^3.1.0", 18 | "classnames": "^2.5.1", 19 | "query-string": "9.2.0", 20 | "react": "^19.1.0", 21 | "react-dom": "^19.1.0", 22 | "react-router": "^7.6.1", 23 | "yaml": "^2.8.0" 24 | }, 25 | "devDependencies": { 26 | "cross-env": "^7.0.3", 27 | "docusaurus-node-polyfills": "^1.0.0" 28 | }, 29 | "browserslist": { 30 | "production": [ 31 | ">0.2%", 32 | "not dead", 33 | "not op_mini all" 34 | ], 35 | "development": [ 36 | "last 1 chrome version", 37 | "last 1 firefox version", 38 | "last 1 safari version" 39 | ] 40 | }, 41 | "engines": { 42 | "npm": "10.1.0", 43 | "node": "20.9.0" 44 | }, 45 | "volta": { 46 | "node": "20.9.0" 47 | } 48 | } 49 | -------------------------------------------------------------------------------- /website/plugins/appinsights/analytics.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright (c) Facebook, Inc. and its affiliates. 3 | * 4 | * This source code is licensed under the MIT license found in the 5 | * LICENSE file in the root directory of this source tree. 6 | */ 7 | 8 | import ExecutionEnvironment from '@docusaurus/ExecutionEnvironment'; 9 | 10 | export default (function () { 11 | if (!ExecutionEnvironment.canUseDOM) { 12 | return null; 13 | } 14 | 15 | return { 16 | onRouteUpdate({ location }) { 17 | window.setTimeout(() => { 18 | if (window.appInsights) { 19 | window.appInsights.trackPageView({name: location.pathname}); 20 | } 21 | }, 0); 22 | }, 23 | }; 24 | })(); 25 | -------------------------------------------------------------------------------- /website/src/pages/privacy.mdx: -------------------------------------------------------------------------------- 1 | --- 2 | title: Privacy 3 | description: the privacy page 4 | hide_table_of_contents: true 5 | --- 6 | 7 | # Privacy 8 | 9 | ## What information does Oh My Posh collect? 10 | 11 | Oh My Posh and its developer take your privacy very seriously. 12 | Beyond the information installer methods provides to developers that covered 13 | provided by the package manager privacy policies, 14 | we use _no third-party analytics or advertising frameworks inside the executable_. 15 | Oh My Posh logs no information on you and has no interest in doing so. 16 | 17 | ## What information does the Oh My Posh website collect? 18 | 19 | The Oh My Posh website uses Application Insights [custom events][ai] to log anonymous usage data. 20 | We do not collect any personally identifiable information. 21 | 22 | Oh My Posh does not collect, transmit, distribute or sell your data. 23 | 24 | [ai]: https://learn.microsoft.com/en-us/azure/azure-monitor/app/api-custom-events-metrics#trackevent 25 | 26 | 27 | 28 | -------------------------------------------------------------------------------- /website/src/pages/styles.module.css: -------------------------------------------------------------------------------- 1 | /* stylelint-disable docusaurus/copyright-header */ 2 | 3 | /** 4 | * CSS files with the .module.css suffix will be treated as CSS modules 5 | * and scoped locally. 6 | */ 7 | 8 | .heroBanner { 9 | padding: 4rem 0; 10 | min-height: 60vh; 11 | text-align: center; 12 | position: relative; 13 | overflow: hidden; 14 | } 15 | 16 | @media screen and (max-width: 966px) { 17 | .heroBanner { 18 | padding: 2rem 0; 19 | } 20 | } 21 | 22 | .buttons { 23 | display: flex; 24 | flex-wrap: wrap; 25 | gap: 1rem 1.5rem; 26 | align-items: center; 27 | justify-content: center; 28 | } 29 | 30 | .features { 31 | display: flex; 32 | align-items: center; 33 | padding: 2rem 0; 34 | width: 100%; 35 | } 36 | 37 | .featureImage { 38 | height: 200px; 39 | width: 200px; 40 | } 41 | 42 | .getStarted { 43 | border-style: solid; 44 | border-width: 1px; 45 | } 46 | -------------------------------------------------------------------------------- /website/static/.nojekyll: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/JanDeDobbeleer/oh-my-posh/84cb3af96245c6f890e4273a3f6c2eaee692b04a/website/static/.nojekyll -------------------------------------------------------------------------------- /website/static/fonts/VictorMono.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/JanDeDobbeleer/oh-my-posh/84cb3af96245c6f890e4273a3f6c2eaee692b04a/website/static/fonts/VictorMono.ttf -------------------------------------------------------------------------------- /website/static/fonts/fonts.zip: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/JanDeDobbeleer/oh-my-posh/84cb3af96245c6f890e4273a3f6c2eaee692b04a/website/static/fonts/fonts.zip -------------------------------------------------------------------------------- /website/static/img/accordeon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/JanDeDobbeleer/oh-my-posh/84cb3af96245c6f890e4273a3f6c2eaee692b04a/website/static/img/accordeon.png -------------------------------------------------------------------------------- /website/static/img/hero.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/JanDeDobbeleer/oh-my-posh/84cb3af96245c6f890e4273a3f6c2eaee692b04a/website/static/img/hero.png -------------------------------------------------------------------------------- /website/static/img/logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/JanDeDobbeleer/oh-my-posh/84cb3af96245c6f890e4273a3f6c2eaee692b04a/website/static/img/logo.png -------------------------------------------------------------------------------- /website/static/img/posh-tooltip.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/JanDeDobbeleer/oh-my-posh/84cb3af96245c6f890e4273a3f6c2eaee692b04a/website/static/img/posh-tooltip.gif -------------------------------------------------------------------------------- /website/static/img/themes/.keep: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/JanDeDobbeleer/oh-my-posh/84cb3af96245c6f890e4273a3f6c2eaee692b04a/website/static/img/themes/.keep -------------------------------------------------------------------------------- /website/static/img/transient-after.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/JanDeDobbeleer/oh-my-posh/84cb3af96245c6f890e4273a3f6c2eaee692b04a/website/static/img/transient-after.gif -------------------------------------------------------------------------------- /website/static/img/transient-before.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/JanDeDobbeleer/oh-my-posh/84cb3af96245c6f890e4273a3f6c2eaee692b04a/website/static/img/transient-before.gif -------------------------------------------------------------------------------- /website/static/img/transient-color.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/JanDeDobbeleer/oh-my-posh/84cb3af96245c6f890e4273a3f6c2eaee692b04a/website/static/img/transient-color.png -------------------------------------------------------------------------------- /website/staticwebapp.config.json: -------------------------------------------------------------------------------- 1 | { 2 | "platform": { 3 | "apiRuntime": "node:20" 4 | } 5 | } 6 | --------------------------------------------------------------------------------