Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Is undercurl supportted? #1232

Closed
eeeXun opened this issue May 10, 2023 · 6 comments · Fixed by #1896
Closed

Is undercurl supportted? #1232

eeeXun opened this issue May 10, 2023 · 6 comments · Fixed by #1896

Comments

@eeeXun
Copy link

eeeXun commented May 10, 2023

I add set cursorparentfmt "\033[4:3m" in lfrc. But cursor is not showing.

@joelim-work
Copy link
Collaborator

lf doesn't directly write escape sequences to the terminal, because it uses the tcell module for drawing the UI. Instead it parses escape sequences and constructs a Style object containing the relevant settings.

lf/colors.go

Lines 101 to 164 in f04401b

// ECMA-48 details the standard
// TODO: should we support turning off attributes?
// Probably because this is used for previewers too
for i := 0; i < len(nums); i++ {
n := nums[i]
switch {
case n == 0:
st = tcell.StyleDefault
case n == 1:
st = st.Bold(true)
case n == 2:
st = st.Dim(true)
case n == 3:
st = st.Italic(true)
case n == 4:
st = st.Underline(true)
case n == 5 || n == 6:
st = st.Blink(true)
case n == 7:
st = st.Reverse(true)
case n == 8:
// TODO: tcell PR for proper conceal
_, bg, _ := st.Decompose()
st = st.Foreground(bg)
case n == 9:
st = st.StrikeThrough(true)
case n >= 30 && n <= 37:
st = st.Foreground(tcell.PaletteColor(n - 30))
case n >= 90 && n <= 97:
st = st.Foreground(tcell.PaletteColor(n - 82))
case n == 38:
if i+3 <= len(nums) && nums[i+1] == 5 {
st = st.Foreground(tcell.PaletteColor(nums[i+2]))
i += 2
} else if i+5 <= len(nums) && nums[i+1] == 2 {
st = st.Foreground(tcell.NewRGBColor(
int32(nums[i+2]),
int32(nums[i+3]),
int32(nums[i+4])))
i += 4
} else {
log.Printf("unknown ansi code or incorrect form: %d", n)
}
case n >= 40 && n <= 47:
st = st.Background(tcell.PaletteColor(n - 40))
case n >= 100 && n <= 107:
st = st.Background(tcell.PaletteColor(n - 92))
case n == 48:
if i+3 <= len(nums) && nums[i+1] == 5 {
st = st.Background(tcell.PaletteColor(nums[i+2]))
i += 2
} else if i+5 <= len(nums) && nums[i+1] == 2 {
st = st.Background(tcell.NewRGBColor(
int32(nums[i+2]),
int32(nums[i+3]),
int32(nums[i+4])))
i += 4
} else {
log.Printf("unknown ansi code or incorrect form: %d", n)
}
default:
log.Printf("unknown ansi code: %d", n)
}
}

For undercurl to work, support would have to be added to tcell, and I think it's unlikely to happen since it's not exactly standard.

@eeeXun
Copy link
Author

eeeXun commented May 13, 2023

Ok, I get it. Thank you for your reply.

@eeeXun eeeXun closed this as completed May 13, 2023
@joelim-work
Copy link
Collaborator

With the release of tcell 2.8.0, this should now be supported, but the code still needs to be changed to handle the non-standard terminal escape sequences. The implementation should look something like below:

Rewrite applyAnsiCodes function to support 4:x tokens in colors.go:

Click to expand
func applyAnsiCodes(s string, st tcell.Style) tcell.Style {
	toks := strings.Split(s, ";")

	// ECMA-48 details the standard
	// TODO: should we support turning off attributes?
	//    Probably because this is used for previewers too
	tokslen := len(toks)
	for i := 0; i < tokslen; i++ {
		switch toks[i] {
		case "", "0":
			st = tcell.StyleDefault
		case "1":
			st = st.Bold(true)
		case "2":
			st = st.Dim(true)
		case "3":
			st = st.Italic(true)
		case "4:0":
			st = st.Underline(false)
		case "4", "4:1":
			st = st.Underline(true)
		case "4:2":
			st = st.Underline(tcell.UnderlineStyleDouble)
		case "4:3":
			st = st.Underline(tcell.UnderlineStyleCurly)
		case "4:4":
			st = st.Underline(tcell.UnderlineStyleDotted)
		case "4:5":
			st = st.Underline(tcell.UnderlineStyleDashed)
		case "5", "6":
			st = st.Blink(true)
		case "7":
			st = st.Reverse(true)
		case "8":
			// TODO: tcell PR for proper conceal
			_, bg, _ := st.Decompose()
			st = st.Foreground(bg)
		case "9":
			st = st.StrikeThrough(true)
		case "30", "31", "32", "33", "34", "35", "36", "37":
			n, _ := strconv.Atoi(toks[i])
			st = st.Foreground(tcell.PaletteColor(n - 30))
		case "90", "91", "92", "93", "94", "95", "96", "97":
			n, _ := strconv.Atoi(toks[i])
			st = st.Foreground(tcell.PaletteColor(n - 82))
		case "38":
			if toks[i+1] == "5" && i+2 < tokslen {
				n, err := strconv.Atoi(toks[i+2])
				if err != nil {
					log.Printf("unknown ansi code: %s", toks[i+2])
					continue
				}
				st = st.Foreground(tcell.PaletteColor(n))
				i += 2
			} else if toks[i+1] == "2" && i+4 < tokslen {
				r, err := strconv.Atoi(toks[i+2])
				if err != nil {
					log.Printf("unknown ansi code: %s", toks[i+2])
					continue
				}
				g, err := strconv.Atoi(toks[i+3])
				if err != nil {
					log.Printf("unknown ansi code: %s", toks[i+3])
					continue
				}
				b, err := strconv.Atoi(toks[i+4])
				if err != nil {
					log.Printf("unknown ansi code: %s", toks[i+4])
					continue
				}
				st = st.Foreground(tcell.NewRGBColor(int32(r), int32(g), int32(b)))
				i += 4
			} else {
				log.Printf("unknown ansi code or incorrect form: %s", toks[i])
			}
		case "40", "41", "42", "43", "44", "45", "46", "47":
			n, _ := strconv.Atoi(toks[i])
			st = st.Background(tcell.PaletteColor(n - 40))
		case "100", "101", "102", "103", "104", "105", "106", "107":
			n, _ := strconv.Atoi(toks[i])
			st = st.Background(tcell.PaletteColor(n - 92))
		case "48":
			if toks[i+1] == "5" && i+2 < tokslen {
				n, err := strconv.Atoi(toks[i+2])
				if err != nil {
					log.Printf("unknown ansi code: %s", toks[i+2])
					continue
				}
				st = st.Background(tcell.PaletteColor(n))
				i += 2
			} else if toks[i+1] == "2" && i+4 < tokslen {
				r, err := strconv.Atoi(toks[i+2])
				if err != nil {
					log.Printf("unknown ansi code: %s", toks[i+2])
					continue
				}
				g, err := strconv.Atoi(toks[i+3])
				if err != nil {
					log.Printf("unknown ansi code: %s", toks[i+3])
					continue
				}
				b, err := strconv.Atoi(toks[i+4])
				if err != nil {
					log.Printf("unknown ansi code: %s", toks[i+4])
					continue
				}
				st = st.Background(tcell.NewRGBColor(int32(r), int32(g), int32(b)))
				i += 4
			} else {
				log.Printf("unknown ansi code or incorrect form: %s", toks[i])
			}
		default:
			log.Printf("unknown ansi code: %s", toks[i])
		}
	}

	return st
}

Additional test cases in colors_test.go:

{"4:0", none, none},
{"4:0", none.Underline(true), none},
{"4:1", none, none.Underline(true)},
{"4:2", none, none.Underline(tcell.UnderlineStyleDouble)},
{"4:3", none, none.Underline(tcell.UnderlineStyleCurly)},
{"4:4", none, none.Underline(tcell.UnderlineStyleDotted)},
{"4:5", none, none.Underline(tcell.UnderlineStyleDashed)},

Example lfrc config for demo:

cmd test &{{
    lf -remote "send $id set cursorpreviewfmt \"\033[4:0m\""
    sleep 1
    lf -remote "send $id set cursorpreviewfmt \"\033[4:1m\""
    sleep 1
    lf -remote "send $id set cursorpreviewfmt \"\033[4:2m\""
    sleep 1
    lf -remote "send $id set cursorpreviewfmt \"\033[4:3m\""
    sleep 1
    lf -remote "send $id set cursorpreviewfmt \"\033[4:4m\""
    sleep 1
    lf -remote "send $id set cursorpreviewfmt \"\033[4:5m\""
    sleep 1
    lf -remote "send $id set cursorpreviewfmt \"\033[4m\""
}}

That being said, I'm not sure how much interest there is in this kind of feature so I plan to leave it here for now.

@joelim-work joelim-work reopened this Jan 15, 2025
@eeeXun
Copy link
Author

eeeXun commented Jan 17, 2025

I want this feature 👀 That would be great if this feature could be implemented

@joelim-work
Copy link
Collaborator

Can you try #1896 to see if the patch works for you?

@eeeXun
Copy link
Author

eeeXun commented Jan 21, 2025

Yes, it works for me

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging a pull request may close this issue.

2 participants