Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions internal/action/command.go
Original file line number Diff line number Diff line change
Expand Up @@ -601,6 +601,8 @@ func doSetGlobalOptionNative(option string, nativeValue any) error {
} else {
config.SetAutoTime(0)
}
} else if option == "ambiguouswidth" {
util.SetAmbiguousWidth(nativeValue.(string))
} else if option == "paste" {
screen.Screen.SetPaste(nativeValue.(bool))
} else if option == "clipboard" {
Expand Down
3 changes: 3 additions & 0 deletions internal/config/settings.go
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@ type optionValidator func(string, any) error

// a list of settings that need option validators
var optionValidators = map[string]optionValidator{
"ambiguouswidth": validateChoice,
"autosave": validateNonNegativeValue,
"clipboard": validateChoice,
"colorcolumn": validateNonNegativeValue,
Expand All @@ -41,6 +42,7 @@ var optionValidators = map[string]optionValidator{

// a list of settings with pre-defined choices
var OptionChoices = map[string][]string{
"ambiguouswidth": {"auto", "single", "double"},
"clipboard": {"internal", "external", "terminal"},
"fileformat": {"unix", "dos"},
"helpsplit": {"hsplit", "vsplit"},
Expand Down Expand Up @@ -110,6 +112,7 @@ var defaultCommonSettings = map[string]any{
// a list of settings that should only be globally modified and their
// default values
var DefaultGlobalOnlySettings = map[string]any{
"ambiguouswidth": "auto",
"autosave": float64(0),
"clipboard": "external",
"colorscheme": "default",
Expand Down
3 changes: 3 additions & 0 deletions internal/screen/screen.go
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ import (
"sync"

"github.com/micro-editor/micro/v2/internal/config"
"github.com/micro-editor/micro/v2/internal/util"
"github.com/micro-editor/tcell/v2"
)

Expand Down Expand Up @@ -182,6 +183,8 @@ func TempStart(screenWasNil bool) {
func Init() error {
drawChan = make(chan bool, 8)

util.SetAmbiguousWidth(config.GetGlobalOption("ambiguouswidth").(string))

// Should we enable true color?
truecolor := config.GetGlobalOption("truecolor").(string)
if truecolor == "on" || (truecolor == "auto" && os.Getenv("MICRO_TRUECOLOR") == "1") {
Expand Down
25 changes: 25 additions & 0 deletions internal/util/util.go
Original file line number Diff line number Diff line change
Expand Up @@ -91,6 +91,31 @@ func init() {
}

Stdout = new(bytes.Buffer)

envAmbiguousWide = runewidth.EastAsianWidth
}

// envAmbiguousWide is the ambiguous width detected from the environment
// (the locale environment variables and RUNEWIDTH_EASTASIAN) at startup
var envAmbiguousWide bool

// SetAmbiguousWidth sets how wide the characters with the East Asian
// Ambiguous width property (see Unicode Standard Annex #11) are considered
// to be: one column ("single"), two columns ("double") or as many columns
// as the environment implies ("auto")
func SetAmbiguousWidth(mode string) {
var wide bool
switch mode {
case "single":
wide = false
case "double":
wide = true
default:
wide = envAmbiguousWide
}
// go-runewidth syncs DefaultCondition with runewidth.EastAsianWidth only
// once, in its init(), so overriding it later has to be done here
runewidth.DefaultCondition.EastAsianWidth = wide
}

// SliceEnd returns a byte slice where the index is a rune index
Expand Down
25 changes: 25 additions & 0 deletions internal/util/util_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,31 @@ func TestStringWidth(t *testing.T) {
assert.Equal(t, 26, n)
}

func TestSetAmbiguousWidth(t *testing.T) {
defer SetAmbiguousWidth("auto")

// U+03B1 is ambiguous width, U+3042 is wide, U+0061 is narrow
bytes := []byte("\u03b1\u3042a")

SetAmbiguousWidth("single")
assert.Equal(t, 1, StringWidth(bytes, 1, 4))
assert.Equal(t, 3, StringWidth(bytes, 2, 4))
assert.Equal(t, 4, StringWidth(bytes, 3, 4))

SetAmbiguousWidth("double")
assert.Equal(t, 2, StringWidth(bytes, 1, 4))
assert.Equal(t, 4, StringWidth(bytes, 2, 4))
assert.Equal(t, 5, StringWidth(bytes, 3, 4))

// "auto" restores the width implied by the environment
SetAmbiguousWidth("auto")
expected := 1
if envAmbiguousWide {
expected = 2
}
assert.Equal(t, expected, StringWidth(bytes, 1, 4))
}

func TestSliceVisualEnd(t *testing.T) {
s := []byte("\thello")
slc, n, _ := SliceVisualEnd(s, 2, 4)
Expand Down
17 changes: 17 additions & 0 deletions runtime/help/options.md
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,23 @@ if you have set either of the above environment variables).

Here are the available options:

* `ambiguouswidth`: how wide the characters with the East Asian Ambiguous
width property (as defined by [Unicode Standard Annex
#11](https://www.unicode.org/reports/tr11)) are considered to be. These
characters, e.g. Greek and Cyrillic letters, accented Latin letters and box
drawing characters, are drawn one column wide by most terminals but two
columns wide by some terminals in an East Asian environment. If micro's
choice does not match your terminal, text is misaligned and the cursor is
drawn in the wrong column. This setting is `global only`.
* `auto`: use two columns if the locale environment variables (`LC_ALL`,
`LC_CTYPE`, `LANG`) indicate a Chinese, Japanese or Korean locale,
otherwise one column. Can be overridden with the `RUNEWIDTH_EASTASIAN`
environment variable.
* `single`: always use one column, like `wcwidth(3)` and most terminals do.
* `double`: always use two columns.

default value: `auto`

* `autoindent`: when creating a new line, use the same indentation as the
previous line.

Expand Down