Skip to content

Conversation

@hello-42
Copy link

I ran into a few issues consecutively when attempting to use the plugins feature:

  1. Any time a non-empty edits table was returned from a plugin's transformSource callback, the process for viewing the internal source would break. This told me that something relevant to transforming the source from the edits was breaking. The fix for this was to extract the transformed source prior to pluginDoc and then map the transformed source into the document.

  2. When using lsp.json.deserialize(), deserializing my sourcemap.json was causing an unexpected stack overflow for the table returned by deserialization. The fix to the error being emitted internally by luau-lsp below was to check if there's space on the stack:

  • lapi.cpp(813): ASSERTION FAILED: L->top < L->ci->top
  1. Post-transformation, TextDocuments and PluginTextDocuments would reference the transformed source's Location data to act as a source for the completions, definitions, diagnostics, inlayHints, and signatureHelp features - which led to the error listed below being emitted internally by luau-lsp for each feature, respectively. The fix was to return the original source's Location data instead.
  • Received response 'textDocument/[...] - (297)' in 3ms. Request failed: invalid string position (-32603)

Some notes:

  • I used AI (Opus 4.5) to make these changes. I don't know how to write C++, and I simply told my agent what issues I was facing. With that being said, I'm making this PR without the expectation that it'll be merged - mainly just to show what changes were made that helped me resolve these issues.
  • I fed my agent any errors that I could witness coming from the binary's output, and lo and behold, these changes resulted and I was able to produce a build that works, supports the plugin functionality, and ultimately results in providing the intellisense I was looking for.

Fix undefined behavior from unspecified argument evaluation order

When constructing PluginTextDocument, `mapping.getTransformedSource()`
and `std::move(mapping)` were passed in the same function call. Since
C++ argument evaluation order is unspecified, the move could happen
before getTransformedSource() was called, resulting in an empty
transformed source string.

Extract the transformed source to a local variable before the
constructor call to ensure correct sequencing.

This fixes the "View Internal Source" debug command returning empty
content when plugins return non-empty TextEdits.
Add lua_checkstack to prevent stack overflow in lsp.json.deserialize

The pushJsonValue helper function recursively pushes values onto the
Lua stack without checking for available space. When parsing large
JSON files (like Roblox sourcemaps), this causes a stack overflow
assertion failure.

Add lua_checkstack(L, 3) at the start of pushJsonValue to ensure
sufficient stack space before each recursive call.
Fix virtual method mismatch causing LSP feature failures

PluginTextDocument overrides getLineOffsets() to return transformed
content line offsets, but the base TextDocument::convertPosition()
methods call getLineOffsets() (virtual) while using _content (original).
This mismatch caused all position-based LSP features to fail with
"invalid string position" errors.

Changes:
- Add getOriginalLineOffsets() helper with caching for original content
- Reimplement convertPosition(lsp::Position) to use original line
  offsets with proper UTF-16 to UTF-8 conversion
- Reimplement convertPosition(Luau::Position) to use original line
  offsets for the reverse mapping
- Add bounds checking in offsetAt() and getText() to prevent
  out-of-bounds string access

This fixes textDocument/completion, textDocument/definition,
textDocument/diagnostic, textDocument/inlayHint, and
textDocument/signatureHelp when plugins are active.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant