From 0b23ccf7265ecb69564b2a6724ffc4013069320b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=C3=B6ran=20Karl?= <3951388+JoeKar@users.noreply.github.com> Date: Sun, 7 Jun 2026 12:30:07 +0200 Subject: [PATCH 1/9] Switch `tcell` to upstream `v3` and overwrite with our fork from `micro-editor` With the fork we can still use raw escape sequences until something similar is present upstream. Adapt to the style and clipboard interface changes: - replace Decompose() with dedicated getters - replace attributes with dedicated getters - replace underline attribute with new interface We have to update our required go toolchain version to 1.25 due to the fact that tcell updated its toolchain dependency to 1.25. --- .github/workflows/nightly.yaml | 2 +- .github/workflows/release.yaml | 2 +- .github/workflows/test.yaml | 2 +- cmd/micro/micro.go | 2 +- cmd/micro/micro_test.go | 2 +- go.mod | 19 +++++---- go.sum | 63 ++++++++++++++++++++++------- internal/action/actions.go | 2 +- internal/action/bindings.go | 2 +- internal/action/bufpane.go | 2 +- internal/action/events.go | 2 +- internal/action/infopane.go | 2 +- internal/action/keytree.go | 2 +- internal/action/rawpane.go | 2 +- internal/action/tab.go | 2 +- internal/action/termpane.go | 2 +- internal/buffer/message.go | 2 +- internal/clipboard/terminal.go | 12 +++--- internal/config/colorscheme.go | 10 ++--- internal/config/colorscheme_test.go | 28 ++++++++----- internal/display/bufwindow.go | 24 +++++------ internal/display/infowindow.go | 2 +- internal/display/tabwindow.go | 2 +- internal/display/termwindow.go | 2 +- internal/screen/screen.go | 2 +- 25 files changed, 121 insertions(+), 73 deletions(-) diff --git a/.github/workflows/nightly.yaml b/.github/workflows/nightly.yaml index c06a9d6cba..ff0b59f7ec 100644 --- a/.github/workflows/nightly.yaml +++ b/.github/workflows/nightly.yaml @@ -7,7 +7,7 @@ jobs: nightly: strategy: matrix: - go-version: [1.23.x] + go-version: [1.25.x] os: [ubuntu-latest] runs-on: ${{ matrix.os }} steps: diff --git a/.github/workflows/release.yaml b/.github/workflows/release.yaml index 066615afa4..15ca418b2a 100644 --- a/.github/workflows/release.yaml +++ b/.github/workflows/release.yaml @@ -8,7 +8,7 @@ jobs: release: strategy: matrix: - go-version: [1.23.x] + go-version: [1.25.x] os: [ubuntu-latest] runs-on: ${{ matrix.os }} steps: diff --git a/.github/workflows/test.yaml b/.github/workflows/test.yaml index ef113b0db4..32b1f1664d 100644 --- a/.github/workflows/test.yaml +++ b/.github/workflows/test.yaml @@ -4,7 +4,7 @@ jobs: test: strategy: matrix: - go-version: [1.19.x, 1.23.x] + go-version: [1.25.x] os: [ubuntu-latest, macos-latest, windows-latest] runs-on: ${{ matrix.os }} steps: diff --git a/cmd/micro/micro.go b/cmd/micro/micro.go index 44596a391b..1331e41cb0 100644 --- a/cmd/micro/micro.go +++ b/cmd/micro/micro.go @@ -16,6 +16,7 @@ import ( "syscall" "time" + "github.com/gdamore/tcell/v3" "github.com/go-errors/errors" isatty "github.com/mattn/go-isatty" "github.com/micro-editor/micro/v2/internal/action" @@ -25,7 +26,6 @@ import ( "github.com/micro-editor/micro/v2/internal/screen" "github.com/micro-editor/micro/v2/internal/shell" "github.com/micro-editor/micro/v2/internal/util" - "github.com/micro-editor/tcell/v2" lua "github.com/yuin/gopher-lua" ) diff --git a/cmd/micro/micro_test.go b/cmd/micro/micro_test.go index 185b296e5b..d90c699ea7 100644 --- a/cmd/micro/micro_test.go +++ b/cmd/micro/micro_test.go @@ -6,13 +6,13 @@ import ( "os" "testing" + "github.com/gdamore/tcell/v3" "github.com/go-errors/errors" "github.com/micro-editor/micro/v2/internal/action" "github.com/micro-editor/micro/v2/internal/buffer" "github.com/micro-editor/micro/v2/internal/config" "github.com/micro-editor/micro/v2/internal/screen" "github.com/micro-editor/micro/v2/internal/util" - "github.com/micro-editor/tcell/v2" "github.com/stretchr/testify/assert" ) diff --git a/go.mod b/go.mod index 2e243c271d..9accea6e65 100644 --- a/go.mod +++ b/go.mod @@ -3,12 +3,12 @@ module github.com/micro-editor/micro/v2 require ( github.com/blang/semver v3.5.1+incompatible github.com/dustin/go-humanize v1.0.0 + github.com/gdamore/tcell/v3 v3.4.0 github.com/go-errors/errors v1.0.1 github.com/kballard/go-shellquote v0.0.0-20180428030007-95032a82bc51 github.com/mattn/go-isatty v0.0.20 github.com/mattn/go-runewidth v0.0.16 github.com/micro-editor/json5 v1.0.1-micro - github.com/micro-editor/tcell/v2 v2.0.13 github.com/micro-editor/terminal v0.0.0-20250324214352-e587e959c6b5 github.com/mitchellh/go-homedir v1.1.0 github.com/sergi/go-diff v1.1.0 @@ -16,25 +16,28 @@ require ( github.com/yuin/gopher-lua v1.1.1 github.com/zyedidia/clipper v0.1.1 github.com/zyedidia/glob v0.0.0-20170209203856-dd4023a66dc3 - golang.org/x/text v0.4.0 + golang.org/x/text v0.38.0 gopkg.in/yaml.v2 v2.2.8 layeh.com/gopher-luar v1.0.11 ) require ( + github.com/clipperhouse/displaywidth v0.11.0 // indirect + github.com/clipperhouse/uax29/v2 v2.7.0 // indirect github.com/creack/pty v1.1.18 // indirect github.com/davecgh/go-spew v1.1.1 // indirect - github.com/gdamore/encoding v1.0.0 // indirect - github.com/lucasb-eyer/go-colorful v1.0.3 // indirect + github.com/gdamore/encoding v1.0.1 // indirect + github.com/lucasb-eyer/go-colorful v1.4.0 // indirect github.com/pmezard/go-difflib v1.0.0 // indirect github.com/rivo/uniseg v0.2.0 // indirect - github.com/zyedidia/poller v1.0.1 // indirect - golang.org/x/sys v0.30.0 // indirect - golang.org/x/term v0.29.0 // indirect + golang.org/x/sys v0.46.0 // indirect + golang.org/x/term v0.44.0 // indirect ) replace github.com/kballard/go-shellquote => github.com/micro-editor/go-shellquote v0.0.0-20250101105543-feb6c39314f5 replace layeh.com/gopher-luar v1.0.11 => github.com/layeh/gopher-luar v1.0.11 -go 1.19 +replace github.com/gdamore/tcell/v3 => github.com/micro-editor/tcell/v3 v3.0.0-20260629163637-dedaf40206a5 + +go 1.25.0 diff --git a/go.sum b/go.sum index 2acd88f55d..5e66652ea6 100644 --- a/go.sum +++ b/go.sum @@ -3,6 +3,10 @@ github.com/blang/semver v3.5.1+incompatible/go.mod h1:kRBLl5iJ+tD4TcOOxsy/0fnweb github.com/chzyer/logex v1.1.10/go.mod h1:+Ywpsq7O8HXn0nuIou7OrIPyXbp3wmkHB+jjWRnGsAI= github.com/chzyer/readline v0.0.0-20180603132655-2972be24d48e/go.mod h1:nSuG5e5PlCu98SY8svDHJxuZscDgtXS6KTTbou5AhLI= github.com/chzyer/test v0.0.0-20180213035817-a1ea475d72b1/go.mod h1:Q3SI9o4m/ZMnBNeIyt5eFwwo7qiLfzFZmjNmxjkiQlU= +github.com/clipperhouse/displaywidth v0.11.0 h1:lBc6kY44VFw+TDx4I8opi/EtL9m20WSEFgwIwO+UVM8= +github.com/clipperhouse/displaywidth v0.11.0/go.mod h1:bkrFNkf81G8HyVqmKGxsPufD3JhNl3dSqnGhOoSD/o0= +github.com/clipperhouse/uax29/v2 v2.7.0 h1:+gs4oBZ2gPfVrKPthwbMzWZDaAFPGYK72F0NJv2v7Vk= +github.com/clipperhouse/uax29/v2 v2.7.0/go.mod h1:EFJ2TJMRUaplDxHKj1qAEhCtQPW2tJSwu5BF98AuoVM= github.com/creack/pty v1.1.18 h1:n56/Zwd5o6whRC5PMGretI4IdRLlmBXYNjScPaBgsbY= github.com/creack/pty v1.1.18/go.mod h1:MOBLtS5ELjhRRrroQr9kyvTxUAFNvYEK993ew/Vr4O4= github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= @@ -10,8 +14,8 @@ github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/dustin/go-humanize v1.0.0 h1:VSnTsYCnlFHaM2/igO1h6X3HA71jcobQuxemgkq4zYo= github.com/dustin/go-humanize v1.0.0/go.mod h1:HtrtbFcZ19U5GC7JDqmcUSB87Iq5E25KnS6fMYU6eOk= -github.com/gdamore/encoding v1.0.0 h1:+7OoQ1Bc6eTm5niUzBa0Ctsh6JbMW6Ra+YNuAtDBdko= -github.com/gdamore/encoding v1.0.0/go.mod h1:alR0ol34c49FCSBLjhosxzcPHQbf2trDkoo5dl+VrEg= +github.com/gdamore/encoding v1.0.1 h1:YzKZckdBL6jVt2Gc+5p82qhrGiqMdG/eNs6Wy0u3Uhw= +github.com/gdamore/encoding v1.0.1/go.mod h1:0Z0cMFinngz9kS1QfMjCP8TY7em3bZYeeklsSDPivEo= github.com/go-errors/errors v1.0.1 h1:LUHzmkK3GUKUrL/1gfBUxAHzcev3apQlezX/+O7ma6w= github.com/go-errors/errors v1.0.1/go.mod h1:f4zRHt4oKfwPJE5k8C9vpYG+aDHdBFUsgrm6/TyX73Q= github.com/kr/pretty v0.1.0 h1:L/CwN0zerZDmRFUapSPitk6f+Q3+0za1rQkzVuMiMFI= @@ -20,10 +24,11 @@ github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ= github.com/kr/text v0.1.0 h1:45sCR5RtlFHMR4UwH9sdQ5TC8v0qDQCHnXt+kaKSTVE= github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI= github.com/kylelemons/godebug v1.1.0 h1:RPNrshWIDI6G2gRW9EHilWtl7Z6Sb1BR0xunSBf0SNc= +github.com/kylelemons/godebug v1.1.0/go.mod h1:9/0rRGxNHcop5bhtWyNeEfOS8JIWk580+fNqagV/RAw= github.com/layeh/gopher-luar v1.0.11 h1:ss6t9OtykOiETBScJylSMPhuYAtOmpH5rSX10/wCcis= github.com/layeh/gopher-luar v1.0.11/go.mod h1:TPnIVCZ2RJBndm7ohXyaqfhzjlZ+OA2SZR/YwL8tECk= -github.com/lucasb-eyer/go-colorful v1.0.3 h1:QIbQXiugsb+q10B+MI+7DI1oQLdmnep86tWFlaaUAac= -github.com/lucasb-eyer/go-colorful v1.0.3/go.mod h1:R4dSotOR9KMtayYi1e77YzuveK+i7ruzyGqttikkLy0= +github.com/lucasb-eyer/go-colorful v1.4.0 h1:UtrWVfLdarDgc44HcS7pYloGHJUjHV/4FwW4TvVgFr4= +github.com/lucasb-eyer/go-colorful v1.4.0/go.mod h1:R4dSotOR9KMtayYi1e77YzuveK+i7ruzyGqttikkLy0= github.com/mattn/go-isatty v0.0.20 h1:xfD0iDuEKnDkl03q4limB+vH+GxLEtL/jb4xVJSWWEY= github.com/mattn/go-isatty v0.0.20/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y= github.com/mattn/go-runewidth v0.0.16 h1:E5ScNMtiwvlvB5paMFdw9p4kSQzbXFikJ5SQO6TULQc= @@ -32,8 +37,8 @@ github.com/micro-editor/go-shellquote v0.0.0-20250101105543-feb6c39314f5 h1:D7BP github.com/micro-editor/go-shellquote v0.0.0-20250101105543-feb6c39314f5/go.mod h1:zaPgW/fDiW4MUfEwxpC+GB/bhvX44NJaNHmRAC9auHQ= github.com/micro-editor/json5 v1.0.1-micro h1:5Y4MuzhkmW0sQQNPvrIVevIOKi557qsznwjRr4iq1AI= github.com/micro-editor/json5 v1.0.1-micro/go.mod h1:cmlPHZ1JKOXNse0/3zwwKj/GUpzAVkzx4lZDkpHl4q0= -github.com/micro-editor/tcell/v2 v2.0.13 h1:xyuSpBhSBsUH+bs7FER9IV2/TsQpBmCFiNWJVAEdT68= -github.com/micro-editor/tcell/v2 v2.0.13/go.mod h1:ixpjICpoGp83FZVoLYFJPBwCAslHeTnvgPdhJVPLyy0= +github.com/micro-editor/tcell/v3 v3.0.0-20260629163637-dedaf40206a5 h1:vwHFkhbdjyf5/XMPbPzSDYtXMOQJKvmwJA+nlrJHB9s= +github.com/micro-editor/tcell/v3 v3.0.0-20260629163637-dedaf40206a5/go.mod h1:Ev/2PFhL0QtVmu6XPZG9NEuITAZ6XH7i7/BF3wupBdw= github.com/micro-editor/terminal v0.0.0-20250324214352-e587e959c6b5 h1:czSkYUNmHuWS2lv8VreufENEXZNOCGZcXd744YKf8yM= github.com/micro-editor/terminal v0.0.0-20250324214352-e587e959c6b5/go.mod h1:OszIG7ockt4osicVHq6gI2QmV4PBDK6H5/Bj8GDGv4Q= github.com/mitchellh/go-homedir v1.1.0 h1:lukF9ziXFxDFPkA1vsr5zpc1XuPDn/wFntq5mG+4E0Y= @@ -43,11 +48,13 @@ github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZN github.com/rivo/uniseg v0.2.0 h1:S1pD9weZBuJdFmowNwbpi7BJ8TNftyUImj/0WQi72jY= github.com/rivo/uniseg v0.2.0/go.mod h1:J6wj4VEh+S6ZtnVlnTBMWIodfgj8LQOQFoIToxlJtxc= github.com/robertkrimen/otto v0.2.1 h1:FVP0PJ0AHIjC+N4pKCG9yCDz6LHNPCwi/GKID5pGGF0= +github.com/robertkrimen/otto v0.2.1/go.mod h1:UPwtJ1Xu7JrLcZjNWN8orJaM5n5YEtqL//farB5FlRY= github.com/sergi/go-diff v1.1.0 h1:we8PVUC3FE2uYfodKH/nBHMSetSfHDR6scGdBi+erh0= github.com/sergi/go-diff v1.1.0/go.mod h1:STckp+ISIX8hZLjrqAeVduY0gWCT9IjLuqbuNXdaHfM= github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= github.com/stretchr/testify v1.4.0 h1:2E4SXV/wtOkTonXsotYi4li6zVWxYlZuYNCXe9XRJyk= github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4= +github.com/yuin/goldmark v1.4.13/go.mod h1:6yULJ656Px+3vBD8DxQVa3kxgyrAnzto9xy5taEt/CY= github.com/yuin/gopher-lua v0.0.0-20190206043414-8bfc7677f583/go.mod h1:gqRgreBUhTSL0GeU64rtZ3Uq3wtjOa/TB2YfrtkCbVQ= github.com/yuin/gopher-lua v1.1.1 h1:kYKnWBjvbNP4XLT3+bPEwAXJx262OhaHDWDVOPjL46M= github.com/yuin/gopher-lua v1.1.1/go.mod h1:GBR0iDaNXjAgGg9zfCvksxSRnQx76gclCIb7kdAd1Pw= @@ -55,21 +62,49 @@ github.com/zyedidia/clipper v0.1.1 h1:HBgguFNDq/QmSQKBnhy4sMKzILINr139VEgAhftOUT github.com/zyedidia/clipper v0.1.1/go.mod h1:7YApPNiiTZTXdKKZG92G50qj6mnWEX975Sdu65J7YpQ= github.com/zyedidia/glob v0.0.0-20170209203856-dd4023a66dc3 h1:oMHjjTLfGXVuyOQBYj5/td9WC0mw4g1xDBPovIqmHew= github.com/zyedidia/glob v0.0.0-20170209203856-dd4023a66dc3/go.mod h1:YKbIYP//Eln8eDgAJGI3IDvR3s4Tv9Z9TGIOumiyQ5c= -github.com/zyedidia/poller v1.0.1 h1:Tt9S3AxAjXwWGNiC2TUdRJkQDZSzCBNVQ4xXiQ7440s= -github.com/zyedidia/poller v1.0.1/go.mod h1:vZXJOHGDcuK08GXhF6IAY0ZFd2WcgOR5DOTp84Uk5eE= +golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= +golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc= +golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4/go.mod h1:jJ57K6gSWd91VN4djpZkiMVwK6gcyfeH4XE8wZrZaV4= +golang.org/x/mod v0.8.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs= +golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= +golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg= +golang.org/x/net v0.0.0-20220722155237-a158d28d115b/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c= +golang.org/x/net v0.6.0/go.mod h1:2Tu9+aMcznHK/AK1HMvgo6xiTLG5rD5rZLDS+rp2Bjs= +golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.0.0-20220722155255-886fb9371eb4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.1.0/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sys v0.0.0-20190204203706-41f3e6584952/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= +golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= +golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20220520151302-bc2c85ada10a/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20220722155257-8c9f86f7a55f/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.5.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.30.0 h1:QjkSwP/36a20jFYWkSue1YwXzLmsV5Gfq7Eiy72C1uc= -golang.org/x/sys v0.30.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= -golang.org/x/term v0.29.0 h1:L6pJp37ocefwRRtYPKSWOWzOtWSxVajvz2ldH/xi3iU= -golang.org/x/term v0.29.0/go.mod h1:6bl4lRlvVuDgSf3179VpIxBF0o10JUpXWOnI7nErv7s= +golang.org/x/sys v0.46.0 h1:noSf2Fq6F8DBgS+LysIkx7rIExoNHJsxOAtPp4rthXw= +golang.org/x/sys v0.46.0/go.mod h1:4GL1E5IUh+htKOUEOaiffhrAeqysfVGipDYzABqnCmw= +golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= +golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8= +golang.org/x/term v0.5.0/go.mod h1:jMB1sMXY+tzblOD4FWmEbocvup2/aLOaQEp7JmGp78k= +golang.org/x/term v0.44.0 h1:0rLvDRCtNj0gZkyIXhCyOb2OAzEhLVqc4B+hrsBhrmc= +golang.org/x/term v0.44.0/go.mod h1:7ze4MdzUzLXpSAoFP1H0bOI9aXDqveSvatT5vKcFh2Y= golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= -golang.org/x/text v0.4.0 h1:BrVqGRd7+k1DiOgtnFvAkoQEWQvBc25ouMJM6429SFg= -golang.org/x/text v0.4.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8= +golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= +golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ= +golang.org/x/text v0.7.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8= +golang.org/x/text v0.14.0/go.mod h1:18ZOQIKpY8NJVqYksKHtTdi31H5itFRjB5/qKTNYzSU= +golang.org/x/text v0.38.0 h1:sXmwo9DwP3OK9EZ7PqAdaooSGozfl/3a6/xJcbzPRhE= +golang.org/x/text v0.38.0/go.mod h1:YXZt3QhHUKYT53r2lLKFIVi6Ao1jdzrTR/KQ09qyxF4= +golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= +golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= +golang.org/x/tools v0.1.12/go.mod h1:hNGJHUnrk76NpqgfD5Aqm5Crs+Hm0VOH/i9J2+nxYbc= +golang.org/x/tools v0.6.0/go.mod h1:Xwgl3UAJ/d3gWutnCtw505GrjyAbvKui8lOU390QaIU= +golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15 h1:YR8cESwS4TdDjEe65xsg0ogRM/Nc3DYOhEAlW+xobZo= gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/sourcemap.v1 v1.0.5 h1:inv58fC9f9J3TK2Y2R1NPntXEn3/wjWHkonhIUODNTI= +gopkg.in/sourcemap.v1 v1.0.5/go.mod h1:2RlvNNSMglmRrcvhfuzp4hQHwOtjxlbjX7UPY/GXb78= gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= gopkg.in/yaml.v2 v2.2.4/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= gopkg.in/yaml.v2 v2.2.8 h1:obN1ZagJSUGI0Ek/LBmuj4SNLPfIny3KsKFopxRdj10= diff --git a/internal/action/actions.go b/internal/action/actions.go index 491199f76f..796584d2eb 100644 --- a/internal/action/actions.go +++ b/internal/action/actions.go @@ -10,6 +10,7 @@ import ( "strings" "time" + "github.com/gdamore/tcell/v3" shellquote "github.com/kballard/go-shellquote" "github.com/micro-editor/micro/v2/internal/buffer" "github.com/micro-editor/micro/v2/internal/clipboard" @@ -18,7 +19,6 @@ import ( "github.com/micro-editor/micro/v2/internal/screen" "github.com/micro-editor/micro/v2/internal/shell" "github.com/micro-editor/micro/v2/internal/util" - "github.com/micro-editor/tcell/v2" ) // ScrollUp is not an action diff --git a/internal/action/bindings.go b/internal/action/bindings.go index 555a21b803..2291cfe183 100644 --- a/internal/action/bindings.go +++ b/internal/action/bindings.go @@ -11,11 +11,11 @@ import ( "strings" "unicode" + "github.com/gdamore/tcell/v3" "github.com/micro-editor/json5" "github.com/micro-editor/micro/v2/internal/config" "github.com/micro-editor/micro/v2/internal/screen" "github.com/micro-editor/micro/v2/internal/util" - "github.com/micro-editor/tcell/v2" ) var Binder = map[string]func(e Event, action string){ diff --git a/internal/action/bufpane.go b/internal/action/bufpane.go index df6a20f8d8..d8906d9cb7 100644 --- a/internal/action/bufpane.go +++ b/internal/action/bufpane.go @@ -6,13 +6,13 @@ import ( luar "layeh.com/gopher-luar" + "github.com/gdamore/tcell/v3" "github.com/micro-editor/micro/v2/internal/buffer" "github.com/micro-editor/micro/v2/internal/config" "github.com/micro-editor/micro/v2/internal/display" ulua "github.com/micro-editor/micro/v2/internal/lua" "github.com/micro-editor/micro/v2/internal/screen" "github.com/micro-editor/micro/v2/internal/util" - "github.com/micro-editor/tcell/v2" lua "github.com/yuin/gopher-lua" ) diff --git a/internal/action/events.go b/internal/action/events.go index aa99d8af30..862c835d18 100644 --- a/internal/action/events.go +++ b/internal/action/events.go @@ -6,7 +6,7 @@ import ( "fmt" "strings" - "github.com/micro-editor/tcell/v2" + "github.com/gdamore/tcell/v3" ) type Event interface { diff --git a/internal/action/infopane.go b/internal/action/infopane.go index 6ecd955296..d641221100 100644 --- a/internal/action/infopane.go +++ b/internal/action/infopane.go @@ -3,12 +3,12 @@ package action import ( "bytes" + "github.com/gdamore/tcell/v3" "github.com/micro-editor/micro/v2/internal/buffer" "github.com/micro-editor/micro/v2/internal/config" "github.com/micro-editor/micro/v2/internal/display" "github.com/micro-editor/micro/v2/internal/info" "github.com/micro-editor/micro/v2/internal/util" - "github.com/micro-editor/tcell/v2" ) type InfoKeyAction func(*InfoPane) diff --git a/internal/action/keytree.go b/internal/action/keytree.go index 58838f334c..88714a8bd9 100644 --- a/internal/action/keytree.go +++ b/internal/action/keytree.go @@ -3,7 +3,7 @@ package action import ( "bytes" - "github.com/micro-editor/tcell/v2" + "github.com/gdamore/tcell/v3" ) type PaneKeyAction func(Pane) bool diff --git a/internal/action/rawpane.go b/internal/action/rawpane.go index e75e857789..1242b77f80 100644 --- a/internal/action/rawpane.go +++ b/internal/action/rawpane.go @@ -4,9 +4,9 @@ import ( "fmt" "reflect" + "github.com/gdamore/tcell/v3" "github.com/micro-editor/micro/v2/internal/buffer" "github.com/micro-editor/micro/v2/internal/display" - "github.com/micro-editor/tcell/v2" ) type RawPane struct { diff --git a/internal/action/tab.go b/internal/action/tab.go index e1672a9332..18b0637b9e 100644 --- a/internal/action/tab.go +++ b/internal/action/tab.go @@ -3,13 +3,13 @@ package action import ( luar "layeh.com/gopher-luar" + "github.com/gdamore/tcell/v3" "github.com/micro-editor/micro/v2/internal/buffer" "github.com/micro-editor/micro/v2/internal/config" "github.com/micro-editor/micro/v2/internal/display" ulua "github.com/micro-editor/micro/v2/internal/lua" "github.com/micro-editor/micro/v2/internal/screen" "github.com/micro-editor/micro/v2/internal/views" - "github.com/micro-editor/tcell/v2" ) // The TabList is a list of tabs and a window to display the tab bar diff --git a/internal/action/termpane.go b/internal/action/termpane.go index 4a0d71e937..77e5fb9db2 100644 --- a/internal/action/termpane.go +++ b/internal/action/termpane.go @@ -4,12 +4,12 @@ import ( "errors" "runtime" + "github.com/gdamore/tcell/v3" "github.com/micro-editor/micro/v2/internal/clipboard" "github.com/micro-editor/micro/v2/internal/config" "github.com/micro-editor/micro/v2/internal/display" "github.com/micro-editor/micro/v2/internal/screen" "github.com/micro-editor/micro/v2/internal/shell" - "github.com/micro-editor/tcell/v2" "github.com/micro-editor/terminal" ) diff --git a/internal/buffer/message.go b/internal/buffer/message.go index b2ab4e3473..eecfb745c1 100644 --- a/internal/buffer/message.go +++ b/internal/buffer/message.go @@ -1,8 +1,8 @@ package buffer import ( + "github.com/gdamore/tcell/v3" "github.com/micro-editor/micro/v2/internal/config" - "github.com/micro-editor/tcell/v2" ) type MsgType int diff --git a/internal/clipboard/terminal.go b/internal/clipboard/terminal.go index fac159cd31..fddea136f9 100644 --- a/internal/clipboard/terminal.go +++ b/internal/clipboard/terminal.go @@ -4,8 +4,8 @@ import ( "errors" "time" + "github.com/gdamore/tcell/v3" "github.com/micro-editor/micro/v2/internal/screen" - "github.com/micro-editor/tcell/v2" ) type terminalClipboard struct{} @@ -13,14 +13,13 @@ type terminalClipboard struct{} var terminal terminalClipboard func (t terminalClipboard) read(reg string) (string, error) { - screen.Screen.GetClipboard(reg) + screen.Screen.GetClipboard() // wait at most 200ms for response for { select { case event := <-screen.Events: - e, ok := event.(*tcell.EventPaste) - if ok { - return e.Text(), nil + if e, ok := event.(*tcell.EventClipboard); ok { + return string(e.Data()), nil } case <-time.After(200 * time.Millisecond): return "", errors.New("No clipboard received from terminal") @@ -29,5 +28,6 @@ func (t terminalClipboard) read(reg string) (string, error) { } func (t terminalClipboard) write(text, reg string) error { - return screen.Screen.SetClipboard(text, reg) + screen.Screen.SetClipboard([]byte(text)) + return nil } diff --git a/internal/config/colorscheme.go b/internal/config/colorscheme.go index eab0458b97..3b3ca73022 100644 --- a/internal/config/colorscheme.go +++ b/internal/config/colorscheme.go @@ -6,7 +6,7 @@ import ( "strconv" "strings" - "github.com/micro-editor/tcell/v2" + "github.com/gdamore/tcell/v3" ) // DefStyle is Micro's default style @@ -176,19 +176,19 @@ func StringToStyle(str string) tcell.Style { var fgColor, bgColor tcell.Color var ok bool if fg == "" || fg == "default" { - fgColor, _, _ = DefStyle.Decompose() + fgColor = DefStyle.GetForeground() } else { fgColor, ok = StringToColor(fg) if !ok { - fgColor, _, _ = DefStyle.Decompose() + fgColor = DefStyle.GetForeground() } } if bg == "" || bg == "default" { - _, bgColor, _ = DefStyle.Decompose() + bgColor = DefStyle.GetBackground() } else { bgColor, ok = StringToColor(bg) if !ok { - _, bgColor, _ = DefStyle.Decompose() + bgColor = DefStyle.GetBackground() } } diff --git a/internal/config/colorscheme_test.go b/internal/config/colorscheme_test.go index 25165703df..5f1ea383ad 100644 --- a/internal/config/colorscheme_test.go +++ b/internal/config/colorscheme_test.go @@ -3,14 +3,15 @@ package config import ( "testing" - "github.com/micro-editor/tcell/v2" + "github.com/gdamore/tcell/v3" "github.com/stretchr/testify/assert" ) func TestSimpleStringToStyle(t *testing.T) { s := StringToStyle("lightblue,magenta") - fg, bg, _ := s.Decompose() + fg := s.GetForeground() + bg := s.GetBackground() assert.Equal(t, tcell.ColorBlue, fg) assert.Equal(t, tcell.ColorPurple, bg) @@ -19,7 +20,9 @@ func TestSimpleStringToStyle(t *testing.T) { func TestAttributeStringToStyle(t *testing.T) { s := StringToStyle("bold cyan,brightcyan") - fg, bg, attr := s.Decompose() + fg := s.GetForeground() + bg := s.GetBackground() + attr := s.GetAttributes() assert.Equal(t, tcell.ColorTeal, fg) assert.Equal(t, tcell.ColorAqua, bg) @@ -29,7 +32,10 @@ func TestAttributeStringToStyle(t *testing.T) { func TestMultiAttributesStringToStyle(t *testing.T) { s := StringToStyle("bold blink dim italic reverse strikethrough underline cyan,brightcyan") - fg, bg, attr := s.Decompose() + fg := s.GetForeground() + bg := s.GetBackground() + attr := s.GetAttributes() + ul := s.GetUnderlineStyle() assert.Equal(t, tcell.ColorTeal, fg) assert.Equal(t, tcell.ColorAqua, bg) @@ -38,15 +44,16 @@ func TestMultiAttributesStringToStyle(t *testing.T) { tcell.AttrDim| tcell.AttrItalic| tcell.AttrReverse| - tcell.AttrStrikeThrough| - tcell.AttrUnderline, + tcell.AttrStrikeThrough, attr) + assert.Equal(t, tcell.UnderlineStyleSolid, ul) } func TestColor256StringToStyle(t *testing.T) { s := StringToStyle("128,60") - fg, bg, _ := s.Decompose() + fg := s.GetForeground() + bg := s.GetBackground() assert.Equal(t, tcell.Color128, fg) assert.Equal(t, tcell.Color60, bg) @@ -55,7 +62,8 @@ func TestColor256StringToStyle(t *testing.T) { func TestColorHexStringToStyle(t *testing.T) { s := StringToStyle("#deadbe,#ef1234") - fg, bg, _ := s.Decompose() + fg := s.GetForeground() + bg := s.GetBackground() assert.Equal(t, tcell.NewRGBColor(222, 173, 190), fg) assert.Equal(t, tcell.NewRGBColor(239, 18, 52), bg) @@ -73,7 +81,9 @@ color-link constant.string.char "#BDE6AD,#282828"` c, err := ParseColorscheme("testColorscheme", testColorscheme, nil) assert.Nil(t, err) - fg, bg, _ := c["comment"].Decompose() + fg := c["comment"].GetForeground() + bg := c["comment"].GetBackground() + assert.Equal(t, tcell.NewRGBColor(117, 113, 94), fg) assert.Equal(t, tcell.NewRGBColor(40, 40, 40), bg) } diff --git a/internal/display/bufwindow.go b/internal/display/bufwindow.go index ddbb044c7b..8515f56ffc 100644 --- a/internal/display/bufwindow.go +++ b/internal/display/bufwindow.go @@ -4,12 +4,12 @@ import ( "strconv" "strings" + "github.com/gdamore/tcell/v3" runewidth "github.com/mattn/go-runewidth" "github.com/micro-editor/micro/v2/internal/buffer" "github.com/micro-editor/micro/v2/internal/config" "github.com/micro-editor/micro/v2/internal/screen" "github.com/micro-editor/micro/v2/internal/util" - "github.com/micro-editor/tcell/v2" ) // The BufWindow provides a way of displaying a certain section of a buffer. @@ -319,7 +319,7 @@ func (w *BufWindow) drawDiffGutter(backgroundStyle tcell.Style, softwrapped bool style := backgroundStyle if s, ok := config.Colorscheme[styleName]; ok { - foreground, _, _ := s.Decompose() + foreground := s.GetForeground() style = style.Foreground(foreground) } @@ -568,7 +568,7 @@ func (w *BufWindow) displayBuffer() { } if s, ok := config.Colorscheme["indent-char"]; ok { - fg, _, _ := s.Decompose() + fg := s.GetForeground() style = style.Foreground(fg) } @@ -576,11 +576,11 @@ func (w *BufWindow) displayBuffer() { if b.Settings["hltaberrors"].(bool) && bloc.X < leadingwsEnd { if s, ok := config.Colorscheme["tab-error"]; ok { if b.Settings["tabstospaces"].(bool) && r == '\t' { - fg, _, _ := s.Decompose() + fg := s.GetForeground() style = style.Background(fg) preservebg = true } else if !b.Settings["tabstospaces"].(bool) && r == ' ' { - fg, _, _ := s.Decompose() + fg := s.GetForeground() style = style.Background(fg) preservebg = true } @@ -598,7 +598,7 @@ func (w *BufWindow) displayBuffer() { } } if hl { - fg, _, _ := s.Decompose() + fg := s.GetForeground() style = style.Background(fg) preservebg = true } @@ -629,8 +629,8 @@ func (w *BufWindow) displayBuffer() { } } - _, origBg, _ := style.Decompose() - _, defBg, _ := config.DefStyle.Decompose() + origBg := style.GetBackground() + defBg := config.DefStyle.GetBackground() // syntax or hlsearch highlighting with non-default background takes precedence // over cursor-line and color-column @@ -653,7 +653,7 @@ func (w *BufWindow) displayBuffer() { if b.Settings["cursorline"].(bool) && w.active && !preservebg && !c.HasSelection() && c.Y == bloc.Y { if s, ok := config.Colorscheme["cursor-line"]; ok { - fg, _, _ := s.Decompose() + fg := s.GetForeground() style = style.Background(fg) } } @@ -669,7 +669,7 @@ func (w *BufWindow) displayBuffer() { if s, ok := config.Colorscheme["color-column"]; ok { if colorcolumn != 0 && vloc.X-w.gutterOffset+w.StartCol == colorcolumn && !preservebg { - fg, _, _ := s.Decompose() + fg := s.GetForeground() style = style.Background(fg) } } @@ -809,7 +809,7 @@ func (w *BufWindow) displayBuffer() { if b.Settings["cursorline"].(bool) && w.active && !c.HasSelection() && c.Y == bloc.Y { if s, ok := config.Colorscheme["cursor-line"]; ok { - fg, _, _ := s.Decompose() + fg := s.GetForeground() style = style.Background(fg) } } @@ -818,7 +818,7 @@ func (w *BufWindow) displayBuffer() { curStyle := style if s, ok := config.Colorscheme["color-column"]; ok { if colorcolumn != 0 && i-w.gutterOffset+w.StartCol == colorcolumn { - fg, _, _ := s.Decompose() + fg := s.GetForeground() curStyle = style.Background(fg) } } diff --git a/internal/display/infowindow.go b/internal/display/infowindow.go index 39ca72778a..4d8e0d1734 100644 --- a/internal/display/infowindow.go +++ b/internal/display/infowindow.go @@ -1,13 +1,13 @@ package display import ( + "github.com/gdamore/tcell/v3" runewidth "github.com/mattn/go-runewidth" "github.com/micro-editor/micro/v2/internal/buffer" "github.com/micro-editor/micro/v2/internal/config" "github.com/micro-editor/micro/v2/internal/info" "github.com/micro-editor/micro/v2/internal/screen" "github.com/micro-editor/micro/v2/internal/util" - "github.com/micro-editor/tcell/v2" ) type InfoWindow struct { diff --git a/internal/display/tabwindow.go b/internal/display/tabwindow.go index ad57963baf..abb3ffa479 100644 --- a/internal/display/tabwindow.go +++ b/internal/display/tabwindow.go @@ -1,12 +1,12 @@ package display import ( + "github.com/gdamore/tcell/v3" runewidth "github.com/mattn/go-runewidth" "github.com/micro-editor/micro/v2/internal/buffer" "github.com/micro-editor/micro/v2/internal/config" "github.com/micro-editor/micro/v2/internal/screen" "github.com/micro-editor/micro/v2/internal/util" - "github.com/micro-editor/tcell/v2" ) type TabWindow struct { diff --git a/internal/display/termwindow.go b/internal/display/termwindow.go index 0dcea5a857..06f0ae63c2 100644 --- a/internal/display/termwindow.go +++ b/internal/display/termwindow.go @@ -1,12 +1,12 @@ package display import ( + "github.com/gdamore/tcell/v3" "github.com/micro-editor/micro/v2/internal/buffer" "github.com/micro-editor/micro/v2/internal/config" "github.com/micro-editor/micro/v2/internal/screen" "github.com/micro-editor/micro/v2/internal/shell" "github.com/micro-editor/micro/v2/internal/util" - "github.com/micro-editor/tcell/v2" "github.com/micro-editor/terminal" ) diff --git a/internal/screen/screen.go b/internal/screen/screen.go index 4b2c564249..a6a6224751 100644 --- a/internal/screen/screen.go +++ b/internal/screen/screen.go @@ -6,8 +6,8 @@ import ( "os" "sync" + "github.com/gdamore/tcell/v3" "github.com/micro-editor/micro/v2/internal/config" - "github.com/micro-editor/tcell/v2" ) // Screen is the tcell screen we use to draw to the terminal From 1cd4ec2254cbcf9db3cc3b95a346b76a2dbf018e Mon Sep 17 00:00:00 2001 From: Tubbles Date: Sat, 18 Apr 2026 16:49:49 +0200 Subject: [PATCH 2/9] screen: adapt to tcell v3 Several small API adaptations in the screen wrapper: - Screen.SetPaste(bool) is replaced by Screen.EnablePaste() and Screen.DisablePaste(). - Screen.CanDisplay is no longer exposed; drop the pass-through. - Screen.RegisterRawSeq / UnregisterRawSeq are gone. The v3 input processor handles the CSI-u keyboard protocol natively, covering the cases raw-seq registration was used for. - Add InitMockScreen helper for tests that migrated from the removed SimulationScreen to vt.MockTerm. Assisted-by: Claude:claude-opus-4-7 --- internal/screen/screen.go | 108 ++++++++++++++++++-------------------- 1 file changed, 50 insertions(+), 58 deletions(-) diff --git a/internal/screen/screen.go b/internal/screen/screen.go index a6a6224751..2e3ce34ea4 100644 --- a/internal/screen/screen.go +++ b/internal/screen/screen.go @@ -1,12 +1,12 @@ package screen import ( - "errors" "log" "os" "sync" "github.com/gdamore/tcell/v3" + "github.com/gdamore/tcell/v3/vt" "github.com/micro-editor/micro/v2/internal/config" ) @@ -18,8 +18,9 @@ import ( // same time too. var Screen tcell.Screen -// Events is the channel of tcell events -var Events chan (tcell.Event) +// Events is the channel of tcell events. It is an alias for the tcell +// Screen.EventQ() channel, populated during Init(). +var Events chan tcell.Event // RestartCallback is called when the screen is restarted after it was // temporarily shut down @@ -32,12 +33,6 @@ var lock sync.Mutex // written to even if no event user event has occurred var drawChan chan bool -// rawSeq is the list of raw escape sequences that are bound to some actions -// via keybindings and thus should be parsed by tcell. We need to register -// them in tcell every time we reinitialize the screen, so we need to remember -// them in a list -var rawSeq = make([]string, 0) - // Lock locks the screen lock func Lock() { lock.Lock() @@ -71,6 +66,21 @@ type screenCell struct { var lastCursor screenCell +// splitGrapheme splits a tcell v3 grapheme-cluster string into a +// (primary rune, combining runes) pair for the Screen.SetContent API, +// which still takes a rune + []rune. +func splitGrapheme(s string) (rune, []rune) { + rs := []rune(s) + switch len(rs) { + case 0: + return ' ', nil + case 1: + return rs[0], nil + default: + return rs[0], rs[1:] + } +} + // ShowFakeCursor displays a cursor at the given position by modifying the // style of the given column instead of actually using the terminal cursor // This can be useful in certain terminals such as the windows console where @@ -78,7 +88,8 @@ var lastCursor screenCell // This keeps track of the most recent fake cursor location and resets it when // a new fake cursor location is specified func ShowFakeCursor(x, y int) { - r, combc, style, _ := Screen.GetContent(x, y) + s, style, _ := Screen.Get(x, y) + r, combc := splitGrapheme(s) Screen.SetContent(lastCursor.x, lastCursor.y, lastCursor.r, lastCursor.combc, lastCursor.style) Screen.SetContent(x, y, r, combc, config.DefStyle.Reverse(true)) @@ -96,7 +107,8 @@ func UseFake() bool { // reset previous locations of the cursor // Fake cursors are also necessary to display multiple cursors func ShowFakeCursorMulti(x, y int) { - r, _, _, _ := Screen.GetContent(x, y) + s, _, _ := Screen.Get(x, y) + r, _ := splitGrapheme(s) Screen.SetContent(x, y, r, nil, config.DefStyle.Reverse(true)) } @@ -114,10 +126,6 @@ func ShowCursor(x, y int) { // SetContent sets a cell at a point on the screen and makes sure that it is // synced with the last cursor location func SetContent(x, y int, mainc rune, combc []rune, style tcell.Style) { - if !Screen.CanDisplay(mainc, true) { - mainc = '�' - } - Screen.SetContent(x, y, mainc, combc, style) if UseFake() && lastCursor.x == x && lastCursor.y == y { lastCursor.r = mainc @@ -126,34 +134,6 @@ func SetContent(x, y int, mainc rune, combc []rune, style tcell.Style) { } } -// RegisterRawSeq registers a raw escape sequence that should be parsed by tcell -func RegisterRawSeq(r string) { - for _, seq := range rawSeq { - if seq == r { - return - } - } - rawSeq = append(rawSeq, r) - - if Screen != nil { - Screen.RegisterRawSeq(r) - } -} - -// UnregisterRawSeq unregisters a raw escape sequence that should be parsed by tcell -func UnregisterRawSeq(r string) { - for i, seq := range rawSeq { - if seq == r { - rawSeq[i] = rawSeq[len(rawSeq)-1] - rawSeq = rawSeq[:len(rawSeq)-1] - } - } - - if Screen != nil { - Screen.UnregisterRawSeq(r) - } -} - // TempFini shuts the screen down temporarily func TempFini() bool { screenWasNil := Screen == nil @@ -220,7 +200,11 @@ func Init() error { return err } - Screen.SetPaste(config.GetGlobalOption("paste").(bool)) + if config.GetGlobalOption("paste").(bool) { + Screen.EnablePaste() + } else { + Screen.DisablePaste() + } // restore TERM if modifiedTerm { @@ -231,33 +215,41 @@ func Init() error { Screen.EnableMouse() } - for _, r := range rawSeq { - Screen.RegisterRawSeq(r) - } + Events = Screen.EventQ() return nil } -// InitSimScreen initializes a simulation screen for testing purposes -func InitSimScreen() (tcell.SimulationScreen, error) { +// InitMockScreen initializes a terminfo-backed tcell.Screen wired to a +// vt.MockTerm for use in tests. The returned MockTerm is the test-side +// handle for injecting keys/mouse/raw-bytes; the tcell.Screen is +// installed as screen.Screen and its EventQ() is aliased to +// screen.Events, so the rest of micro drives it unchanged. +// +// Uses OptTerm("xterm-256color") to pin terminfo lookup rather than +// relying on $TERM, which may be unset or "dumb" in CI. +// +// This replaces the v2-era InitSimScreen helper: tcell v3 removed +// SimulationScreen, and vt.MockTerm is the blessed upstream mock. +// Note that the vt package docstring flags its API as still under +// development, so test-only use is deliberate. +func InitMockScreen() (vt.MockTerm, error) { drawChan = make(chan bool, 8) - // Initilize tcell - var err error - s := tcell.NewSimulationScreen("") - if s == nil { - return nil, errors.New("Failed to get a simulation screen") + mt := vt.NewMockTerm() + s, err := tcell.NewTerminfoScreenFromTty(mt, tcell.OptTerm("xterm-256color")) + if err != nil { + return nil, err } - if err = s.Init(); err != nil { + if err := s.Init(); err != nil { return nil, err } - - s.SetSize(80, 24) Screen = s + Events = Screen.EventQ() if config.GetGlobalOption("mouse").(bool) { Screen.EnableMouse() } - return s, nil + return mt, nil } From a1e493a437e4cd452ac9f071d7304d3855c8eae1 Mon Sep 17 00:00:00 2001 From: Tubbles Date: Sat, 18 Apr 2026 16:50:03 +0200 Subject: [PATCH 3/9] action: adapt to tcell v3 event model - EventKey.Rune() is replaced by Str(). Call sites take the first rune where a scalar is still required. Multi-rune grapheme clusters get truncated to their first rune on the keystroke path; the paste path handles wide input via accumulation (below). - NewEventKey takes a string instead of a rune. - EventPaste no longer carries payload text. Between Start and End the v3 input processor streams EventKey events, so bufpane accumulates them into a buffer and replays the whole block through paste() on End. This preserves single-undo and smartpaste behaviour for bracketed pastes and prevents plugin-driven autoclose etc. from firing per-character. - command.go: SetPaste(bool) call is split into EnablePaste / DisablePaste. - events.go: drop the *EventRaw branch from ConstructEvent and eventEscSeq. CSI-u covers the original use case natively. - rawpane: the EscSeq accessor is no longer a method on the event type; use the internal eventEscSeq helper. - bindings.go: tcell v3 dropped the KeyCtrlSpace / KeyCtrlLeftSq / KeyCtrlBackslash / KeyCtrlRightSq / KeyCtrlCarat / KeyCtrlUnderscore constants, and its CSI-u disambiguate path delivers Ctrl+Shift+letter, Ctrl+Alt+letter, and Ctrl+non-letter as KeyRune+str+mod rather than the KeyCtrlX form. Add ctrlNameAliases for the long-form names, gate the KeyCtrlX return on mod == ModCtrl exactly to mirror tcell's NewEventKey fold (key.go:282-291), and lowercase the rune in the single-character emission so the unshifted base kitty sends matches bindings written as CtrlShiftP etc. Plain Ctrl+letter keeps the legacy KeyCtrlX path. CtrlLeftSq parses but does not fire because tcell consumes 0x1B as the start of an escape sequence, matching v2 behaviour. - tcell v3 drops KeyBackspace2 and only populates KeyBackspace Assisted-by: Claude:claude-opus-4-7 action tcell --- internal/action/actions.go | 2 +- internal/action/bindings.go | 320 ++++++++++++++++++++---------------- internal/action/bufpane.go | 68 ++++++-- internal/action/command.go | 6 +- internal/action/events.go | 26 +-- internal/action/infopane.go | 7 +- internal/action/rawpane.go | 2 +- 7 files changed, 256 insertions(+), 175 deletions(-) diff --git a/internal/action/actions.go b/internal/action/actions.go index 796584d2eb..79e950f34b 100644 --- a/internal/action/actions.go +++ b/internal/action/actions.go @@ -2110,7 +2110,7 @@ func (h *BufPane) PlayMacro() bool { } for _, action := range curmacro { switch t := action.(type) { - case rune: + case string: h.DoRuneInsert(t) case BufKeyAction: t(h) diff --git a/internal/action/bindings.go b/internal/action/bindings.go index 2291cfe183..3977700a87 100644 --- a/internal/action/bindings.go +++ b/internal/action/bindings.go @@ -93,10 +93,6 @@ func BindKey(k, v string, bind func(e Event, a string)) { return } - if strings.HasPrefix(k, "\x1b") { - screen.RegisterRawSeq(k) - } - bind(event, v) // switch e := event.(type) { @@ -174,17 +170,32 @@ modSearch: return KeyEvent{}, false } - // Control is handled in a special way, since the terminal sends explicitly - // marked escape sequences for control keys - // We should check for Control keys first + // When Ctrl is one of the modifiers, two things to handle: + // 1. The historical CtrlSpace / CtrlLeftSq / CtrlBackslash / + // CtrlRightSq / CtrlCarat / CtrlUnderscore aliases. tcell v3 + // dropped the matching Key constants, so route them through + // ctrlNameAliases to the rune+mod path below. + // 2. tcell's NewEventKey only folds {KeyRune, letter, ModCtrl} + // back to a KeyCtrlX constant when mod == ModCtrl exactly + // (key.go:282-291). So return the KeyCtrlX form only in that + // case; for Ctrl+Shift+letter / Ctrl+Alt+letter / Ctrl+rune, + // kitty CSI-u and the legacy paths both deliver the event as + // {KeyRune, str, mod} and we fall through to the rune branch. if modifiers&tcell.ModCtrl != 0 { - // see if the key is in bindingKeys with the Ctrl prefix. - k = string(unicode.ToUpper(rune(k[0]))) + k[1:] - if code, ok := keyEvents["Ctrl"+k]; ok { - return KeyEvent{ - code: code, - mod: modifiers, - }, true + if r, ok := ctrlNameAliases[k]; ok { + k = string(r) + } else { + // Allow Ctrl-q / Ctrl-Q / Ctrl-up / Ctrl-Up to be written + // either way. + k = string(unicode.ToUpper(rune(k[0]))) + k[1:] + } + if modifiers == tcell.ModCtrl { + if code, ok := keyEvents["Ctrl"+k]; ok { + return KeyEvent{ + code: code, + mod: modifiers, + }, true + } } } @@ -215,10 +226,19 @@ modSearch: // If we were given one character, then we've got a rune. if len(k) == 1 { + str := k + // Both kitty's CSI-u disambiguate path (which encodes the + // unshifted base keycode + modifier mask) and tcell's legacy + // byte-path normalisation in NewEventKey deliver lowercase + // letters when Ctrl is held. Lowercase the bound rune so + // CtrlShiftP matches the "p" that kitty actually sends. + if modifiers&tcell.ModCtrl != 0 { + str = strings.ToLower(str) + } return KeyEvent{ code: tcell.KeyRune, mod: modifiers, - r: rune(k[0]), + str: str, }, true } @@ -361,10 +381,6 @@ func UnbindKey(k string) error { } } - if strings.HasPrefix(k, "\x1b") { - screen.UnregisterRawSeq(k) - } - defaults := DefaultBindings("buffer") if a, ok := defaults[k]; ok { BindKey(k, a, Binder["buffer"]) @@ -380,6 +396,23 @@ func UnbindKey(k string) error { return e } +// ctrlNameAliases lets bindings.json keep using the historical long +// names (CtrlSpace, CtrlUnderscore, ...) that tcell v3 dropped as +// dedicated Key constants. Inside the Ctrl branch of findSingleEvent +// these are rewritten to the corresponding rune so they fall through +// to the rune+mod emission path. +// +// Note: CtrlLeftSq parses but never fires, because tcell consumes +// 0x1B as the start of an escape sequence. That matches v2 behaviour. +var ctrlNameAliases = map[string]rune{ + "Space": ' ', + "LeftSq": '[', + "Backslash": '\\', + "RightSq": ']', + "Carat": '^', + "Underscore": '_', +} + var mouseEvents = map[string]tcell.ButtonMask{ "MouseLeft": tcell.ButtonPrimary, "MouseMiddle": tcell.ButtonMiddle, @@ -391,132 +424,133 @@ var mouseEvents = map[string]tcell.ButtonMask{ } var keyEvents = map[string]tcell.Key{ - "Up": tcell.KeyUp, - "Down": tcell.KeyDown, - "Right": tcell.KeyRight, - "Left": tcell.KeyLeft, - "UpLeft": tcell.KeyUpLeft, - "UpRight": tcell.KeyUpRight, - "DownLeft": tcell.KeyDownLeft, - "DownRight": tcell.KeyDownRight, - "Center": tcell.KeyCenter, - "PageUp": tcell.KeyPgUp, - "PageDown": tcell.KeyPgDn, - "Home": tcell.KeyHome, - "End": tcell.KeyEnd, - "Insert": tcell.KeyInsert, - "Delete": tcell.KeyDelete, - "Help": tcell.KeyHelp, - "Exit": tcell.KeyExit, - "Clear": tcell.KeyClear, - "Cancel": tcell.KeyCancel, - "Print": tcell.KeyPrint, - "Pause": tcell.KeyPause, - "Backtab": tcell.KeyBacktab, - "F1": tcell.KeyF1, - "F2": tcell.KeyF2, - "F3": tcell.KeyF3, - "F4": tcell.KeyF4, - "F5": tcell.KeyF5, - "F6": tcell.KeyF6, - "F7": tcell.KeyF7, - "F8": tcell.KeyF8, - "F9": tcell.KeyF9, - "F10": tcell.KeyF10, - "F11": tcell.KeyF11, - "F12": tcell.KeyF12, - "F13": tcell.KeyF13, - "F14": tcell.KeyF14, - "F15": tcell.KeyF15, - "F16": tcell.KeyF16, - "F17": tcell.KeyF17, - "F18": tcell.KeyF18, - "F19": tcell.KeyF19, - "F20": tcell.KeyF20, - "F21": tcell.KeyF21, - "F22": tcell.KeyF22, - "F23": tcell.KeyF23, - "F24": tcell.KeyF24, - "F25": tcell.KeyF25, - "F26": tcell.KeyF26, - "F27": tcell.KeyF27, - "F28": tcell.KeyF28, - "F29": tcell.KeyF29, - "F30": tcell.KeyF30, - "F31": tcell.KeyF31, - "F32": tcell.KeyF32, - "F33": tcell.KeyF33, - "F34": tcell.KeyF34, - "F35": tcell.KeyF35, - "F36": tcell.KeyF36, - "F37": tcell.KeyF37, - "F38": tcell.KeyF38, - "F39": tcell.KeyF39, - "F40": tcell.KeyF40, - "F41": tcell.KeyF41, - "F42": tcell.KeyF42, - "F43": tcell.KeyF43, - "F44": tcell.KeyF44, - "F45": tcell.KeyF45, - "F46": tcell.KeyF46, - "F47": tcell.KeyF47, - "F48": tcell.KeyF48, - "F49": tcell.KeyF49, - "F50": tcell.KeyF50, - "F51": tcell.KeyF51, - "F52": tcell.KeyF52, - "F53": tcell.KeyF53, - "F54": tcell.KeyF54, - "F55": tcell.KeyF55, - "F56": tcell.KeyF56, - "F57": tcell.KeyF57, - "F58": tcell.KeyF58, - "F59": tcell.KeyF59, - "F60": tcell.KeyF60, - "F61": tcell.KeyF61, - "F62": tcell.KeyF62, - "F63": tcell.KeyF63, - "F64": tcell.KeyF64, - "CtrlSpace": tcell.KeyCtrlSpace, - "CtrlA": tcell.KeyCtrlA, - "CtrlB": tcell.KeyCtrlB, - "CtrlC": tcell.KeyCtrlC, - "CtrlD": tcell.KeyCtrlD, - "CtrlE": tcell.KeyCtrlE, - "CtrlF": tcell.KeyCtrlF, - "CtrlG": tcell.KeyCtrlG, - "CtrlH": tcell.KeyCtrlH, - "CtrlI": tcell.KeyCtrlI, - "CtrlJ": tcell.KeyCtrlJ, - "CtrlK": tcell.KeyCtrlK, - "CtrlL": tcell.KeyCtrlL, - "CtrlM": tcell.KeyCtrlM, - "CtrlN": tcell.KeyCtrlN, - "CtrlO": tcell.KeyCtrlO, - "CtrlP": tcell.KeyCtrlP, - "CtrlQ": tcell.KeyCtrlQ, - "CtrlR": tcell.KeyCtrlR, - "CtrlS": tcell.KeyCtrlS, - "CtrlT": tcell.KeyCtrlT, - "CtrlU": tcell.KeyCtrlU, - "CtrlV": tcell.KeyCtrlV, - "CtrlW": tcell.KeyCtrlW, - "CtrlX": tcell.KeyCtrlX, - "CtrlY": tcell.KeyCtrlY, - "CtrlZ": tcell.KeyCtrlZ, - "CtrlLeftSq": tcell.KeyCtrlLeftSq, - "CtrlBackslash": tcell.KeyCtrlBackslash, - "CtrlRightSq": tcell.KeyCtrlRightSq, - "CtrlCarat": tcell.KeyCtrlCarat, - "CtrlUnderscore": tcell.KeyCtrlUnderscore, - "Tab": tcell.KeyTab, - "Esc": tcell.KeyEsc, - "Escape": tcell.KeyEscape, - "Enter": tcell.KeyEnter, - "Backspace": tcell.KeyBackspace2, - "OldBackspace": tcell.KeyBackspace, + "Up": tcell.KeyUp, + "Down": tcell.KeyDown, + "Right": tcell.KeyRight, + "Left": tcell.KeyLeft, + "UpLeft": tcell.KeyUpLeft, + "UpRight": tcell.KeyUpRight, + "DownLeft": tcell.KeyDownLeft, + "DownRight": tcell.KeyDownRight, + "Center": tcell.KeyCenter, + "PageUp": tcell.KeyPgUp, + "PageDown": tcell.KeyPgDn, + "Home": tcell.KeyHome, + "End": tcell.KeyEnd, + "Insert": tcell.KeyInsert, + "Delete": tcell.KeyDelete, + "Help": tcell.KeyHelp, + "Exit": tcell.KeyExit, + "Clear": tcell.KeyClear, + "Cancel": tcell.KeyCancel, + "Print": tcell.KeyPrint, + "Pause": tcell.KeyPause, + "Backtab": tcell.KeyBacktab, + "F1": tcell.KeyF1, + "F2": tcell.KeyF2, + "F3": tcell.KeyF3, + "F4": tcell.KeyF4, + "F5": tcell.KeyF5, + "F6": tcell.KeyF6, + "F7": tcell.KeyF7, + "F8": tcell.KeyF8, + "F9": tcell.KeyF9, + "F10": tcell.KeyF10, + "F11": tcell.KeyF11, + "F12": tcell.KeyF12, + "F13": tcell.KeyF13, + "F14": tcell.KeyF14, + "F15": tcell.KeyF15, + "F16": tcell.KeyF16, + "F17": tcell.KeyF17, + "F18": tcell.KeyF18, + "F19": tcell.KeyF19, + "F20": tcell.KeyF20, + "F21": tcell.KeyF21, + "F22": tcell.KeyF22, + "F23": tcell.KeyF23, + "F24": tcell.KeyF24, + "F25": tcell.KeyF25, + "F26": tcell.KeyF26, + "F27": tcell.KeyF27, + "F28": tcell.KeyF28, + "F29": tcell.KeyF29, + "F30": tcell.KeyF30, + "F31": tcell.KeyF31, + "F32": tcell.KeyF32, + "F33": tcell.KeyF33, + "F34": tcell.KeyF34, + "F35": tcell.KeyF35, + "F36": tcell.KeyF36, + "F37": tcell.KeyF37, + "F38": tcell.KeyF38, + "F39": tcell.KeyF39, + "F40": tcell.KeyF40, + "F41": tcell.KeyF41, + "F42": tcell.KeyF42, + "F43": tcell.KeyF43, + "F44": tcell.KeyF44, + "F45": tcell.KeyF45, + "F46": tcell.KeyF46, + "F47": tcell.KeyF47, + "F48": tcell.KeyF48, + "F49": tcell.KeyF49, + "F50": tcell.KeyF50, + "F51": tcell.KeyF51, + "F52": tcell.KeyF52, + "F53": tcell.KeyF53, + "F54": tcell.KeyF54, + "F55": tcell.KeyF55, + "F56": tcell.KeyF56, + "F57": tcell.KeyF57, + "F58": tcell.KeyF58, + "F59": tcell.KeyF59, + "F60": tcell.KeyF60, + "F61": tcell.KeyF61, + "F62": tcell.KeyF62, + "F63": tcell.KeyF63, + "F64": tcell.KeyF64, + // CtrlSpace / CtrlLeftSq / CtrlBackslash / CtrlRightSq / CtrlCarat / + // CtrlUnderscore are intentionally absent: tcell v3 dropped the + // matching Key constants. findSingleEvent translates those names + // via ctrlNameAliases (above) and emits {KeyRune, char, ModCtrl} + // instead, which is the form tcell actually delivers under v3. + "CtrlA": tcell.KeyCtrlA, + "CtrlB": tcell.KeyCtrlB, + "CtrlC": tcell.KeyCtrlC, + "CtrlD": tcell.KeyCtrlD, + "CtrlE": tcell.KeyCtrlE, + "CtrlF": tcell.KeyCtrlF, + "CtrlG": tcell.KeyCtrlG, + "CtrlH": tcell.KeyCtrlH, + "CtrlI": tcell.KeyCtrlI, + "CtrlJ": tcell.KeyCtrlJ, + "CtrlK": tcell.KeyCtrlK, + "CtrlL": tcell.KeyCtrlL, + "CtrlM": tcell.KeyCtrlM, + "CtrlN": tcell.KeyCtrlN, + "CtrlO": tcell.KeyCtrlO, + "CtrlP": tcell.KeyCtrlP, + "CtrlQ": tcell.KeyCtrlQ, + "CtrlR": tcell.KeyCtrlR, + "CtrlS": tcell.KeyCtrlS, + "CtrlT": tcell.KeyCtrlT, + "CtrlU": tcell.KeyCtrlU, + "CtrlV": tcell.KeyCtrlV, + "CtrlW": tcell.KeyCtrlW, + "CtrlX": tcell.KeyCtrlX, + "CtrlY": tcell.KeyCtrlY, + "CtrlZ": tcell.KeyCtrlZ, + "Tab": tcell.KeyTab, + "Esc": tcell.KeyEsc, + "Escape": tcell.KeyEscape, + "Enter": tcell.KeyEnter, + // tcell v3 treats KeyBackspace2 as KeyBackspace + "Backspace": tcell.KeyBackspace, + "OldBackspace": tcell.KeyBackspace, // I renamed these keys to PageUp and PageDown but I don't want to break someone's keybindings "PgUp": tcell.KeyPgUp, "PgDown": tcell.KeyPgDn, + "PgDn": tcell.KeyPgDn, } diff --git a/internal/action/bufpane.go b/internal/action/bufpane.go index d8906d9cb7..099bae6bf7 100644 --- a/internal/action/bufpane.go +++ b/internal/action/bufpane.go @@ -254,6 +254,14 @@ type BufPane struct { // since we may not know the window geometry yet. In such case we finish // its initialization a bit later, after the initial resize. initialized bool + + // pasteBuf accumulates key events that arrive between + // EventPaste{Start} and EventPaste{End}. tcell v3 no longer + // delivers the pasted payload as a single EventPaste.Text(); + // keys arrive as intervening EventKey values and we reassemble + // them into a single clipboard-style insert. + pasteBuf strings.Builder + inPaste bool } func newBufPane(buf *buffer.Buffer, win display.BWindow, tab *Tab) *BufPane { @@ -450,20 +458,46 @@ func (h *BufPane) HandleEvent(event tcell.Event) { } switch e := event.(type) { - case *tcell.EventRaw: - re := RawEvent{ - esc: e.EscSeq(), - } - h.DoKeyEvent(re) case *tcell.EventPaste: - h.paste(e.Text()) - h.Relocate() + if e.Start() { + h.pasteBuf.Reset() + h.inPaste = true + } else { + text := h.pasteBuf.String() + h.pasteBuf.Reset() + h.inPaste = false + if text != "" { + h.paste(text) + h.Relocate() + } + } case *tcell.EventKey: + // While a bracketed paste is in flight, tcell v3 delivers + // the payload as a stream of EventKey between EventPaste + // Start/End. Accumulate rather than dispatching so plugin + // hooks and macro recording fire once at End, not per key. + // + // tcell v3 promotes control bytes to dedicated Key codes + // with empty Str. Map only the two that realistically + // appear in clipboard text (TAB, LF) back to their + // source bytes; everything else is dropped silently so odd + // control chars can't sneak into the buffer. + if h.inPaste { + switch e.Key() { + case tcell.KeyTab: + h.pasteBuf.WriteByte('\t') + case tcell.KeyEnter: + h.pasteBuf.WriteByte('\n') + default: + h.pasteBuf.WriteString(e.Str()) + } + break + } ke := keyEvent(e) done := h.DoKeyEvent(ke) if !done && e.Key() == tcell.KeyRune { - h.DoRuneInsert(e.Rune()) + h.DoRuneInsert(e.Str()) } case *tcell.EventMouse: if e.Buttons() != tcell.ButtonNone { @@ -616,15 +650,17 @@ func (h *BufPane) DoMouseEvent(e MouseEvent, te *tcell.EventMouse) bool { // return false } -// DoRuneInsert inserts a given rune into the current buffer -// (possibly multiple times for multiple cursors) -func (h *BufPane) DoRuneInsert(r rune) { +// DoRuneInsert inserts the given grapheme-cluster string into the +// current buffer (possibly multiple times for multiple cursors). +// In tcell v3 each EventKey carries a grapheme cluster via Str(), +// so the input width is a string rather than a single rune. +func (h *BufPane) DoRuneInsert(s string) { cursors := h.Buf.GetCursors() for _, c := range cursors { // Insert a character h.Buf.SetCurCursor(c.Num) h.Cursor = c - if !h.PluginCB("preRune", string(r)) { + if !h.PluginCB("preRune", s) { continue } if c.HasSelection() { @@ -635,15 +671,15 @@ func (h *BufPane) DoRuneInsert(r rune) { if h.Buf.OverwriteMode { next := c.Loc next.X++ - h.Buf.Replace(c.Loc, next, string(r)) + h.Buf.Replace(c.Loc, next, s) } else { - h.Buf.Insert(c.Loc, string(r)) + h.Buf.Insert(c.Loc, s) } if recordingMacro { - curmacro = append(curmacro, r) + curmacro = append(curmacro, s) } h.Relocate() - h.PluginCB("onRune", string(r)) + h.PluginCB("onRune", s) } } diff --git a/internal/action/command.go b/internal/action/command.go index cd97c5222d..b646df33c8 100644 --- a/internal/action/command.go +++ b/internal/action/command.go @@ -602,7 +602,11 @@ func doSetGlobalOptionNative(option string, nativeValue any) error { config.SetAutoTime(0) } } else if option == "paste" { - screen.Screen.SetPaste(nativeValue.(bool)) + if nativeValue.(bool) { + screen.Screen.EnablePaste() + } else { + screen.Screen.DisablePaste() + } } else if option == "clipboard" { m := clipboard.SetMethod(nativeValue.(string)) err := clipboard.Initialize(m) diff --git a/internal/action/events.go b/internal/action/events.go index 862c835d18..7bf9173dd0 100644 --- a/internal/action/events.go +++ b/internal/action/events.go @@ -26,13 +26,13 @@ func (r RawEvent) Name() string { // KeyEvent is a key event containing a key code, // some possible modifiers (alt, ctrl, etc...) and -// a rune if it was simply a character press -// Note: to be compatible with tcell events, -// for ctrl keys r=code +// a grapheme-cluster string if it was simply a character press +// Note: to be compatible with tcell events, for ctrl keys str +// holds the rune-derived string. type KeyEvent struct { code tcell.Key mod tcell.ModMask - r rune + str string any bool } @@ -50,7 +50,7 @@ func keyEvent(e *tcell.EventKey) KeyEvent { mod: metaToAlt(e.Modifiers()), } if e.Key() == tcell.KeyRune { - ke.r = e.Rune() + ke.str = e.Str() } return ke } @@ -77,7 +77,7 @@ func (k KeyEvent) Name() string { ok := false if s, ok = tcell.KeyNames[k.code]; !ok { if k.code == tcell.KeyRune { - s = string(k.r) + s = k.str } else { s = fmt.Sprintf("Key[%d]", k.code) } @@ -158,6 +158,16 @@ func (m MouseEvent) Name() string { return "" } +// eventEscSeq returns the raw escape sequence associated with a tcell +// event, if any. Only EventKey carries source bytes through the +// tcell v3 EscSeq overlay. +func eventEscSeq(event tcell.Event) string { + if e, ok := event.(*tcell.EventKey); ok { + return e.EscSeq() + } + return "" +} + // ConstructEvent takes a tcell event and returns a micro // event. Note that tcell events can't express certain // micro events such as key sequences. This function is @@ -167,10 +177,6 @@ func ConstructEvent(event tcell.Event) (Event, error) { switch e := event.(type) { case *tcell.EventKey: return keyEvent(e), nil - case *tcell.EventRaw: - return RawEvent{ - esc: e.EscSeq(), - }, nil case *tcell.EventMouse: return MouseEvent{ btn: e.Buttons(), diff --git a/internal/action/infopane.go b/internal/action/infopane.go index d641221100..a70d7cc45a 100644 --- a/internal/action/infopane.go +++ b/internal/action/infopane.go @@ -91,8 +91,9 @@ func (h *InfoPane) HandleEvent(event tcell.Event) { done := h.DoKeyEvent(ke) hasYN := h.HasYN if e.Key() == tcell.KeyRune && hasYN { - y := e.Rune() == 'y' || e.Rune() == 'Y' - n := e.Rune() == 'n' || e.Rune() == 'N' + s := e.Str() + y := s == "y" || s == "Y" + n := s == "n" || s == "N" if y || n { h.YNResp = y h.DonePrompt(false) @@ -102,7 +103,7 @@ func (h *InfoPane) HandleEvent(event tcell.Event) { } } if e.Key() == tcell.KeyRune && !done && !hasYN { - h.DoRuneInsert(e.Rune()) + h.DoRuneInsert(e.Str()) done = true } if done && h.HasPrompt && !hasYN { diff --git a/internal/action/rawpane.go b/internal/action/rawpane.go index 1242b77f80..6658714bc3 100644 --- a/internal/action/rawpane.go +++ b/internal/action/rawpane.go @@ -44,7 +44,7 @@ func (h *RawPane) HandleEvent(event tcell.Event) { h.Buf.Insert(h.Cursor.Loc, fmt.Sprintf(": %s", e.Name())) } - h.Buf.Insert(h.Cursor.Loc, fmt.Sprintf(": %q\n", event.EscSeq())) + h.Buf.Insert(h.Cursor.Loc, fmt.Sprintf(": %q\n", eventEscSeq(event))) h.Relocate() } From 569184cade52df6436b96ec3a910670b6a37eb9b Mon Sep 17 00:00:00 2001 From: Tubbles Date: Sat, 18 Apr 2026 16:50:12 +0200 Subject: [PATCH 4/9] main: adapt event loop to tcell v3 - tcell v3 exposes an EventQ() channel directly. The PollEvent goroutine micro used to plumb events into its own channel is no longer needed; select on Screen.EventQ() alongside the existing timer / redraw channels. - EventError.Err() is gone. Use errors.Is(e, io.EOF) to distinguish clean EOF from real errors. Assisted-by: Claude:claude-opus-4-7 --- cmd/micro/micro.go | 23 +++++++---------------- 1 file changed, 7 insertions(+), 16 deletions(-) diff --git a/cmd/micro/micro.go b/cmd/micro/micro.go index 1331e41cb0..844e99a647 100644 --- a/cmd/micro/micro.go +++ b/cmd/micro/micro.go @@ -1,6 +1,7 @@ package main import ( + "errors" "flag" "fmt" "io" @@ -17,7 +18,7 @@ import ( "time" "github.com/gdamore/tcell/v3" - "github.com/go-errors/errors" + goerrors "github.com/go-errors/errors" isatty "github.com/mattn/go-isatty" "github.com/micro-editor/micro/v2/internal/action" "github.com/micro-editor/micro/v2/internal/buffer" @@ -393,7 +394,7 @@ func main() { if e, ok := err.(*lua.ApiError); ok { fmt.Println("Lua API error:", e) } else { - fmt.Println("Micro encountered an error:", errors.Wrap(err, 2).ErrorStack(), "\nIf you can reproduce this error, please report it at https://github.com/micro-editor/micro/issues") + fmt.Println("Micro encountered an error:", goerrors.Wrap(err, 2).ErrorStack(), "\nIf you can reproduce this error, please report it at https://github.com/micro-editor/micro/issues") } // immediately backup all buffers with unsaved changes for _, b := range buffer.OpenBuffers { @@ -463,19 +464,9 @@ func main() { config.SetAutoTime(a) } - screen.Events = make(chan tcell.Event) - - // Here is the event loop which runs in a separate thread - go func() { - for { - screen.Lock() - e := screen.Screen.PollEvent() - screen.Unlock() - if e != nil { - screen.Events <- e - } - } - }() + // screen.Events is populated by screen.Init() with the tcell v3 + // EventQ() channel; the former manual PollEvent goroutine is + // no longer needed (tcell v3 drives its own reader loop). // clear the drawchan so we don't redraw excessively // if someone requested a redraw before we started displaying @@ -542,7 +533,7 @@ func DoEvent() { if e, ok := event.(*tcell.EventError); ok { log.Println("tcell event error: ", e.Error()) - if e.Err() == io.EOF { + if errors.Is(e, io.EOF) { // shutdown due to terminal closing/becoming inaccessible exit(0) } From 2b4f2fcfefc3ca77fd701f93b52aac2c84527449 Mon Sep 17 00:00:00 2001 From: Tubbles Date: Sat, 18 Apr 2026 16:50:22 +0200 Subject: [PATCH 5/9] termpane: adapt to tcell v3 - Mirror the bracketed-paste accumulator on the termpane side. Between EventPaste{Start} and EventPaste{End} the intervening EventKeys are buffered together with \x1b[200~ / \x1b[201~ framing and the whole frame is written to the child pty in one go. This preserves bracketed-paste semantics for programs running inside termpane (so e.g. bash skips alias expansion on the pasted text). - When forwarding keystrokes to the pty, translate CSI-u Ctrl+letter events (KeyRune + ModCtrl + single-letter Str) back to the legacy single-byte C0 control. Child shells and readline speak the legacy byte protocol regardless of the outer terminal's keyboard encoding. - Use the eventEscSeq() helper for raw key forwarding now that EscSeq is not a method on the event type. Assisted-by: Claude:claude-opus-4-7 --- internal/action/termpane.go | 81 +++++++++++++++++++++++++++++++++---- 1 file changed, 74 insertions(+), 7 deletions(-) diff --git a/internal/action/termpane.go b/internal/action/termpane.go index 77e5fb9db2..07cf699c7c 100644 --- a/internal/action/termpane.go +++ b/internal/action/termpane.go @@ -3,6 +3,7 @@ package action import ( "errors" "runtime" + "strings" "github.com/gdamore/tcell/v3" "github.com/micro-editor/micro/v2/internal/clipboard" @@ -57,6 +58,17 @@ type TermPane struct { mouseReleased bool id uint64 tab *Tab + + // pasteBuf accumulates the bracketed-paste frame (opening + // \x1b[200~, per-key source bytes, closing \x1b[201~) between + // EventPaste{Start} and EventPaste{End}. tcell v3 no longer + // delivers paste payload via EventPaste.Text(); it streams + // EventKey events between Start and End. We reassemble them + // and forward the whole frame to the pty as one write so the + // child shell's own bracketed-paste detection fires and skips + // alias expansion, readline edit, and completion. + pasteBuf strings.Builder + inPaste bool } func NewTermPane(x, y, w, h int, t *shell.Terminal, id uint64, tab *Tab) (*TermPane, error) { @@ -119,12 +131,70 @@ func (t *TermPane) Unsplit() { MainTab().SetActive(len(MainTab().Panes) - 1) } +// normalizedTermKey returns the legacy KeyCtrl* value for a Ctrl+ +// event even when it arrived via the CSI-u keyboard protocol as +// KeyRune+ModCtrl+single-letter-Str. For any other event it returns +// e.Key() unchanged. Child shells and readline speak the legacy byte +// protocol, so this lets termpane's Ctrl+letter checks and byte +// forwarding match regardless of the outer terminal's encoding. +func normalizedTermKey(e *tcell.EventKey) tcell.Key { + if e.Key() != tcell.KeyRune || e.Modifiers() != tcell.ModCtrl { + return e.Key() + } + s := e.Str() + if len(s) != 1 { + return e.Key() + } + b := s[0] + switch { + case b >= 'a' && b <= 'z': + return tcell.KeyCtrlA + tcell.Key(b-'a') + case b >= 'A' && b <= 'Z': + return tcell.KeyCtrlA + tcell.Key(b-'A') + } + return e.Key() +} + +// termPtyBytes returns the byte sequence to forward to the child pty for +// a key event. Ctrl+ combinations (whether legacy or CSI-u on +// the input side) become their single legacy byte; everything else falls +// through to the raw source escape stamped on the event. +func termPtyBytes(e *tcell.EventKey, nk tcell.Key) string { + if nk >= tcell.KeyCtrlA && nk <= tcell.KeyCtrlZ { + return string([]byte{byte(nk) - 0x40}) + } + return eventEscSeq(e) +} + // HandleEvent handles a tcell event by forwarding it to the terminal emulator // If the event is a mouse event and the program running in the emulator // does not have mouse support, the emulator will support selections and // copy-paste func (t *TermPane) HandleEvent(event tcell.Event) { + if ep, ok := event.(*tcell.EventPaste); ok { + if t.Status == shell.TTDone { + return + } + if ep.Start() { + t.pasteBuf.Reset() + t.pasteBuf.WriteString("\x1b[200~") + t.inPaste = true + } else { + t.pasteBuf.WriteString("\x1b[201~") + t.WriteString(t.pasteBuf.String()) + t.pasteBuf.Reset() + t.inPaste = false + } + return + } + if e, ok := event.(*tcell.EventKey); ok { + if t.inPaste { + if t.Status != shell.TTDone { + t.pasteBuf.WriteString(eventEscSeq(e)) + } + return + } ke := keyEvent(e) action, more := TermBindings.NextEvent(ke, nil) @@ -141,23 +211,20 @@ func (t *TermPane) HandleEvent(event tcell.Event) { return } + nk := normalizedTermKey(e) if t.Status == shell.TTDone { - switch e.Key() { + switch nk { case tcell.KeyEscape, tcell.KeyCtrlQ, tcell.KeyEnter: t.Close() t.Quit() default: } } - if e.Key() == tcell.KeyCtrlC && t.HasSelection() { + if nk == tcell.KeyCtrlC && t.HasSelection() { clipboard.Write(t.GetSelection(t.GetView().Width), clipboard.ClipboardReg) InfoBar.Message("Copied selection to clipboard") } else if t.Status != shell.TTDone { - t.WriteString(event.EscSeq()) - } - } else if _, ok := event.(*tcell.EventPaste); ok { - if t.Status != shell.TTDone { - t.WriteString(event.EscSeq()) + t.WriteString(termPtyBytes(e, nk)) } } else if e, ok := event.(*tcell.EventMouse); !ok || t.State.Mode(terminal.ModeMouseMask) { // t.WriteString(event.EscSeq()) From b4547a04c171b396b80c0baba149168e3a7a9452 Mon Sep 17 00:00:00 2001 From: Tubbles Date: Sat, 18 Apr 2026 16:50:30 +0200 Subject: [PATCH 6/9] test: port micro_test to vt.MockTerm tcell v3 drops SimulationScreen. The package's vt.MockTerm is the closest replacement: it satisfies tcell.Screen with an in-memory cell grid and event queue, so the existing test harness adapts with minimal changes. PostEvent(NewEventResize(...)) replaces the old InjectResize helper. Assisted-by: Claude:claude-opus-4-7 --- cmd/micro/micro_test.go | 153 ++++++++++++++++++++++++++++------------ 1 file changed, 106 insertions(+), 47 deletions(-) diff --git a/cmd/micro/micro_test.go b/cmd/micro/micro_test.go index d90c699ea7..e649005797 100644 --- a/cmd/micro/micro_test.go +++ b/cmd/micro/micro_test.go @@ -5,8 +5,10 @@ import ( "log" "os" "testing" + "time" "github.com/gdamore/tcell/v3" + "github.com/gdamore/tcell/v3/vt" "github.com/go-errors/errors" "github.com/micro-editor/micro/v2/internal/action" "github.com/micro-editor/micro/v2/internal/buffer" @@ -17,13 +19,40 @@ import ( ) var tempDir string -var sim tcell.SimulationScreen +var mt vt.MockTerm + +// settleEvents waits briefly for the tcell reader goroutine to parse +// whatever bytes MockTerm just delivered into EventKey/EventMouse +// events on screen.Events, then returns. Unlike v2's SimulationScreen +// (which exposed a synchronous PollEvent), v3 parses asynchronously, +// so we have to give the reader a window to catch up before draining. +func settleEvents() { + deadline := time.Now().Add(200 * time.Millisecond) + for time.Now().Before(deadline) { + if len(screen.Events) > 0 || len(screen.DrawChan()) > 0 { + return + } + time.Sleep(2 * time.Millisecond) + } +} -func init() { - screen.Events = make(chan tcell.Event, 8) +// drainEvents runs DoEvent once per pending queue entry. Each DoEvent +// call consumes one event from screen.Events (or one Redraw token). +// After draining, a final short settle pass catches any tail events +// the processed action may have posted (e.g. a command prompt). +func drainEvents() { + for { + settleEvents() + if len(screen.Events) == 0 && len(screen.DrawChan()) == 0 { + return + } + for len(screen.Events) > 0 || len(screen.DrawChan()) > 0 { + DoEvent() + } + } } -func startup(args []string) (tcell.SimulationScreen, error) { +func startup(args []string) (vt.MockTerm, error) { var err error tempDir, err = os.MkdirTemp("", "micro_test") @@ -47,7 +76,7 @@ func startup(args []string) (tcell.SimulationScreen, error) { return nil, err } - s, err := screen.InitSimScreen() + mtLocal, err := screen.InitMockScreen() if err != nil { return nil, err } @@ -62,8 +91,7 @@ func startup(args []string) (tcell.SimulationScreen, error) { b.Backup() } } - // Print the stack trace too - log.Fatalf(errors.Wrap(err, 2).ErrorStack()) + log.Fatalf("%s", errors.Wrap(err, 2).ErrorStack()) } }() @@ -94,60 +122,94 @@ func startup(args []string) (tcell.SimulationScreen, error) { return nil, err } - s.InjectResize() - handleEvent() + w, h := screen.Screen.Size() + mtLocal.SetSize(vt.Coord{X: vt.Col(w), Y: vt.Row(h)}) + drainEvents() - return s, nil + return mtLocal, nil } func cleanup() { os.RemoveAll(tempDir) } -func handleEvent() { - screen.Lock() - e := screen.Screen.PollEvent() - screen.Unlock() - if e != nil { - screen.Events <- e +// keyBytes translates a (tcell.Key, rune, mod) injection into the raw +// byte sequence tcell's input parser expects. For plain runes the +// payload is the UTF-8 encoding; for control bytes (Ctrl-A .. Ctrl-Z, +// Enter, Tab, Backspace, Esc) it's the single-byte code; for the +// arrow keys it's the legacy CSI escape. This covers every key +// currently exercised by the test suite. Unknown combinations panic +// so a missing mapping fails loudly rather than silently no-opping. +func keyBytes(key tcell.Key, r rune, _ tcell.ModMask) []byte { + switch key { + case tcell.KeyRune: + return []byte(string(r)) + case tcell.KeyEnter: + return []byte{'\r'} + case tcell.KeyEscape: + return []byte{0x1b} + case tcell.KeyTab: + return []byte{'\t'} + case tcell.KeyBackspace: + return []byte{0x08} + case tcell.KeyBackspace2: + return []byte{0x7f} + case tcell.KeyUp: + return []byte{0x1b, '[', 'A'} + case tcell.KeyDown: + return []byte{0x1b, '[', 'B'} + case tcell.KeyRight: + return []byte{0x1b, '[', 'C'} + case tcell.KeyLeft: + return []byte{0x1b, '[', 'D'} } - - for len(screen.DrawChan()) > 0 || len(screen.Events) > 0 { - DoEvent() + if key >= tcell.KeyCtrlA && key <= tcell.KeyCtrlZ { + // tcell v3 reshuffled the Key constants: KeyCtrlA is 65, not 1. + // Convert back to the ASCII control byte (0x01..0x1A). + return []byte{byte(key-tcell.KeyCtrlA) + 1} } + if r != 0 { + return []byte(string(r)) + } + panic(fmt.Sprintf("keyBytes: unsupported key 0x%x", key)) } func injectKey(key tcell.Key, r rune, mod tcell.ModMask) { - sim.InjectKey(key, r, mod) - handleEvent() + mt.SendRaw(keyBytes(key, r, mod)) + drainEvents() } -func injectMouse(x, y int, buttons tcell.ButtonMask, mod tcell.ModMask) { - sim.InjectMouse(x, y, buttons, mod) - handleEvent() +func injectString(str string) { + mt.SendRaw([]byte(str)) + drainEvents() } -func injectString(str string) { - // the tcell simulation screen event channel can only handle - // 10 events at once, so we need to divide up the key events - // into chunks of 10 and handle the 10 events before sending - // another chunk of events - iters := len(str) / 10 - extra := len(str) % 10 - - for i := 0; i < iters; i++ { - s := i * 10 - e := i*10 + 10 - sim.InjectKeyBytes([]byte(str[s:e])) - for i := 0; i < 10; i++ { - handleEvent() - } +// tcellButtonToVt maps a tcell ButtonMask to the single vt.Button it +// corresponds to. micro_test only ever passes Button1 or ButtonNone; +// for ButtonNone (release) the caller must remember the button that +// was last pressed — vt.MouseEvent requires a non-nil Button even +// for release events. We assume Button1 for all releases, which is +// correct for every call site below. +func tcellButtonToVt(b tcell.ButtonMask) vt.Button { + switch { + case b&tcell.Button1 != 0: + return vt.Button1 + case b&tcell.Button2 != 0: + return vt.Button2 + case b&tcell.Button3 != 0: + return vt.Button3 } + return vt.Button1 +} - sim.InjectKeyBytes([]byte(str[len(str)-extra:])) - for i := 0; i < extra; i++ { - handleEvent() - } +func injectMouse(x, y int, buttons tcell.ButtonMask, _ tcell.ModMask) { + down := buttons != tcell.ButtonNone + mt.MouseEvent(vt.MouseEvent{ + Position: vt.Coord{X: vt.Col(x), Y: vt.Row(y)}, + Button: tcellButtonToVt(buttons), + Down: down, + }) + drainEvents() } func openFile(file string) { @@ -187,7 +249,7 @@ func createTestFile(t *testing.T, content string) string { func TestMain(m *testing.M) { var err error - sim, err = startup([]string{}) + mt, err = startup([]string{}) if err != nil { log.Fatalln(err) } @@ -211,7 +273,6 @@ func TestSimpleEdit(t *testing.T) { injectKey(tcell.KeyUp, 0, tcell.ModNone) injectString("first line") - // test both kinds of backspace for i := 0; i < len("ne"); i++ { injectKey(tcell.KeyBackspace, rune(tcell.KeyBackspace), tcell.ModNone) } @@ -336,5 +397,3 @@ func TestMultiCursor(t *testing.T) { func TestSettingsPersistence(t *testing.T) { // TODO } - -// more tests (rendering, tabs, plugins)? From f5e19bbec8020f11bdb50b4d5f061014210bcc6a Mon Sep 17 00:00:00 2001 From: Tubbles Date: Sat, 18 Apr 2026 16:50:39 +0200 Subject: [PATCH 7/9] runtime: drop deprecated Ctrl-key constant names tcell v3 removes the legacy KeyCtrlSpace / KeyCtrlLeftSq / KeyCtrlBackslash / KeyCtrlRightSq / KeyCtrlCarat / KeyCtrlUnderscore constants. Micro's bindings parser previously accepted the matching string names ("CtrlSpace", "CtrlLeftSq", ...) as aliases; those are now invalid and the parser emits a TermMessage warning when it sees one. Update the shipped comment plugin and the keybindings help page to use the supported forms. Assisted-by: Claude:claude-opus-4-7 --- runtime/help/keybindings.md | 6 ------ runtime/plugins/comment/comment.lua | 2 +- runtime/plugins/comment/help/comment.md | 4 ++-- 3 files changed, 3 insertions(+), 9 deletions(-) diff --git a/runtime/help/keybindings.md b/runtime/help/keybindings.md index 50dc4057c4..3b9004d790 100644 --- a/runtime/help/keybindings.md +++ b/runtime/help/keybindings.md @@ -452,7 +452,6 @@ F61 F62 F63 F64 -CtrlSpace Ctrl-a Ctrl-b Ctrl-c @@ -479,11 +478,6 @@ Ctrl-w Ctrl-x Ctrl-y Ctrl-z -CtrlLeftSq -CtrlBackslash -CtrlRightSq -CtrlCarat -CtrlUnderscore Backspace OldBackspace Tab diff --git a/runtime/plugins/comment/comment.lua b/runtime/plugins/comment/comment.lua index ccda2e1297..70b6444412 100644 --- a/runtime/plugins/comment/comment.lua +++ b/runtime/plugins/comment/comment.lua @@ -217,6 +217,6 @@ end function init() config.MakeCommand("comment", comment, config.NoComplete) config.TryBindKey("Alt-/", "lua:comment.comment", false) - config.TryBindKey("CtrlUnderscore", "lua:comment.comment", false) + config.TryBindKey("Ctrl-_", "lua:comment.comment", false) config.AddRuntimeFile("comment", config.RTHelp, "help/comment.md") end diff --git a/runtime/plugins/comment/help/comment.md b/runtime/plugins/comment/help/comment.md index fc0cfe01a3..59bd3e1c76 100644 --- a/runtime/plugins/comment/help/comment.md +++ b/runtime/plugins/comment/help/comment.md @@ -2,8 +2,8 @@ The comment plugin provides auto commenting/uncommenting. The default binding to comment/uncomment a line is `Alt-/` -and `CtrlUnderscore`, which is equivalent in most terminals -to `Ctrl-/`. You can easily modify that in your `bindings.json` +and `Ctrl-_`, which is equivalent in most terminals to +`Ctrl-/`. You can easily modify that in your `bindings.json` file: ```json From 587dcbeb0fb483b2ab91050dde7787baca129e81 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=C3=B6ran=20Karl?= <3951388+JoeKar@users.noreply.github.com> Date: Sun, 7 Jun 2026 14:15:52 +0200 Subject: [PATCH 8/9] bindings: Adapt defaults --- internal/action/defaults_darwin.go | 274 ++++++++++++++--------------- internal/action/defaults_other.go | 274 ++++++++++++++--------------- 2 files changed, 274 insertions(+), 274 deletions(-) diff --git a/internal/action/defaults_darwin.go b/internal/action/defaults_darwin.go index 44b4fcc1ae..1198a3e347 100644 --- a/internal/action/defaults_darwin.go +++ b/internal/action/defaults_darwin.go @@ -1,81 +1,81 @@ package action var bufdefaults = map[string]string{ - "Up": "CursorUp", - "Down": "CursorDown", - "Right": "CursorRight", - "Left": "CursorLeft", - "ShiftUp": "SelectUp", - "ShiftDown": "SelectDown", - "ShiftLeft": "SelectLeft", - "ShiftRight": "SelectRight", - "AltLeft": "WordLeft", - "AltRight": "WordRight", - "AltUp": "MoveLinesUp", - "AltDown": "MoveLinesDown", - "AltShiftRight": "SelectWordRight", - "AltShiftLeft": "SelectWordLeft", - "CtrlLeft": "StartOfTextToggle", - "CtrlRight": "EndOfLine", - "CtrlShiftLeft": "SelectToStartOfTextToggle", - "ShiftHome": "SelectToStartOfTextToggle", - "CtrlShiftRight": "SelectToEndOfLine", - "ShiftEnd": "SelectToEndOfLine", - "CtrlUp": "CursorStart", - "CtrlDown": "CursorEnd", - "CtrlShiftUp": "SelectToStart", - "CtrlShiftDown": "SelectToEnd", - "Alt-{": "ParagraphPrevious", - "Alt-}": "ParagraphNext", - "Enter": "InsertNewline", - "CtrlH": "Backspace", - "Backspace": "Backspace", - "OldBackspace": "Backspace", - "Alt-CtrlH": "DeleteWordLeft", - "Alt-Backspace": "DeleteWordLeft", - "Tab": "Autocomplete|IndentSelection|InsertTab", - "Backtab": "CycleAutocompleteBack|OutdentSelection|OutdentLine", - "Ctrl-o": "OpenFile", - "Ctrl-s": "Save", - "Ctrl-f": "Find", - "Alt-F": "FindLiteral", - "Ctrl-n": "FindNext", - "Ctrl-p": "FindPrevious", - "Alt-[": "DiffPrevious|CursorStart", - "Alt-]": "DiffNext|CursorEnd", - "Ctrl-z": "Undo", - "Ctrl-y": "Redo", - "Ctrl-c": "Copy|CopyLine", - "Ctrl-x": "Cut|CutLine", - "Ctrl-k": "CutLine", - "Ctrl-d": "Duplicate|DuplicateLine", - "Ctrl-v": "Paste", - "Ctrl-a": "SelectAll", - "Ctrl-t": "AddTab", - "Alt-,": "PreviousTab|LastTab", - "Alt-.": "NextTab|FirstTab", - "Home": "StartOfTextToggle", - "End": "EndOfLine", - "CtrlHome": "CursorStart", - "CtrlEnd": "CursorEnd", - "PageUp": "CursorPageUp", - "PageDown": "CursorPageDown", - "CtrlPageUp": "PreviousTab|LastTab", - "CtrlPageDown": "NextTab|FirstTab", - "ShiftPageUp": "SelectPageUp", - "ShiftPageDown": "SelectPageDown", - "Ctrl-g": "ToggleHelp", - "Alt-g": "ToggleKeyMenu", - "Ctrl-r": "ToggleRuler", - "Ctrl-l": "command-edit:goto ", - "Delete": "Delete", - "Ctrl-b": "ShellMode", - "Ctrl-q": "Quit", - "Ctrl-e": "CommandMode", - "Ctrl-w": "NextSplit|FirstSplit", - "Ctrl-u": "ToggleMacro", - "Ctrl-j": "PlayMacro", - "Insert": "ToggleOverwriteMode", + "Up": "CursorUp", + "Down": "CursorDown", + "Right": "CursorRight", + "Left": "CursorLeft", + "Shift-Up": "SelectUp", + "Shift-Down": "SelectDown", + "Shift-Left": "SelectLeft", + "Shift-Right": "SelectRight", + "Ctrl-Left": "StartOfTextToggle", + "Ctrl-Right": "EndOfLine", + "Alt-Up": "MoveLinesUp", + "Alt-Down": "MoveLinesDown", + "Shift-Ctrl-Right": "SelectWordRight", + "Shift-Ctrl-Left": "SelectWordLeft", + "Alt-Left": "WordLeft", + "Alt-Right": "WordRight", + "Shift-Alt-Left": "SelectToStartOfTextToggle", + "Shift-Home": "SelectToStartOfTextToggle", + "Shift-Alt-Right": "SelectToEndOfLine", + "Shift-End": "SelectToEndOfLine", + "Ctrl-Up": "CursorStart", + "Ctrl-Down": "CursorEnd", + "Shift-Ctrl-Up": "SelectToStart", + "Shift-Ctrl-Down": "SelectToEnd", + "Alt-{": "ParagraphPrevious", + "Alt-}": "ParagraphNext", + "Enter": "InsertNewline", + "Ctrl-H": "Backspace", + "Backspace": "Backspace", + "OldBackspace": "Backspace", + "Alt-Ctrl-h": "DeleteWordLeft", + "Alt-Backspace": "DeleteWordLeft", + "Tab": "Autocomplete|IndentSelection|InsertTab", + "Backtab": "CycleAutocompleteBack|OutdentSelection|OutdentLine", + "Ctrl-o": "OpenFile", + "Ctrl-s": "Save", + "Ctrl-f": "Find", + "Alt-F": "FindLiteral", + "Ctrl-n": "FindNext", + "Ctrl-p": "FindPrevious", + "Alt-[": "DiffPrevious|CursorStart", + "Alt-]": "DiffNext|CursorEnd", + "Ctrl-z": "Undo", + "Ctrl-y": "Redo", + "Ctrl-c": "Copy|CopyLine", + "Ctrl-x": "Cut|CutLine", + "Ctrl-k": "CutLine", + "Ctrl-d": "Duplicate|DuplicateLine", + "Ctrl-v": "Paste", + "Ctrl-a": "SelectAll", + "Ctrl-t": "AddTab", + "Alt-,": "PreviousTab|LastTab", + "Alt-.": "NextTab|FirstTab", + "Home": "StartOfTextToggle", + "End": "EndOfLine", + "Ctrl-Home": "CursorStart", + "Ctrl-End": "CursorEnd", + "PageUp": "CursorPageUp", + "PageDown": "CursorPageDown", + "Ctrl-PageUp": "PreviousTab|LastTab", + "Ctrl-PageDown": "NextTab|FirstTab", + "Shift-PageUp": "SelectPageUp", + "Shift-PageDown": "SelectPageDown", + "Ctrl-g": "ToggleHelp", + "Alt-g": "ToggleKeyMenu", + "Ctrl-r": "ToggleRuler", + "Ctrl-l": "command-edit:goto ", + "Delete": "Delete", + "Ctrl-b": "ShellMode", + "Ctrl-q": "Quit", + "Ctrl-e": "CommandMode", + "Ctrl-w": "NextSplit|FirstSplit", + "Ctrl-u": "ToggleMacro", + "Ctrl-j": "PlayMacro", + "Insert": "ToggleOverwriteMode", // Emacs-style keybindings "Alt-f": "WordRight", @@ -102,71 +102,71 @@ var bufdefaults = map[string]string{ "MouseMiddle": "PastePrimary", "Ctrl-MouseLeft": "MouseMultiCursor", - "Alt-n": "SpawnMultiCursor", - "AltShiftUp": "SpawnMultiCursorUp", - "AltShiftDown": "SpawnMultiCursorDown", - "Alt-m": "SpawnMultiCursorSelect", - "Alt-p": "RemoveMultiCursor", - "Alt-c": "RemoveAllMultiCursors", - "Alt-x": "SkipMultiCursor", + "Alt-n": "SpawnMultiCursor", + "Shift-Alt-Up": "SpawnMultiCursorUp", + "Shift-Alt-Down": "SpawnMultiCursorDown", + "Alt-m": "SpawnMultiCursorSelect", + "Alt-p": "RemoveMultiCursor", + "Alt-c": "RemoveAllMultiCursors", + "Alt-x": "SkipMultiCursor", } var infodefaults = map[string]string{ - "Up": "HistoryUp", - "Down": "HistoryDown", - "Right": "CursorRight", - "Left": "CursorLeft", - "ShiftUp": "SelectUp", - "ShiftDown": "SelectDown", - "ShiftLeft": "SelectLeft", - "ShiftRight": "SelectRight", - "AltLeft": "WordLeft", - "AltRight": "WordRight", - "AltUp": "CursorStart", - "AltDown": "CursorEnd", - "AltShiftRight": "SelectWordRight", - "AltShiftLeft": "SelectWordLeft", - "CtrlLeft": "StartOfTextToggle", - "CtrlRight": "EndOfLine", - "CtrlShiftLeft": "SelectToStartOfTextToggle", - "ShiftHome": "SelectToStartOfTextToggle", - "CtrlShiftRight": "SelectToEndOfLine", - "ShiftEnd": "SelectToEndOfLine", - "CtrlUp": "CursorStart", - "CtrlDown": "CursorEnd", - "CtrlShiftUp": "SelectToStart", - "CtrlShiftDown": "SelectToEnd", - "Enter": "ExecuteCommand", - "CtrlH": "Backspace", - "Backspace": "Backspace", - "OldBackspace": "Backspace", - "Alt-CtrlH": "DeleteWordLeft", - "Alt-Backspace": "DeleteWordLeft", - "Tab": "CommandComplete", - "Backtab": "CycleAutocompleteBack", - "Ctrl-z": "Undo", - "Ctrl-y": "Redo", - "Ctrl-c": "Copy", - "Ctrl-x": "Cut", - "Ctrl-k": "CutLine", - "Ctrl-v": "Paste", - "Home": "StartOfTextToggle", - "End": "EndOfLine", - "CtrlHome": "CursorStart", - "CtrlEnd": "CursorEnd", - "Delete": "Delete", - "Ctrl-q": "AbortCommand", - "Ctrl-e": "EndOfLine", - "Ctrl-a": "StartOfLine", - "Ctrl-w": "DeleteWordLeft", - "Insert": "ToggleOverwriteMode", - "Ctrl-b": "WordLeft", - "Ctrl-f": "WordRight", - "Ctrl-d": "DeleteWordLeft", - "Ctrl-m": "ExecuteCommand", - "Ctrl-n": "HistoryDown", - "Ctrl-p": "HistoryUp", - "Ctrl-u": "SelectToStart", + "Up": "HistoryUp", + "Down": "HistoryDown", + "Right": "CursorRight", + "Left": "CursorLeft", + "Shift-Up": "SelectUp", + "Shift-Down": "SelectDown", + "Shift-Left": "SelectLeft", + "Shift-Right": "SelectRight", + "Alt-Left": "WordLeft", + "Alt-Right": "WordRight", + "Alt-Up": "CursorStart", + "Alt-Down": "CursorEnd", + "Shift-Alt-Right": "SelectWordRight", + "Shift-Alt-Left": "SelectWordLeft", + "Ctrl-Left": "StartOfTextToggle", + "Ctrl-Right": "EndOfLine", + "Shift-Ctrl-Left": "SelectToStartOfTextToggle", + "Shift-Home": "SelectToStartOfTextToggle", + "Shift-Ctrl-Right": "SelectToEndOfLine", + "Shift-End": "SelectToEndOfLine", + "Ctrl-Up": "CursorStart", + "Ctrl-Down": "CursorEnd", + "Shift-Ctrl-Up": "SelectToStart", + "Shift-Ctrl-Down": "SelectToEnd", + "Enter": "ExecuteCommand", + "Ctrl-H": "Backspace", + "Backspace": "Backspace", + "OldBackspace": "Backspace", + "Alt-Ctrl-h": "DeleteWordLeft", + "Alt-Backspace": "DeleteWordLeft", + "Tab": "CommandComplete", + "Backtab": "CycleAutocompleteBack", + "Ctrl-z": "Undo", + "Ctrl-y": "Redo", + "Ctrl-c": "Copy", + "Ctrl-x": "Cut", + "Ctrl-k": "CutLine", + "Ctrl-v": "Paste", + "Home": "StartOfTextToggle", + "End": "EndOfLine", + "Ctrl-Home": "CursorStart", + "Ctrl-End": "CursorEnd", + "Delete": "Delete", + "Ctrl-q": "AbortCommand", + "Ctrl-e": "EndOfLine", + "Ctrl-a": "StartOfLine", + "Ctrl-w": "DeleteWordLeft", + "Insert": "ToggleOverwriteMode", + "Ctrl-b": "WordLeft", + "Ctrl-f": "WordRight", + "Ctrl-d": "DeleteWordLeft", + "Ctrl-m": "ExecuteCommand", + "Ctrl-n": "HistoryDown", + "Ctrl-p": "HistoryUp", + "Ctrl-u": "SelectToStart", // Emacs-style keybindings "Alt-f": "WordRight", diff --git a/internal/action/defaults_other.go b/internal/action/defaults_other.go index 10e5b08358..3ce3838d23 100644 --- a/internal/action/defaults_other.go +++ b/internal/action/defaults_other.go @@ -4,81 +4,81 @@ package action var bufdefaults = map[string]string{ - "Up": "CursorUp", - "Down": "CursorDown", - "Right": "CursorRight", - "Left": "CursorLeft", - "ShiftUp": "SelectUp", - "ShiftDown": "SelectDown", - "ShiftLeft": "SelectLeft", - "ShiftRight": "SelectRight", - "CtrlLeft": "WordLeft", - "CtrlRight": "WordRight", - "AltUp": "MoveLinesUp", - "AltDown": "MoveLinesDown", - "CtrlShiftRight": "SelectWordRight", - "CtrlShiftLeft": "SelectWordLeft", - "AltLeft": "StartOfTextToggle", - "AltRight": "EndOfLine", - "AltShiftLeft": "SelectToStartOfTextToggle", - "ShiftHome": "SelectToStartOfTextToggle", - "AltShiftRight": "SelectToEndOfLine", - "ShiftEnd": "SelectToEndOfLine", - "CtrlUp": "CursorStart", - "CtrlDown": "CursorEnd", - "CtrlShiftUp": "SelectToStart", - "CtrlShiftDown": "SelectToEnd", - "Alt-{": "ParagraphPrevious", - "Alt-}": "ParagraphNext", - "Enter": "InsertNewline", - "CtrlH": "Backspace", - "Backspace": "Backspace", - "OldBackspace": "Backspace", - "Alt-CtrlH": "DeleteWordLeft", - "Alt-Backspace": "DeleteWordLeft", - "Tab": "Autocomplete|IndentSelection|InsertTab", - "Backtab": "CycleAutocompleteBack|OutdentSelection|OutdentLine", - "Ctrl-o": "OpenFile", - "Ctrl-s": "Save", - "Ctrl-f": "Find", - "Alt-F": "FindLiteral", - "Ctrl-n": "FindNext", - "Ctrl-p": "FindPrevious", - "Alt-[": "DiffPrevious|CursorStart", - "Alt-]": "DiffNext|CursorEnd", - "Ctrl-z": "Undo", - "Ctrl-y": "Redo", - "Ctrl-c": "Copy|CopyLine", - "Ctrl-x": "Cut|CutLine", - "Ctrl-k": "CutLine", - "Ctrl-d": "Duplicate|DuplicateLine", - "Ctrl-v": "Paste", - "Ctrl-a": "SelectAll", - "Ctrl-t": "AddTab", - "Alt-,": "PreviousTab|LastTab", - "Alt-.": "NextTab|FirstTab", - "Home": "StartOfTextToggle", - "End": "EndOfLine", - "CtrlHome": "CursorStart", - "CtrlEnd": "CursorEnd", - "PageUp": "CursorPageUp", - "PageDown": "CursorPageDown", - "CtrlPageUp": "PreviousTab|LastTab", - "CtrlPageDown": "NextTab|FirstTab", - "ShiftPageUp": "SelectPageUp", - "ShiftPageDown": "SelectPageDown", - "Ctrl-g": "ToggleHelp", - "Alt-g": "ToggleKeyMenu", - "Ctrl-r": "ToggleRuler", - "Ctrl-l": "command-edit:goto ", - "Delete": "Delete", - "Ctrl-b": "ShellMode", - "Ctrl-q": "Quit", - "Ctrl-e": "CommandMode", - "Ctrl-w": "NextSplit|FirstSplit", - "Ctrl-u": "ToggleMacro", - "Ctrl-j": "PlayMacro", - "Insert": "ToggleOverwriteMode", + "Up": "CursorUp", + "Down": "CursorDown", + "Right": "CursorRight", + "Left": "CursorLeft", + "Shift-Up": "SelectUp", + "Shift-Down": "SelectDown", + "Shift-Left": "SelectLeft", + "Shift-Right": "SelectRight", + "Ctrl-Left": "WordLeft", + "Ctrl-Right": "WordRight", + "Alt-Up": "MoveLinesUp", + "Alt-Down": "MoveLinesDown", + "Shift-Ctrl-Right": "SelectWordRight", + "Shift-Ctrl-Left": "SelectWordLeft", + "Alt-Left": "StartOfTextToggle", + "Alt-Right": "EndOfLine", + "Shift-Alt-Left": "SelectToStartOfTextToggle", + "Shift-Home": "SelectToStartOfTextToggle", + "Shift-Alt-Right": "SelectToEndOfLine", + "Shift-End": "SelectToEndOfLine", + "Ctrl-Up": "CursorStart", + "Ctrl-Down": "CursorEnd", + "Shift-Ctrl-Up": "SelectToStart", + "Shift-Ctrl-Down": "SelectToEnd", + "Alt-{": "ParagraphPrevious", + "Alt-}": "ParagraphNext", + "Enter": "InsertNewline", + "Ctrl-H": "Backspace", + "Backspace": "Backspace", + "OldBackspace": "Backspace", + "Alt-Ctrl-h": "DeleteWordLeft", + "Alt-Backspace": "DeleteWordLeft", + "Tab": "Autocomplete|IndentSelection|InsertTab", + "Backtab": "CycleAutocompleteBack|OutdentSelection|OutdentLine", + "Ctrl-o": "OpenFile", + "Ctrl-s": "Save", + "Ctrl-f": "Find", + "Alt-F": "FindLiteral", + "Ctrl-n": "FindNext", + "Ctrl-p": "FindPrevious", + "Alt-[": "DiffPrevious|CursorStart", + "Alt-]": "DiffNext|CursorEnd", + "Ctrl-z": "Undo", + "Ctrl-y": "Redo", + "Ctrl-c": "Copy|CopyLine", + "Ctrl-x": "Cut|CutLine", + "Ctrl-k": "CutLine", + "Ctrl-d": "Duplicate|DuplicateLine", + "Ctrl-v": "Paste", + "Ctrl-a": "SelectAll", + "Ctrl-t": "AddTab", + "Alt-,": "PreviousTab|LastTab", + "Alt-.": "NextTab|FirstTab", + "Home": "StartOfTextToggle", + "End": "EndOfLine", + "Ctrl-Home": "CursorStart", + "Ctrl-End": "CursorEnd", + "PageUp": "CursorPageUp", + "PageDown": "CursorPageDown", + "Ctrl-PageUp": "PreviousTab|LastTab", + "Ctrl-PageDown": "NextTab|FirstTab", + "Shift-PageUp": "SelectPageUp", + "Shift-PageDown": "SelectPageDown", + "Ctrl-g": "ToggleHelp", + "Alt-g": "ToggleKeyMenu", + "Ctrl-r": "ToggleRuler", + "Ctrl-l": "command-edit:goto ", + "Delete": "Delete", + "Ctrl-b": "ShellMode", + "Ctrl-q": "Quit", + "Ctrl-e": "CommandMode", + "Ctrl-w": "NextSplit|FirstSplit", + "Ctrl-u": "ToggleMacro", + "Ctrl-j": "PlayMacro", + "Insert": "ToggleOverwriteMode", // Emacs-style keybindings "Alt-f": "WordRight", @@ -105,71 +105,71 @@ var bufdefaults = map[string]string{ "MouseMiddle": "PastePrimary", "Ctrl-MouseLeft": "MouseMultiCursor", - "Alt-n": "SpawnMultiCursor", - "Alt-m": "SpawnMultiCursorSelect", - "AltShiftUp": "SpawnMultiCursorUp", - "AltShiftDown": "SpawnMultiCursorDown", - "Alt-p": "RemoveMultiCursor", - "Alt-c": "RemoveAllMultiCursors", - "Alt-x": "SkipMultiCursor", + "Alt-n": "SpawnMultiCursor", + "Alt-m": "SpawnMultiCursorSelect", + "Shift-Alt-Up": "SpawnMultiCursorUp", + "Shift-Alt-Down": "SpawnMultiCursorDown", + "Alt-p": "RemoveMultiCursor", + "Alt-c": "RemoveAllMultiCursors", + "Alt-x": "SkipMultiCursor", } var infodefaults = map[string]string{ - "Up": "HistoryUp", - "Down": "HistoryDown", - "Right": "CursorRight", - "Left": "CursorLeft", - "ShiftUp": "SelectUp", - "ShiftDown": "SelectDown", - "ShiftLeft": "SelectLeft", - "ShiftRight": "SelectRight", - "AltLeft": "StartOfTextToggle", - "AltRight": "EndOfLine", - "AltUp": "CursorStart", - "AltDown": "CursorEnd", - "AltShiftRight": "SelectWordRight", - "AltShiftLeft": "SelectWordLeft", - "CtrlLeft": "WordLeft", - "CtrlRight": "WordRight", - "CtrlShiftLeft": "SelectToStartOfTextToggle", - "ShiftHome": "SelectToStartOfTextToggle", - "CtrlShiftRight": "SelectToEndOfLine", - "ShiftEnd": "SelectToEndOfLine", - "CtrlUp": "CursorStart", - "CtrlDown": "CursorEnd", - "CtrlShiftUp": "SelectToStart", - "CtrlShiftDown": "SelectToEnd", - "Enter": "ExecuteCommand", - "CtrlH": "Backspace", - "Backspace": "Backspace", - "OldBackspace": "Backspace", - "Alt-CtrlH": "DeleteWordLeft", - "Alt-Backspace": "DeleteWordLeft", - "Tab": "CommandComplete", - "Backtab": "CycleAutocompleteBack", - "Ctrl-z": "Undo", - "Ctrl-y": "Redo", - "Ctrl-c": "Copy", - "Ctrl-x": "Cut", - "Ctrl-k": "CutLine", - "Ctrl-v": "Paste", - "Home": "StartOfTextToggle", - "End": "EndOfLine", - "CtrlHome": "CursorStart", - "CtrlEnd": "CursorEnd", - "Delete": "Delete", - "Ctrl-q": "AbortCommand", - "Ctrl-e": "EndOfLine", - "Ctrl-a": "StartOfLine", - "Ctrl-w": "DeleteWordLeft", - "Insert": "ToggleOverwriteMode", - "Ctrl-b": "WordLeft", - "Ctrl-f": "WordRight", - "Ctrl-d": "DeleteWordLeft", - "Ctrl-m": "ExecuteCommand", - "Ctrl-n": "HistoryDown", - "Ctrl-p": "HistoryUp", - "Ctrl-u": "SelectToStart", + "Up": "HistoryUp", + "Down": "HistoryDown", + "Right": "CursorRight", + "Left": "CursorLeft", + "Shift-Up": "SelectUp", + "Shift-Down": "SelectDown", + "Shift-Left": "SelectLeft", + "Shift-Right": "SelectRight", + "Alt-Left": "StartOfTextToggle", + "Alt-Right": "EndOfLine", + "Alt-Up": "CursorStart", + "Alt-Down": "CursorEnd", + "Shift-Alt-Right": "SelectWordRight", + "Shift-Alt-Left": "SelectWordLeft", + "Ctrl-Left": "WordLeft", + "Ctrl-Right": "WordRight", + "Shift-Ctrl-Left": "SelectToStartOfTextToggle", + "Shift-Home": "SelectToStartOfTextToggle", + "Shift-Ctrl-Right": "SelectToEndOfLine", + "Shift-End": "SelectToEndOfLine", + "Ctrl-Up": "CursorStart", + "Ctrl-Down": "CursorEnd", + "Shift-Ctrl-Up": "SelectToStart", + "Shift-Ctrl-Down": "SelectToEnd", + "Enter": "ExecuteCommand", + "Ctrl-H": "Backspace", + "Backspace": "Backspace", + "OldBackspace": "Backspace", + "Alt-Ctrl-h": "DeleteWordLeft", + "Alt-Backspace": "DeleteWordLeft", + "Tab": "CommandComplete", + "Backtab": "CycleAutocompleteBack", + "Ctrl-z": "Undo", + "Ctrl-y": "Redo", + "Ctrl-c": "Copy", + "Ctrl-x": "Cut", + "Ctrl-k": "CutLine", + "Ctrl-v": "Paste", + "Home": "StartOfTextToggle", + "End": "EndOfLine", + "Ctrl-Home": "CursorStart", + "Ctrl-End": "CursorEnd", + "Delete": "Delete", + "Ctrl-q": "AbortCommand", + "Ctrl-e": "EndOfLine", + "Ctrl-a": "StartOfLine", + "Ctrl-w": "DeleteWordLeft", + "Insert": "ToggleOverwriteMode", + "Ctrl-b": "WordLeft", + "Ctrl-f": "WordRight", + "Ctrl-d": "DeleteWordLeft", + "Ctrl-m": "ExecuteCommand", + "Ctrl-n": "HistoryDown", + "Ctrl-p": "HistoryUp", + "Ctrl-u": "SelectToStart", // Emacs-style keybindings "Alt-f": "WordRight", From d6daf2a82249fd2b8f444223b9f4f95d96ef1421 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=C3=B6ran=20Karl?= <3951388+JoeKar@users.noreply.github.com> Date: Mon, 29 Jun 2026 21:02:36 +0200 Subject: [PATCH 9/9] settings: Change default of `paste` to `true` for Windows Terminal App --- internal/config/settings.go | 12 +++++++++++- runtime/help/options.md | 2 ++ 2 files changed, 13 insertions(+), 1 deletion(-) diff --git a/internal/config/settings.go b/internal/config/settings.go index 45f6b3aad6..82950e374f 100644 --- a/internal/config/settings.go +++ b/internal/config/settings.go @@ -123,7 +123,7 @@ var DefaultGlobalOnlySettings = map[string]any{ "mouse": true, "multiopen": "tab", "parsecursor": false, - "paste": false, + "paste": defaultPaste(), "pluginchannels": []string{"https://raw.githubusercontent.com/micro-editor/plugin-channel/master/channel.json"}, "pluginrepos": []string{}, "savehistory": true, @@ -463,6 +463,16 @@ func defaultFakeCursor() bool { return false } +func defaultPaste() bool { + _, wt := os.LookupEnv("WT_SESSION") + if runtime.GOOS == "windows" && wt { + // enabled for windows terminal app where the default paste bindings + // like Ctrl-v and Ctrl-Shift-v produce bracketed paste + return true + } + return false +} + func GetInfoBarOffset() int { offset := 0 if GetGlobalOption("infobar").(bool) { diff --git a/runtime/help/options.md b/runtime/help/options.md index 542089425b..cde82b8625 100644 --- a/runtime/help/options.md +++ b/runtime/help/options.md @@ -306,6 +306,8 @@ Here are the available options: keybinding) then it is a good idea to enable this option during the paste and disable once the paste is over. See `> help copypaste` for details about copying and pasting in a terminal environment. + Note: This option defaults to `true` in case `micro` is used in the Windows + Terminal App. default value: `false`