diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index 299fb016..c5b9d531 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -11,6 +11,10 @@ on: - created - edited +concurrency: + group: ${{ github.workflow }}-${{ github.ref }} + cancel-in-progress: true + defaults: run: shell: bash @@ -20,15 +24,17 @@ jobs: strategy: matrix: os: - - ubuntu-22.04 # https://github.com/actions/runner-images/blob/main/images/linux/Ubuntu2204-Readme.md - - macos-13 # https://github.com/actions/runner-images/blob/main/images/macos/macos-13-Readme.md - - windows-2022 # https://github.com/actions/runner-images/blob/main/images/win/Windows2022-Readme.md + - ubuntu-24.04 # https://github.com/actions/runner-images/blob/main/images/ubuntu/Ubuntu2404-Readme.md + - macos-15 # https://github.com/actions/runner-images/blob/main/images/macos/macos-15-Readme.md + - windows-2025 # https://github.com/actions/runner-images/blob/main/images/windows/Windows2025-Readme.md go-version: - - 1.18.x - - 1.19.x - - 1.20.x + - 1.22.x + - 1.23.x + - 1.24.x neovim-version: - v0.9.1 + - v0.10.4 + - v0.11.0 - nightly fail-fast: false @@ -42,12 +48,12 @@ jobs: echo "NVIM_VERSION=$(if [ ${{ matrix.neovim-version }} != 'nightly' ]; then echo 'stable'; else echo 'nightly'; fi)" >> $GITHUB_ENV - name: Install Go - uses: actions/setup-go@v3 + uses: actions/setup-go@v5 with: go-version: ${{ matrix.go-version }} - name: Checkout code - uses: actions/checkout@v3 + uses: actions/checkout@v4 - name: Install neovim binary uses: rhysd/action-setup-vim@v1 @@ -59,8 +65,8 @@ jobs: run: | go test -race -count=1 -covermode=atomic -coverpkg=./... -coverprofile=coverage.out ./... - - uses: codecov/codecov-action@v3 + - uses: codecov/codecov-action@v5 with: - file: coverage.out + files: coverage.out flags: ${{ env.OS }}-${{ env.GO_VERSION }}-${{ env.NVIM_VERSION }} env_vars: OS,GO_VERSION,NVIM_VERSION diff --git a/msgpack/decode.go b/msgpack/decode.go index b055dac2..e96cb716 100644 --- a/msgpack/decode.go +++ b/msgpack/decode.go @@ -321,6 +321,8 @@ func stringDecoder(ds *decodeState, v reflect.Value) { var x string switch ds.Type() { + case Nil: + x = "" case Binary, String: x = ds.String() default: diff --git a/msgpack/decode_test.go b/msgpack/decode_test.go index deaec364..a1abdcea 100644 --- a/msgpack/decode_test.go +++ b/msgpack/decode_test.go @@ -964,6 +964,16 @@ func Test_stringDecoder(t *testing.T) { want string wantErr bool }{ + "Nil": { + ds: &decodeState{ + Decoder: &Decoder{ + p: nil, + t: Nil, + }, + }, + want: string(""), + wantErr: false, + }, "Binary": { ds: &decodeState{ Decoder: &Decoder{ diff --git a/nvim/api_test.go b/nvim/api_test.go index 81a2d861..808e75f3 100644 --- a/nvim/api_test.go +++ b/nvim/api_test.go @@ -2250,8 +2250,19 @@ func testCommand(v *Nvim) func(*testing.T) { if err != nil { t.Fatal(err) } - if len(cmds) > 0 { - t.Fatalf("expected 0 length but got %#v", cmds) + + var want int + switch nvimVersion.Minor { + case 9: + want = 0 + case 10: + want = 3 + default: + want = 4 + } + + if len(cmds) > want { + t.Fatalf("expected %d length but got %#v", want, cmds) } }) @@ -2266,8 +2277,17 @@ func testCommand(v *Nvim) func(*testing.T) { if err := b.Execute(); err != nil { t.Fatal(err) } - if len(cmds) > 0 { - t.Fatalf("expected 0 length but got %#v", cmds) + var want int + switch nvimVersion.Minor { + case 9: + want = 0 + case 10: + want = 3 + default: + want = 4 + } + if len(cmds) > want { + t.Fatalf("expected %d length but got %#v", want, cmds) } }) }) @@ -3015,195 +3035,49 @@ func testKey(v *Nvim) func(*testing.T) { skipBetweenVersion(t, "v0.7.0", "v0.8.0") mode := "n" - if err := v.SetKeyMap(mode, "y", "yy", make(map[string]bool)); err != nil { + original, err := v.KeyMap(mode) + if err != nil { t.Fatal(err) } - var wantMaps []*Mapping - wantMapsLen := 0 - switch nvimVersion.Minor { - case 6: - wantMaps = []*Mapping{ - { - LHS: "", - RHS: "nohlsearch|diffupdate", - Silent: 0, - NoRemap: 1, - Expr: 0, - Buffer: 0, - SID: 0, - NoWait: 0, - }, - { - LHS: "Y", - RHS: "y$", - Silent: 0, - NoRemap: 1, - Expr: 0, - Buffer: 0, - SID: 0, - NoWait: 0, - }, - { - LHS: "y", - RHS: "yy", - Silent: 0, - NoRemap: 0, - Expr: 0, - Buffer: 0, - SID: 0, - NoWait: 0, - }, - } - wantMapsLen = 2 - case 7: - wantMaps = []*Mapping{ - { - LHS: "", - RHS: "nohlsearch|diffupdate|normal! ", - Silent: 0, - NoRemap: 1, - Expr: 0, - Buffer: 0, - SID: 0, - NoWait: 0, - }, - { - LHS: "Y", - RHS: "y$", - Silent: 0, - NoRemap: 1, - Expr: 0, - Buffer: 0, - SID: 0, - NoWait: 0, - }, - { - LHS: "y", - RHS: "yy", - Silent: 0, - NoRemap: 0, - Expr: 0, - Buffer: 0, - SID: -9, - NoWait: 0, - }, - } - wantMapsLen = 2 - case 8: - wantMaps = []*Mapping{ - { - LHS: "&", - RHS: ":&&", - Silent: 0, - NoRemap: 1, - Expr: 0, - Buffer: 0, - SID: -8, - NoWait: 0, - }, - { - LHS: "Y", - RHS: "y$", - Silent: 0, - NoRemap: 1, - Expr: 0, - Buffer: 0, - SID: -8, - NoWait: 0, - }, - { - LHS: "y", - RHS: "yy", - Silent: 0, - NoRemap: 0, - Expr: 0, - Buffer: 0, - SID: -9, - NoWait: 0, - }, - { - LHS: "", - RHS: "nohlsearch|diffupdate|normal! ", - Silent: 0, - NoRemap: 1, - Expr: 0, - Buffer: 0, - SID: -8, - NoWait: 0, - }, - } - wantMapsLen = 3 - default: - wantMaps = []*Mapping{ - { - LHS: "&", - RHS: ":&&", - Silent: 0, - NoRemap: 1, - Expr: 0, - Buffer: 0, - SID: -8, - NoWait: 0, - }, - { - LHS: "Y", - RHS: "y$", - Silent: 0, - NoRemap: 1, - Expr: 0, - Buffer: 0, - SID: -8, - NoWait: 0, - }, - { - LHS: "y", - RHS: "yy", - Silent: 0, - NoRemap: 0, - Expr: 0, - Buffer: 0, - SID: -9, - NoWait: 0, - }, - { - LHS: "", - RHS: "nohlsearch|diffupdate|normal! ", - Silent: 0, - NoRemap: 1, - Expr: 0, - Buffer: 0, - SID: -8, - NoWait: 0, - }, - } - wantMapsLen = 3 + lhs := "y" + rhs := "yy" + if err := v.SetKeyMap(mode, lhs, rhs, make(map[string]bool)); err != nil { + t.Fatal(err) } - got, err := v.KeyMap(mode) + added, err := v.KeyMap(mode) if err != nil { t.Fatal(err) } - if !reflect.DeepEqual(got, wantMaps) { - for i, gotmap := range got { - t.Logf(" got[%d]: %#v", i, gotmap) - } - for i, wantmap := range wantMaps { - t.Logf("want[%d]: %#v", i, wantmap) + + if len(added) != len(original)+1 { + t.Fatalf("want %d but got %d", len(original)+1, len(added)) + } + + var keymap *Mapping + for _, m := range added { + if m.LHS == "y" { + keymap = m + break } - t.Fatalf("KeyMap(%s) = %#v, want: %#v", mode, got, wantMaps) } - if err := v.DeleteKeyMap(mode, "y"); err != nil { + if keymap.LHS != lhs && keymap.RHS != rhs { + t.Fatalf("want { LHS = %s, RHS = %s } but got { LHS = %s, RHS = %s }", lhs, rhs, keymap.LHS, keymap.RHS) + } + + if err := v.DeleteKeyMap(mode, lhs); err != nil { t.Fatal(err) } - got2, err := v.KeyMap(mode) + deleted, err := v.KeyMap(mode) if err != nil { t.Fatal(err) } - if len(got2) != wantMapsLen { - t.Fatalf("expected %d but got %#v", wantMapsLen, got2) + + if len(deleted) != len(original) { + t.Fatalf("want %d but got %d", len(original), len(added)) } }) @@ -3398,202 +3272,58 @@ func testKey(v *Nvim) func(*testing.T) { t.Run("KeyMap", func(t *testing.T) { skipBetweenVersion(t, "v0.7.0", "v0.8.0") + mode := "n" b := v.NewBatch() - mode := "n" - b.SetKeyMap(mode, "y", "yy", make(map[string]bool)) + var original []*Mapping + b.KeyMap(mode, &original) if err := b.Execute(); err != nil { t.Fatal(err) } - var wantMaps []*Mapping - wantMapsLen := 0 - switch nvimVersion.Minor { - case 6: - wantMaps = []*Mapping{ - { - LHS: "", - RHS: "nohlsearch|diffupdate", - Silent: 0, - NoRemap: 1, - Expr: 0, - Buffer: 0, - SID: 0, - NoWait: 0, - }, - { - LHS: "Y", - RHS: "y$", - Silent: 0, - NoRemap: 1, - Expr: 0, - Buffer: 0, - SID: 0, - NoWait: 0, - }, - { - LHS: "y", - RHS: "yy", - Silent: 0, - NoRemap: 0, - Expr: 0, - Buffer: 0, - SID: 0, - NoWait: 0, - }, - } - wantMapsLen = 2 - case 7: - wantMaps = []*Mapping{ - { - LHS: "", - RHS: "nohlsearch|diffupdate|normal! ", - Silent: 0, - NoRemap: 1, - Expr: 0, - Buffer: 0, - SID: 0, - NoWait: 0, - }, - { - LHS: "Y", - RHS: "y$", - Silent: 0, - NoRemap: 1, - Expr: 0, - Buffer: 0, - SID: 0, - NoWait: 0, - }, - { - LHS: "y", - RHS: "yy", - Silent: 0, - NoRemap: 0, - Expr: 0, - Buffer: 0, - SID: -9, - NoWait: 0, - }, - } - wantMapsLen = 2 - case 8: - wantMaps = []*Mapping{ - { - LHS: "&", - RHS: ":&&", - Silent: 0, - NoRemap: 1, - Expr: 0, - Buffer: 0, - SID: -8, - NoWait: 0, - }, - { - LHS: "Y", - RHS: "y$", - Silent: 0, - NoRemap: 1, - Expr: 0, - Buffer: 0, - SID: -8, - NoWait: 0, - }, - { - LHS: "y", - RHS: "yy", - Silent: 0, - NoRemap: 0, - Expr: 0, - Buffer: 0, - SID: -9, - NoWait: 0, - }, - { - LHS: "", - RHS: "nohlsearch|diffupdate|normal! ", - Silent: 0, - NoRemap: 1, - Expr: 0, - Buffer: 0, - SID: -8, - NoWait: 0, - }, - } - wantMapsLen = 3 - default: - wantMaps = []*Mapping{ - { - LHS: "&", - RHS: ":&&", - Silent: 0, - NoRemap: 1, - Expr: 0, - Buffer: 0, - SID: -8, - NoWait: 0, - }, - { - LHS: "Y", - RHS: "y$", - Silent: 0, - NoRemap: 1, - Expr: 0, - Buffer: 0, - SID: -8, - NoWait: 0, - }, - { - LHS: "y", - RHS: "yy", - Silent: 0, - NoRemap: 0, - Expr: 0, - Buffer: 0, - SID: -9, - NoWait: 0, - }, - { - LHS: "", - RHS: "nohlsearch|diffupdate|normal! ", - Silent: 0, - NoRemap: 1, - Expr: 0, - Buffer: 0, - SID: -8, - NoWait: 0, - }, - } - wantMapsLen = 3 + lhs := "y" + rhs := "yy" + + b.SetKeyMap(mode, lhs, rhs, make(map[string]bool)) + if err := b.Execute(); err != nil { + t.Fatal(err) } - var got []*Mapping - b.KeyMap(mode, &got) + var added []*Mapping + b.KeyMap(mode, &added) if err := b.Execute(); err != nil { t.Fatal(err) } - if !reflect.DeepEqual(got, wantMaps) { - for i, gotmap := range got { - t.Logf(" got[%d]: %#v", i, gotmap) - } - for i, wantmap := range wantMaps { - t.Logf("want[%d]: %#v", i, wantmap) + + if len(added) != len(original)+1 { + t.Fatalf("want %d but got %d", len(original)+1, len(added)) + } + + var keymap *Mapping + for _, m := range added { + if m.LHS == "y" { + keymap = m + break } - t.Fatalf("KeyMap(%s) = %#v, want: %#v", mode, got, wantMaps) } - b.DeleteKeyMap(mode, "y") + if keymap.LHS != lhs && keymap.RHS != rhs { + t.Fatalf("want { LHS = %s, RHS = %s } but got { LHS = %s, RHS = %s }", lhs, rhs, keymap.LHS, keymap.RHS) + } + + b.DeleteKeyMap(mode, lhs) if err := b.Execute(); err != nil { t.Fatal(err) } - var got2 []*Mapping - b.KeyMap(mode, &got2) + var deleted []*Mapping + b.KeyMap(mode, &deleted) if err := b.Execute(); err != nil { t.Fatal(err) } - if len(got2) != wantMapsLen { - t.Fatalf("expected %d but got %#v", wantMapsLen, got2) + + if len(deleted) != len(original) { + t.Fatalf("want %d but got %d", len(original), len(added)) } }) @@ -3840,8 +3570,8 @@ func testHighlight(v *Nvim) func(*testing.T) { const testHLName = `Test` testHLAttrs := &HLAttrs{ Bold: true, - CtermForeground: cm[`Red`], - CtermBackground: cm[`Yellow`], + CtermForeground: 12, + CtermBackground: 14, } if err := v.SetHighlight(nsID, testHLName, testHLAttrs); err != nil { t.Fatal(err) @@ -3917,8 +3647,8 @@ func testHighlight(v *Nvim) func(*testing.T) { const testHLName = `Test` testHLAttrs := &HLAttrs{ Bold: true, - CtermForeground: cm[`Red`], - CtermBackground: cm[`Yellow`], + CtermForeground: 12, + CtermBackground: 14, } b.SetHighlight(nsID, testHLName, testHLAttrs) if err := b.Execute(); err != nil { diff --git a/nvim/plugin/plugin_test.go b/nvim/plugin/plugin_test.go index b515d79a..4ec59711 100644 --- a/nvim/plugin/plugin_test.go +++ b/nvim/plugin/plugin_test.go @@ -386,6 +386,22 @@ func TestSubscribe(t *testing.T) { t.Parallel() p := plugin.New(nvimtest.NewChildProcess(t)) + apiInfo, err := p.Nvim.APIInfo() + if err != nil { + t.Fatal(err) + } + if len(apiInfo) != 2 { + t.Fatalf("unknown APIInfo: %#v", apiInfo) + } + + info, ok := apiInfo[1].(map[string]any) + if !ok { + t.Fatalf("apiInfo[1] is not map[string]any type: %T", apiInfo[1]) + } + version := info["version"].(map[string]any) + if version["minor"].(int64) > 10 { + t.Skip("Since Neovim 0.11.0, rpcnotify(0) broadcasts to all channels, not just subscribed channels.") + } const event1 = "event1" eventFn1 := func(t *testing.T, v *nvim.Nvim) error { diff --git a/nvim/types.go b/nvim/types.go index 7c408411..1058a8a2 100644 --- a/nvim/types.go +++ b/nvim/types.go @@ -152,7 +152,7 @@ type HLAttrs struct { // Cterm is cterm attribute map. Sets attributed for cterm colors. // - // Note thet by default cterm attributes are same as attributes of gui color. + // Note that by default cterm attributes are same as attributes of gui color. // // This value is used only SetHighlight. Cterm *HLAttrs `msgpack:"cterm,omitempty"`