···11+MIT License
22+33+Copyright (c) 2024 Rolv Apneseth
44+55+Permission is hereby granted, free of charge, to any person obtaining a copy
66+of this software and associated documentation files (the "Software"), to deal
77+in the Software without restriction, including without limitation the rights
88+to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
99+copies of the Software, and to permit persons to whom the Software is
1010+furnished to do so, subject to the following conditions:
1111+1212+The above copyright notice and this permission notice shall be included in all
1313+copies or substantial portions of the Software.
1414+1515+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
1616+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
1717+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
1818+AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
1919+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
2020+OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
2121+SOFTWARE.
+116
.config/yazi/plugins/starship.yazi/README.md
···11+# starship.yazi
22+33+Starship prompt plugin for [Yazi](https://github.com/sxyazi/yazi)
44+55+<https://github.com/Rolv-Apneseth/starship.yazi/assets/69486699/f7314687-5cb1-4d66-8d9d-cca960ba6716>
66+77+## Requirements
88+99+- [Yazi](https://github.com/sxyazi/yazi) (v25.4.8+)
1010+- [starship](https://github.com/starship/starship)
1111+1212+## Installation
1313+1414+```bash
1515+ya pkg add Rolv-Apneseth/starship
1616+```
1717+1818+### Manual
1919+2020+```sh
2121+# Linux / MacOS
2222+git clone https://github.com/Rolv-Apneseth/starship.yazi.git ~/.config/yazi/plugins/starship.yazi
2323+# Windows
2424+git clone https://github.com/Rolv-Apneseth/starship.yazi.git %AppData%\yazi\config\plugins\starship.yazi
2525+```
2626+2727+## Usage
2828+2929+Add this to `~/.config/yazi/init.lua`:
3030+3131+```lua
3232+require("starship"):setup()
3333+```
3434+3535+Make sure you have [starship](https://github.com/starship/starship) installed and in your `PATH`.
3636+3737+## Config
3838+3939+Here is an example with all available config options:
4040+4141+```lua
4242+require("starship"):setup({
4343+ -- Hide flags (such as filter, find and search). This can be beneficial for starship themes
4444+ -- which are intended to go across the entire width of the terminal.
4545+ hide_flags = false,
4646+ -- Whether to place flags after the starship prompt. False means the flags will be placed before the prompt.
4747+ flags_after_prompt = true,
4848+ -- Custom starship configuration file to use
4949+ config_file = "~/.config/starship_full.toml", -- Default: nil
5050+ -- Whether to enable support for starship's right prompt (i.e. `starship prompt --right`).
5151+ show_right_prompt = false,
5252+ -- Whether to hide the count widget, in case you want only your right prompt to show up. Only has
5353+ -- an effect when `show_right_prompt = true`
5454+ hide_count = false,
5555+ -- Separator to place between the right prompt and the count widget. Use `count_separator = ""`
5656+ -- to have no space between the widgets.
5757+ count_separator = " ",
5858+})
5959+```
6060+6161+## Extra
6262+6363+If you use a `starship` theme with a background colour, it might look a bit to cramped on just the one line `Yazi` gives the header by default. To fix this, you can add this to your `init.lua`:
6464+6565+<details>
6666+<summary>Click to expand</summary>
6767+6868+```lua
6969+local old_build = Tab.build
7070+7171+Tab.build = function(self, ...)
7272+ local bar = function(c, x, y)
7373+ if x <= 0 or x == self._area.w - 1 then
7474+ return ui.Bar(ui.Bar.TOP):area(ui.Rect.default)
7575+ end
7676+7777+ return ui.Bar(ui.Bar.TOP)
7878+ :area(ui.Rect({
7979+ x = x,
8080+ y = math.max(0, y),
8181+ w = ya.clamp(0, self._area.w - x, 1),
8282+ h = math.min(1, self._area.h),
8383+ }))
8484+ :symbol(c)
8585+ end
8686+8787+ local c = self._chunks
8888+ self._chunks = {
8989+ c[1]:pad(ui.Pad.y(1)),
9090+ c[2]:pad(ui.Pad(1, c[3].w > 0 and 0 or 1, 1, c[1].w > 0 and 0 or 1)),
9191+ c[3]:pad(ui.Pad.y(1)),
9292+ }
9393+9494+ local style = th.mgr.border_style
9595+ self._base = ya.list_merge(self._base or {}, {
9696+ ui.Bar(ui.Bar.RIGHT):area(self._chunks[1]):style(style),
9797+ ui.Bar(ui.Bar.LEFT):area(self._chunks[1]):style(style),
9898+9999+ bar("โฌ", c[1].right - 1, c[1].y),
100100+ bar("โด", c[1].right - 1, c[1].bottom - 1),
101101+ bar("โฌ", c[2].right, c[2].y),
102102+ bar("โด", c[2].right, c[2].bottom - 1),
103103+ })
104104+105105+ old_build(self, ...)
106106+end
107107+```
108108+109109+</details>
110110+111111+> [!NOTE]
112112+> This works by overriding your `Tab.build` function so make sure this is the only place you're doing that in your config. For example, this would be incompatible with the [full-border plugin](https://github.com/yazi-rs/plugins/tree/main/full-border.yazi)
113113+114114+## Thanks
115115+116116+- [sxyazi](https://github.com/sxyazi) for providing the code for this plugin and the demo video [in this comment](https://github.com/sxyazi/yazi/issues/767#issuecomment-1977082834)
+242
.config/yazi/plugins/starship.yazi/main.lua
···11+--- @since 25.4.8
22+33+-- For development
44+--[[ local function notify(message) ]]
55+--[[ ya.notify({ title = "Starship", content = message, timeout = 3 }) ]]
66+--[[ end ]]
77+88+local save = ya.sync(function(st, outputs)
99+ st.output_left = outputs.left
1010+ st.output_right = outputs.right
1111+1212+ -- Support for versions later than v25.5.31 (not yet a full release as of writing this comment)
1313+ local render = ui.render or ya.render
1414+ render()
1515+end)
1616+1717+-- Helper function for accessing the `config_file` state variable
1818+---@return string
1919+local get_config_file = ya.sync(function(st)
2020+ return st.config_file
2121+end)
2222+2323+--- Helper function for accessing `show_right_prompt` state variable
2424+---@return boolean
2525+local should_show_right_prompt = ya.sync(function(st)
2626+ return st.show_right_prompt
2727+end)
2828+2929+return {
3030+ ---User arguments for setup method
3131+ ---@class SetupArgs
3232+ ---@field config_file string Absolute path to a starship config file.
3333+ ---@field hide_flags boolean Whether to hide all flags (such as filter and search). Default value is false.
3434+ ---@field flags_after_prompt boolean Whether to place flags (such as filter and search) after the starship prompt. Default value is true.
3535+ ---@field show_right_prompt boolean Whether to enable starship right prompt support. Default value is false.
3636+ ---@field hide_count boolean Whether to hide the count widget. Only has an effect when show_right_prompt is true. Default value is false.
3737+ ---@field count_separator string Set a custom separator between the count widget and the right prompt. Default value is " ", set to "" for no space.
3838+3939+ --- Setup plugin
4040+ --- @param st table State
4141+ --- @param args SetupArgs|nil
4242+ setup = function(st, args)
4343+ local hide_flags = false
4444+ local flags_after_prompt = true
4545+ local hide_count = false
4646+ local count_separator = " "
4747+4848+ -- Check setup args
4949+ if args ~= nil then
5050+ if args.config_file ~= nil then
5151+ local url = Url(args.config_file)
5252+ if url.is_regular then
5353+ local config_file = args.config_file
5454+5555+ -- Manually replace '~' and '$HOME' at the start of the path with the OS environment variable
5656+ local home = os.getenv("HOME")
5757+ if home then
5858+ home = tostring(home)
5959+ config_file = config_file:gsub("^~", home):gsub("^$HOME", home)
6060+ end
6161+6262+ st.config_file = config_file
6363+ end
6464+ end
6565+6666+ if args.show_right_prompt ~= nil then
6767+ -- Save directly to the plugin state so it can be accessed
6868+ -- read from the entry function
6969+ st.show_right_prompt = args.show_right_prompt
7070+ end
7171+7272+ if args.hide_count ~= nil then
7373+ hide_count = args.hide_count
7474+ end
7575+7676+ if args.count_separator ~= nil then
7777+ count_separator = args.count_separator
7878+ end
7979+8080+ if args.hide_flags ~= nil then
8181+ hide_flags = args.hide_flags
8282+ end
8383+8484+ if args.flags_after_prompt ~= nil then
8585+ flags_after_prompt = args.flags_after_prompt
8686+ end
8787+ end
8888+8989+ -- Replace default left header widget
9090+ Header:children_remove(1, Header.LEFT)
9191+ Header:children_add(function(self)
9292+ if hide_flags or not st.output_left then
9393+ return ui.Line.parse(st.output_left or "")
9494+ end
9595+9696+ -- Split `st.output` at the first line break (or keep as is if none was found)
9797+ local output = st.output_left:match("([^\n]*)\n?") or st.output_left
9898+9999+ local flags = self:flags()
100100+ if flags_after_prompt then
101101+ output = output .. " " .. flags
102102+ else
103103+ output = flags .. " " .. output
104104+ end
105105+106106+ local line = ui.Line.parse(output)
107107+ if line:width() > self._area.w then
108108+ return ""
109109+ end
110110+111111+ return line
112112+ end, 1000, Header.LEFT)
113113+114114+ -- Support for right prompt, if enabled
115115+ if st.show_right_prompt then
116116+ -- Remove the default count widget
117117+ Header:children_remove(1, Header.RIGHT)
118118+ -- Replace with a custom widget combining the right prompt and the count widget
119119+ Header:children_add(function(self)
120120+ if not st.output_right then
121121+ st.output_right = ""
122122+ end
123123+ local output = st.output_right:match("([^\n]*)\n?") or st.output_right
124124+ local line = ui.Line.parse(output)
125125+126126+ -- Custom count widget so that we can measure the combined width
127127+ if not hide_count then
128128+ local yanked = #cx.yanked
129129+ local count, style
130130+ if yanked == 0 then
131131+ count = #self._tab.selected
132132+ style = th.mgr.count_selected
133133+ elseif cx.yanked.is_cut then
134134+ count = yanked
135135+ style = th.mgr.count_cut
136136+ else
137137+ count = yanked
138138+ style = th.mgr.count_copied
139139+ end
140140+141141+ -- Append custom count widget
142142+ if count ~= 0 then
143143+ line = ui.Line({
144144+ line,
145145+ ui.Span(count_separator),
146146+ ui.Span(string.format(" %d ", count)):style(style),
147147+ })
148148+ end
149149+ end
150150+151151+ -- Give precedence to the left header widget(s), hiding this component entirely if
152152+ -- there is no room for both
153153+ local right_width = line:width()
154154+ if self._left_width then
155155+ local max = self._area.w - self._left_width
156156+ if max < right_width then
157157+ return ""
158158+ end
159159+ end
160160+161161+ return line
162162+ end, 1000, Header.RIGHT)
163163+164164+ -- Override the header's redraw method, since we want the left side of the header to have
165165+ -- precedence over the right, unlike the default behaviour of hiding the left side if
166166+ -- there isn't room for both.
167167+ function Header:redraw()
168168+ local left = self:children_redraw(self.LEFT)
169169+ self._left_width = left:width()
170170+171171+ local right = self:children_redraw(self.RIGHT)
172172+173173+ return {
174174+ ui.Line(left):area(self._area),
175175+ ui.Line(right):area(self._area):align(ui.Align.RIGHT),
176176+ }
177177+ end
178178+ end
179179+180180+ -- Pass current working directory and custom config path (if specified) to the plugin's entry point
181181+ ---Callback for subscribers to update the prompt
182182+ local callback = function()
183183+ local cwd = cx.active.current.cwd
184184+ if st.cwd ~= cwd then
185185+ st.cwd = cwd
186186+187187+ -- `ya.emit` as of 25.5.28
188188+ local emit = ya.emit or ya.manager_emit
189189+190190+ emit("plugin", {
191191+ st._id,
192192+ ya.quote(tostring(cwd), true),
193193+ })
194194+ end
195195+ end
196196+197197+ -- Subscribe to events
198198+ ps.sub("cd", callback)
199199+ ps.sub("tab", callback)
200200+ end,
201201+202202+ entry = function(_, job)
203203+ local args = job.args
204204+205205+ -- Setup commands for left and right prompts
206206+ local function base_command()
207207+ return Command("starship")
208208+ :arg("prompt")
209209+ :stdin(Command.INHERIT)
210210+ :cwd(args[1])
211211+ :env("STARSHIP_SHELL", "")
212212+ :env("PWD", args[1])
213213+ end
214214+ local command_left = base_command()
215215+ local command_right = base_command():arg("--right")
216216+217217+ -- Point to custom starship config for both commands
218218+ local config_file = get_config_file()
219219+ if config_file then
220220+ command_left = command_left:env("STARSHIP_CONFIG", config_file)
221221+ command_right = command_right:env("STARSHIP_CONFIG", config_file)
222222+ end
223223+224224+ -- Execute left prompt command and save output
225225+ local outputs = { left = "", right = "" }
226226+ local output_left = command_left:output()
227227+ if output_left then
228228+ outputs.left = output_left.stdout:gsub("^%s+", "")
229229+ end
230230+231231+ -- If support for right prompt is enabled, execute right prompt command and save output
232232+ local show_right_prompt = should_show_right_prompt()
233233+ if show_right_prompt then
234234+ local output_right = command_right:output()
235235+ if output_right then
236236+ outputs.right = output_right.stdout:gsub("^%s+", "")
237237+ end
238238+ end
239239+240240+ save(outputs)
241241+ end,
242242+}