oh-my-posh/src/segment_language.go
2021-04-17 13:31:38 +02:00

203 lines
5.1 KiB
Go

package main
import (
"errors"
"fmt"
"strings"
)
type loadContext func()
type inContext func() bool
type matchesVersionFile func() bool
type version struct {
full string
major string
minor string
patch string
}
type cmd struct {
executable string
args []string
regex string
version *version
}
func (c *cmd) parse(versionInfo string) error {
values := findNamedRegexMatch(c.regex, versionInfo)
if len(values) == 0 {
return errors.New("cannot parse version string")
}
c.version = &version{}
c.version.full = values["version"]
c.version.major = values["major"]
c.version.minor = values["minor"]
c.version.patch = values["patch"]
return nil
}
func (c *cmd) buildVersionURL(template string) string {
if template == "" {
return c.version.full
}
truncatingSprintf := func(str string, args ...interface{}) (string, error) {
n := strings.Count(str, "%s")
if n > len(args) {
return "", errors.New("Too many parameters")
}
if n == 0 {
return fmt.Sprintf(str, args...), nil
}
return fmt.Sprintf(str, args[:n]...), nil
}
version, err := truncatingSprintf(template, c.version.full, c.version.major, c.version.minor, c.version.patch)
if err != nil {
return c.version.full
}
return version
}
type language struct {
props *properties
env environmentInfo
extensions []string
commands []*cmd
versionURLTemplate string
activeCommand *cmd
exitCode int
loadContext loadContext
inContext inContext
matchesVersionFile matchesVersionFile
}
const (
// DisplayMode sets the display mode (always, when_in_context, never)
DisplayMode Property = "display_mode"
// DisplayModeAlways displays the segment always
DisplayModeAlways string = "always"
// DisplayModeFiles displays the segment when the current folder contains certain extensions
DisplayModeFiles string = "files"
// DisplayModeEnvironment displays the segment when the environment has a language's context
DisplayModeEnvironment string = "environment"
// DisplayModeContext displays the segment when the environment or files is active
DisplayModeContext string = "context"
// MissingCommandText sets the text to display when the command is not present in the system
MissingCommandText Property = "missing_command_text"
// VersionMismatchColor displays empty string by default
VersionMismatchColor Property = "version_mismatch_color"
// EnableVersionMismatch displays empty string by default
EnableVersionMismatch Property = "enable_version_mismatch"
)
func (l *language) string() string {
if !l.props.getBool(DisplayVersion, true) {
return ""
}
err := l.setVersion()
displayError := l.props.getBool(DisplayError, true)
if err != nil && displayError {
return err.Error()
}
if err != nil {
return ""
}
if l.props.getBool(EnableHyperlink, false) {
return l.activeCommand.buildVersionURL(l.versionURLTemplate)
}
if l.props.getBool(EnableVersionMismatch, false) {
l.setVersionFileMismatch()
}
return l.activeCommand.version.full
}
func (l *language) enabled() bool {
inHomeDir := func() bool {
return l.env.getcwd() == l.env.homeDir()
}
displayMode := l.props.getString(DisplayMode, DisplayModeFiles)
if inHomeDir() && displayMode != DisplayModeAlways {
return false
}
l.loadLanguageContext()
switch displayMode {
case DisplayModeAlways:
return true
case DisplayModeEnvironment:
return l.inLanguageContext()
case DisplayModeFiles:
return l.hasLanguageFiles()
case DisplayModeContext:
fallthrough
default:
return l.hasLanguageFiles() || l.inLanguageContext()
}
}
// hasLanguageFiles will return true at least one file matching the extensions is found
func (l *language) hasLanguageFiles() bool {
for i, extension := range l.extensions {
if l.env.hasFiles(extension) {
break
}
if i == len(l.extensions)-1 {
return false
}
}
return true
}
// setVersion parses the version string returned by the command
func (l *language) setVersion() error {
for _, command := range l.commands {
if !l.env.hasCommand(command.executable) {
continue
}
version, err := l.env.runCommand(command.executable, command.args...)
if exitErr, ok := err.(*commandError); ok {
l.exitCode = exitErr.exitCode
return fmt.Errorf("err executing %s with %s", command.executable, command.args)
}
if version == "" {
continue
}
err = command.parse(version)
if err != nil {
return fmt.Errorf("err parsing info from %s with %s", command.executable, version)
}
l.activeCommand = command
return nil
}
return errors.New(l.props.getString(MissingCommandText, ""))
}
func (l *language) loadLanguageContext() {
if l.loadContext == nil {
return
}
l.loadContext()
}
func (l *language) inLanguageContext() bool {
if l.inContext == nil {
return false
}
return l.inContext()
}
func (l *language) setVersionFileMismatch() {
if l.matchesVersionFile == nil || l.matchesVersionFile() {
return
}
if l.props.getBool(ColorBackground, false) {
l.props.background = l.props.getColor(VersionMismatchColor, l.props.background)
return
}
l.props.foreground = l.props.getColor(VersionMismatchColor, l.props.foreground)
}