Skip to content

Prompts

maxlandon edited this page Jan 2, 2023 · 8 revisions

The console makes use of oh-my-posh prompt engines for each menu. This allows users to have a cross-platform, versatile and customizable prompt system.

Therefore, library users can:

  • Write custom prompt segments, specific to their application logic and/or to a console menu.
  • Use those segments in a prompt configuration file loaded by default (or not) in their applicaiton.
  • Make use of all the builtin prompt segments offered by Oh-My-Posh.

Table of Contents

Note it is not mandatory to register a custom segment before loading a configuration making use of it. You could as well go the next section of this page, and add this code hereafter. The prompt engine would still work as intended.

Additionally, note that both methods below (1 and Alternative) will result in exactly the same functionning.

1) Implementing

In order to spare users from having to import oh-my-posh (which requires import replace directives in the go.mod), a slightly restrained SegmentWriter interface is declared by the console:

// Segment represents a type able to render itself as a prompt segment string.
// Any number of segments can be registered to the prompt engine, and those
// segments can then be used by declaration in the prompt configuration file.
type Segment interface {
	Enabled() bool
	Template() string
}

This interface is quite simple, but also very powerful. It allows library users to declare an arbitrary structure, and to use its various fields in a template string in order to provide a prompt segment. The following is a very simple example, where we just alias a string, and where we just return the value of it. You could as well return a full template in the Template() string method.

type Module string

func (m Module) Enabled() bool {
	return string(m) != ""
}

func (m Module) Template() string {
	return string(m)
}

// Your application would probably change the value of this
// global variable at some point, and the prompt reflects the change in state.
var module Module = Module("scan/docker/permissions")

Binding through the console

Then, register this segment to the prompt library (available for all menus) is as simple as:

    app := console.New()
    configureReadline(app)
    
    // We could also call this on any menu, it makes no difference.
    menu := app.CurrentMenu().Prompt().AddSegment("module", module)

Important note

Due to the way the console will wrap your provided Segment into a compliant oh-my-posh one, any reference to the segment's struct fields in the template string will need to be prefixed with Segment, such as {{ .Segment.Value1 }} or {{ .Segment.Value2 }}.

This does not apply to the following method.

2) Alternative: implementing oh-my-posh SegmentWriter

For those who would need, for some reason, to access lower-level properties of a prompt segment, they can also satisfy the following -slightly enlarged- prompt segment interface:

package oh-my-posh/src

import (
	"github.com/jandedobbeleer/oh-my-posh/platform"
	"github.com/jandedobbeleer/oh-my-posh/properties"
)

type SegmentWriter interface {
    Enabled() bool
    Template() string
    Init(props properties.Properties, env platform.Environment) {
}

NOTE: due to the structure of the oh-my-posh Go module, you will need to use a replace directive in your application go.mod, probably looking like this (notice the /src leaf, because oh-my-posh declares its go.mod in the src/ directory of its repository):

replace github.com/jandedobbeleer/oh-my-posh v12.34.2+incompatible => github.com/jandedobbeleer/oh-my-posh/src v0.0.0-20221230190448-3559254292e5

Binding directly to oh-my-posh

You can then directly register this segment to the oh-my-posh library, without going through the console:

import (
	"github.com/jandedobbeleer/oh-my-posh/engine"
)

func main() {
    engine.Segments[engine.SegmentType("module")] = func() engine.SegmentWriter { return module }
}

Referencing the segment in a configuration

The following is an extracted snippet of the example application prompt configuration:

    "blocks": [
        {
            "alignment": "left",
            "newline": true,
            "segments": [
                {
                    "style": "plain",
                    "template": " using (<#df2e1c>{{ .Segment }}</>) ",
                    "type": "module"
                }
                ... other segments

Loading a prompt configuration

Now that we have our custom segment, we need to load a custom prompt configuration should we want to, for a given menu. Please see [here] for a complete documentation on how to write this configuration.

One of the big advantages of being able to use the oh-my-posh prompt engine, is that we can now use our module segment in pretty much any block of the prompt (primary/secondary/RPROMPT/tooltip, etc).

Coming back to our newly created menu:

func createMenu(c *console.Console) {
    clientMenu := c.NewMenu("client")

    prompt := clientMenu.Prompt()

    // Add custom segments to the prompt library.
    // (again, can be used by all menu engines)
    prompt.AddSegment("module", module)

    // Load a configuration file on the system,
    // where we actually use our new prompt segment.
    prompt.LoadConfig("prompt.omp.json")
}

References and documentation

  • Here for an introduction to the oh-my-posh prompt concepts.
  • Here for documentation on how to write configurations and segments.
Clone this wiki locally