Skip to content

On type formatting for blocks is unhelpful due to cursor placement #2971

Closed

Description

@thedanbob

Description

Ruby LSP Information

VS Code Version

1.95.3

Ruby LSP Extension Version

0.8.16

Ruby LSP Server Version

0.22.1

Ruby LSP Addons

  • Ruby LSP Rails

Ruby Version

3.3.6

Ruby Version Manager

custom

Installed Extensions

Click to expand
  • amx-netlinx (0.6.1)
  • better-toml (0.3.2)
  • copy-text (0.4.9)
  • cpptools (1.22.11)
  • csharp (2.55.29)
  • elixir-ls (0.24.2)
  • gitlens (16.0.5)
  • go (0.42.1)
  • haml (1.4.1)
  • isort (2023.10.1)
  • material-icon-theme (5.15.0)
  • platformio-ide (3.3.3)
  • postcss (2.0.0)
  • python (2024.20.0)
  • ruby-lsp (0.8.16)
  • rust-analyzer (0.3.2212)
  • vscode-ansi (1.1.7)
  • vscode-dotnet-runtime (2.2.3)
  • vscode-eslint (3.0.10)
  • vscode-npm-script (0.3.29)
  • vscode-projects-plus (1.24.0)
  • vscode-zipfs (3.0.0)

Ruby LSP Settings

Click to expand
Workspace
{}
User
{
  "enabledFeatures": {
    "codeActions": true,
    "diagnostics": true,
    "documentHighlights": true,
    "documentLink": true,
    "documentSymbols": true,
    "foldingRanges": true,
    "formatting": true,
    "hover": true,
    "inlayHint": true,
    "onTypeFormatting": true,
    "selectionRanges": true,
    "semanticHighlighting": true,
    "completion": true,
    "codeLens": true,
    "definition": true,
    "workspaceSymbol": true,
    "signatureHelp": true,
    "typeHierarchy": true
  },
  "featuresConfiguration": {},
  "addonSettings": {},
  "rubyVersionManager": {
    "identifier": "custom"
  },
  "customRubyCommand": "rbenv init - fish | source",
  "formatter": "auto",
  "linters": null,
  "bundleGemfile": "",
  "testTimeout": 30,
  "branch": "",
  "pullDiagnosticsOn": "both",
  "useBundlerCompose": false,
  "bypassTypechecker": false,
  "rubyExecutablePath": "",
  "indexing": {},
  "erbSupport": true,
  "featureFlags": {}
}

Reproduction steps

ruby-lsp auto-completes ruby blocks which is potentially a great feature, but the implementation is not quite there. Examples:

  • Type method do |. ruby-lsp autocompletes the argument pipes to method do ||, but the cursor is after the closing pipe, so attempting to finish typing e.g. |a| results in method do ||a||. You have to remember to arrow left before typing the argument.
  • Type def method_name<enter>. ruby-lsp autocompletes the method structure with an empty line and end, but the cursor is after end so you have to remember to arrow up before typing the method body. Same with do ... end blocks.

This behavior IMO is unexpected because in other cases where VSCode autocompletes, the cursor is placed where you would expect. E.g. if you type " or ( you get "" or () but the cursor is between the two characters and you can immediately continue typing.

I really like the completion feature in concept, but since the cursor is placed in the wrong spot it slows me down to the point where disabling completion and typing the whole block myself is faster. Especially since completion isn't enabled in ERB files, so working on a Rails project I have to keep track of the context to type blocks successfully.

Activity

  1. added
    bugSomething isn't working
    vscodeThis pull request should be included in the VS Code extension's release notes
    on Dec 11, 2024
  2. Thomascountz commented on Dec 11, 2024

    @Thomascountz

    I've experienced this as well. Here are the RPC messages. (Cursor position is denoted by █)

    DATA.readlines do |█ 

    Results in:

    DATA.readlines do ||█ 
    // Send:
    {
      "jsonrpc": "2.0",
      "method": "textDocument/didChange",
      "params": {
        "textDocument": {
          "uri": "file:///Users/thomascountz/Code/pipe-character-test.rb",
          "version": 179
        },
        "contentChanges": [
          {
            "range": {
              "start": {
                "line": 5,
                "character": 18
              },
              "end": {
                "line": 5,
                "character": 18
              }
            },
            "text": "|"
          }
        ]
      }
    }
    // Send:
    {
      "jsonrpc": "2.0",
      "id": 465,
      "method": "textDocument/onTypeFormatting",
      "params": {
        "textDocument": {
          "uri": "file:///Users/thomascountz/Code/pipe-character-test.rb"
        },
        "position": {
          "line": 5,
          "character": 19
        },
        "ch": "|",
        "options": {
          "tabSize": 2,
          "insertSpaces": true,
          "trimTrailingWhitespace": true,
          "insertFinalNewline": true,
          "trimFinalNewlines": true
        }
      }
    }
    // Receive:
    {
      "id": 465,
      "result": [
        {
          "range": {
            "start": {
              "line": 5,
              "character": 19
            },
            "end": {
              "line": 5,
              "character": 19
            }
          },
          "newText": "|"
        }
      ],
      "jsonrpc": "2.0"
    }
    // Send:
    {
      "jsonrpc": "2.0",
      "method": "textDocument/didChange",
      "params": {
        "textDocument": {
          "uri": "file:///Users/thomascountz/Code/pipe-character-test.rb",
          "version": 180
        },
        "contentChanges": [
          {
            "range": {
              "start": {
                "line": 5,
                "character": 19
              },
              "end": {
                "line": 5,
                "character": 19
              }
            },
            "text": "|"
          }
        ]
      }
    }
    // Send:
    {
      "jsonrpc": "2.0",
      "id": 466,
      "method": "textDocument/documentHighlight",
      "params": {
        "textDocument": {
          "uri": "file:///Users/thomascountz/Code/pipe-character-test.rb"
        },
        "position": {
          "line": 5,
          "character": 20
        }
      }
    }
    // Receive:
    {
      "id": 466,
      "result": [],
      "jsonrpc": "2.0"
    }
    // Send:
    {
      "jsonrpc": "2.0",
      "id": 467,
      "method": "textDocument/codeAction",
      "params": {
        "textDocument": {
          "uri": "file:///Users/thomascountz/Code/pipe-character-test.rb"
        },
        "range": {
          "start": {
            "line": 5,
            "character": 20
          },
          "end": {
            "line": 5,
            "character": 20
          }
        },
        "context": {
          "diagnostics": [],
          "only": [
            "",
            "quickfix",
            "refactor",
            "refactor.extract",
            "source"
          ]
        }
      }
    }
    // Receive:
    {
      "id": 467,
      "result": [],
      "jsonrpc": "2.0"
    }
    // Send:
    {
      "jsonrpc": "2.0",
      "method": "workspace/didChangeWatchedFiles",
      "params": {
        "changes": [
          {
            "uri": "file:///Users/thomascountz/Code/pipe-character-test.rb",
            "type": 2
          }
        ]
      }
    }
  3. removed
    vscodeThis pull request should be included in the VS Code extension's release notes
    on Dec 13, 2024
  4. added
    pinnedThis issue or pull request is pinned and won't be marked as stale
    on Jan 2, 2025
  5. andyw8 commented on Mar 31, 2025

    @andyw8
    Contributor

    🤔 I noticed this while trying Zed, but I can't reproduce in VS Code.

  6. jondavidschober commented on Nov 13, 2025

    @jondavidschober

    Any updates on this one? Or a good way to disable this? I'm experiencing this on VSCode/Cursor

  7. vinistock commented on Nov 13, 2025

    @vinistock
  8. nathanvy commented on Dec 30, 2025

    @nathanvy

    Just chiming in to note that this issue is reproducible on emacs with lsp-mode and makes writing code quickly pretty painful due to the incorrect cursor placement after the block's end gets completed.

    Expected behaviour:

    def foo
       | # <- cursor ends up here
    end
    

    actual behaviour:

    def foo
    
    end     | # <- cursor ends up here, with increasing whitespace based on lexical scope/depth
    

    @vinistock is there a particular branch where this issue is being worked on? Happy to help if able.

  9. vinistock commented on Jan 7, 2026

    @vinistock
    Member

    I think we should turn off on type formatting for editors that don't support snippets since they doesn't give us the ability to move the user's cursor to the applicable position.

    Instead of checking the client's name only for moving the cursor, we should probably completely avoid any modifications.

    There's no branch for this, my previous comment was incorrect (I got confused with the issue's title). If someone wants to work on it, I'm happy to help and review.

  10. changed the title [-]Code completion for blocks is unhelpful due to cursor placement[/-] [+]On type formatting for blocks is unhelpful due to cursor placement[/+] on Jan 7, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    bugSomething isn't workinghelp-wantedExtra attention is neededpinnedThis issue or pull request is pinned and won't be marked as stale

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions