diff --git a/runtime/lua/vim/lsp/util.lua b/runtime/lua/vim/lsp/util.lua
index 988057f5f959a2a08d09c443d649e2a93726c34a..0d1e3cc0d18edc929a3a85b7b556704b1d167779 100644
--- a/runtime/lua/vim/lsp/util.lua
+++ b/runtime/lua/vim/lsp/util.lua
@@ -2230,6 +2230,35 @@ function M.lookup_section(settings, section)
   return settings
 end
 
+--- Converts line range (0-based, end-inclusive) to lsp range,
+--- handles absence of a trailing newline
+---
+---@param bufnr integer
+---@param start_line integer
+---@param end_line integer
+---@param offset_encoding lsp.PositionEncodingKind
+---@return lsp.Range
+local function make_line_range_params(bufnr, start_line, end_line, offset_encoding)
+  local last_line = api.nvim_buf_line_count(bufnr) - 1
+
+  ---@type lsp.Position
+  local end_pos
+
+  if end_line == last_line and not vim.api.nvim_get_option_value('endofline', { buf = bufnr }) then
+    end_pos = {
+      line = end_line,
+      character = M.character_offset(bufnr, end_line, #get_line(bufnr, end_line), offset_encoding),
+    }
+  else
+    end_pos = { line = end_line + 1, character = 0 }
+  end
+
+  return {
+    start = { line = start_line, character = 0 },
+    ['end'] = end_pos,
+  }
+end
+
 ---@private
 --- Request updated LSP information for a buffer.
 ---
@@ -2253,6 +2282,8 @@ function M._refresh(method, opts)
     return
   end
 
+  local textDocument = M.make_text_document_params(bufnr)
+
   local only_visible = opts.only_visible or false
 
   if only_visible then
@@ -2260,28 +2291,25 @@ function M._refresh(method, opts)
       if api.nvim_win_get_buf(window) == bufnr then
         local first = vim.fn.line('w0', window)
         local last = vim.fn.line('w$', window)
-        local params = {
-          textDocument = M.make_text_document_params(bufnr),
-          range = {
-            start = { line = first - 1, character = 0 },
-            ['end'] = { line = last, character = 0 },
-          },
-        }
         for _, client in ipairs(clients) do
-          client.request(method, params, nil, bufnr)
+          client.request(method, {
+            textDocument = textDocument,
+            range = make_line_range_params(bufnr, first - 1, last - 1, client.offset_encoding),
+          }, nil, bufnr)
         end
       end
     end
   else
-    local params = {
-      textDocument = M.make_text_document_params(bufnr),
-      range = {
-        start = { line = 0, character = 0 },
-        ['end'] = { line = api.nvim_buf_line_count(bufnr), character = 0 },
-      },
-    }
     for _, client in ipairs(clients) do
-      client.request(method, params, nil, bufnr)
+      client.request(method, {
+        textDocument = textDocument,
+        range = make_line_range_params(
+          bufnr,
+          0,
+          api.nvim_buf_line_count(bufnr) - 1,
+          client.offset_encoding
+        ),
+      }, nil, bufnr)
     end
   end
 end
diff --git a/test/functional/fixtures/fake-lsp-server.lua b/test/functional/fixtures/fake-lsp-server.lua
index ef87f6c21a4c06c57aa294dd370d417924341202..0db9265a2926de31e3cbd34545d507e3696d33ff 100644
--- a/test/functional/fixtures/fake-lsp-server.lua
+++ b/test/functional/fixtures/fake-lsp-server.lua
@@ -949,6 +949,28 @@ function tests.set_defaults_all_capabilities()
   }
 end
 
+function tests.inlay_hint()
+  skeleton {
+    on_init = function(params)
+      local expected_capabilities = protocol.make_client_capabilities()
+      assert_eq(params.capabilities, expected_capabilities)
+      return {
+        capabilities = {
+          inlayHintProvider = true;
+        }
+      }
+    end;
+    body = function()
+      notify('start')
+      expect_request('textDocument/inlayHint', function()
+        return nil, {}
+      end)
+      expect_notification("finish")
+      notify('finish')
+    end;
+  }
+end
+
 -- Tests will be indexed by test_name
 local test_name = arg[1]
 local timeout = arg[2]
diff --git a/test/functional/plugin/lsp_spec.lua b/test/functional/plugin/lsp_spec.lua
index 7e30af505879c29cea735855e7caad4dc8942dcb..155c9ad96c70503a459fc3e5cb878a58c426e555 100644
--- a/test/functional/plugin/lsp_spec.lua
+++ b/test/functional/plugin/lsp_spec.lua
@@ -1244,6 +1244,67 @@ describe('LSP', function()
       }
     end)
 
+    it('should send correct range for inlay hints with noeol', function()
+      local expected_handlers = {
+        {NIL, {}, {method="shutdown", client_id=1}};
+        {NIL, {}, {method="finish", client_id=1}};
+        {NIL, {}, {
+          method="textDocument/inlayHint",
+          params = {
+            textDocument = {
+              uri = 'file://',
+            },
+            range = {
+              start = { line = 0, character = 0 },
+              ['end'] = { line = 1, character = 3 },
+            }
+          },
+          bufnr=2,
+          client_id=1,
+        }};
+        {NIL, {}, {method="start", client_id=1}};
+      }
+      local client
+      test_rpc_server {
+        test_name = "inlay_hint";
+        on_setup = function()
+          exec_lua [[
+            BUFFER = vim.api.nvim_create_buf(false, true)
+            vim.api.nvim_buf_set_lines(BUFFER, 0, -1, false, {
+              "testing";
+              "123";
+            })
+            vim.bo[BUFFER].eol = false
+          ]]
+        end;
+        on_init = function(_client)
+          client = _client
+          eq(true, client.supports_method('textDocument/inlayHint'))
+          exec_lua [[
+            assert(lsp.buf_attach_client(BUFFER, TEST_RPC_CLIENT_ID))
+          ]]
+        end;
+        on_exit = function(code, signal)
+          eq(0, code, "exit code")
+          eq(0, signal, "exit signal")
+        end;
+        on_handler = function(err, result, ctx)
+          if ctx.method == 'start' then
+            exec_lua [[
+              vim.lsp.inlay_hint(BUFFER, true)
+            ]]
+          end
+          if ctx.method == 'textDocument/inlayHint' then
+            client.notify('finish')
+          end
+          eq(table.remove(expected_handlers), {err, result, ctx}, "expected handler")
+          if ctx.method == 'finish' then
+            client.stop()
+          end
+        end;
+      }
+    end)
+
     it('should check the body and didChange incremental', function()
       local expected_handlers = {
         {NIL, {}, {method="shutdown", client_id=1}};