ruby icon indicating copy to clipboard operation
ruby copied to clipboard

Return key does not trigger Language Server `onTypeFormatting` request (no `end` completion)

Open andyw8 opened this issue 8 months ago • 4 comments

Ruby LSP has a feature to automatically add ends, e.g. after def method_name. It uses the textDocument/onTypeFormatting language server request.

In Zed, this doesn't work. I suspect this is a bug in Zed rather than the extension, but I'm logging it here first until I learn more.

Example (comparing with VS Code)

In Ruby LSP with VS Code, enable verbose logging.

You'll also need to enable "editor.formatOnType": true since it's disabled by default.

Now type the following:

def foo

The logs will show a request:

Sending request 'textDocument/onTypeFormatting - (41)'.
{
    "textDocument": { "uri": "file:///Users/andy/src/github.com/Shopify/ruby-lsp/x.rb" },
    "position": { "line": 3, "character": 1 },
    "ch": "d",
    "options": { "tabSize": 2, "insertSpaces": true }
}

and an empty response

Received response 'textDocument/onTypeFormatting - (41)' in 1ms.

Now press return and you'll see a second request:

Sending request 'textDocument/onTypeFormatting - (48)'.
{
    "textDocument": { "uri": "file:///Users/andy/src/github.com/Shopify/ruby-lsp/x.rb" },
    "position": { "line": 4, "character": 2 },
    "ch": "\n",
    "options": {
        "tabSize": 2,
        "insertSpaces": true
    }
}

and a response, which causes an end to be added:

[
    {
        "range": {
            "start": { "line": 4, "character": 2 },
            "end": { "line": 4, "character": 2 }
        },
        "newText": "\n"
    },
    {
        "range": {
            "start": { "line": 4, "character": 2 },
            "end": { "line": 4, "character": 2 }
        },
        "newText": "end"
    },
    {
        "range": {
            "start": {  "line": 4, "character": 2 },
            "end": { "line": 4, "character": 2 }
        },
        "newText": "$0"
    }
]

In Zed, we see the first request:

// Send:
{"jsonrpc":"2.0","id":39,"method":"textDocument/onTypeFormatting",
"params":{"textDocument":{"uri":"file:///Users/andy/src/github.com/Shopify/ruby-lsp/x.rb"},"position":{"line":0,"character":1},
"ch":"d",
"options":{"tabSize":2,"insertSpaces":true,"trimTrailingWhitespace":true,"insertFinalNewline":true,"trimFinalNewlines":true}}}

// Receive:
{"id":39,"result":[],"jsonrpc":"2.0"}

but after pressing return, there is no second request.

#38 may be related.

andyw8 avatar Apr 23 '25 17:04 andyw8

I looked to see how the Lua extension handles this, since it also has end, but it's a different approach. It's based on textDocument/publishDiagnostics. So after you type if true then, a suggested fix pops up to add the end, and hitting return selects that.

// Receive:
{
  "jsonrpc": "2.0",
  "method": "textDocument/publishDiagnostics",
  "params": {
    "diagnostics": [
      {
        "code": "miss-exp",
        "data": "syntax",
        "message": "<exp> expected.",
        "range": {
          "end": {
            "character": 2,
            "line": 0
          },
          "start": {
            "character": 2,
            "line": 0
          }
        },
        "severity": 1,
        "source": "Lua Syntax Check."
      },
      {
        "code": "miss-end",
        "data": "syntax",
        "message": "Miss corresponding `end` .",
        "range": {
          "end": {
            "character": 2,
            "line": 0
          },
          "start": {
            "character": 0,
            "line": 0
          }
        },
        "severity": 1,
        "source": "Lua Syntax Check."
      }
    ],
    "uri": "/Users/andy/src/github.com/Shopify/ruby-lsp/x.lua",
    "version": 238
  }
}

// Receive:
{
  "id": 926,
  "jsonrpc": "2.0",
  "result": {
    "insertText": "if $1 then\n\t$0\nend",
    "insertTextFormat": 2,
    "kind": 15,
    "label": "if .. then",
    "sortText": "0001"
  }
}

andyw8 avatar Apr 23 '25 17:04 andyw8

This likely relates to more_trigger_character:

https://github.com/Shopify/ruby-lsp/blob/f3ff6a36afbc7134d640c8e6be54c7c8f63f0aaf/lib/ruby_lsp/requests/on_type_formatting.rb#L15

Mentioned here (moreTriggerCharacter) in the spec:

https://microsoft.github.io/language-server-protocol/specifications/lsp/3.17/specification/#textDocument_onTypeFormatting

andyw8 avatar Apr 23 '25 19:04 andyw8

This seems to be a Zed issue. According to Claude Code's codebase inspection, Zed doesn't send document/onTypeFormatting requests when pressing enter, not only in Ruby files.

AI summary
⏺ Based on my analysis of the Zed codebase, no, Zed does NOT send document/onTypeFormatting LSP requests when pressing Enter.

  Here's what I found:

  Key Evidence

  1. Separate code paths:
    - Regular character input goes through handle_input() (editor.rs:4037) which calls trigger_on_type_formatting() (editor.rs:4380)
    - Pressing Enter uses a dedicated newline() function (editor.rs:4468) that does NOT call trigger_on_type_formatting()
  2. The newline function: The newline() function handles its own logic for inserting newlines, comment continuation, and indentation, but never triggers on-type formatting.
  3. Test evidence: The test test_on_type_formatting_is_applied_after_autoindent (editor_tests.rs:17073) confirms this:
    - It simulates pressing Enter at line 17094
    - Then simulates pressing "." at line 17114
    - Only the "." character triggers the on-type formatting request, not the newline
  4. Implementation details: While trigger_on_type_formatting() only accepts single-character input (editor.rs:5398), which "\n" would satisfy, the newline action bypasses this
  code path entirely.

  So even if a language server declares "\n" as a trigger character in its documentOnTypeFormattingProvider, Zed won't send the request because Enter key presses use a separate
  implementation that doesn't participate in on-type formatting.

janko avatar Nov 19 '25 11:11 janko

I had opened https://github.com/zed-industries/zed/issues/12383, but if it's indeed not specific to Ruby, I think it should be opened as a generic issue.

janko avatar Nov 19 '25 11:11 janko