Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

slangd fails to start with Neovim LSP due NULL/missing value in JSON #3322

Closed
theHamsta opened this issue Nov 11, 2023 · 5 comments · Fixed by #3336
Closed

slangd fails to start with Neovim LSP due NULL/missing value in JSON #3322

theHamsta opened this issue Nov 11, 2023 · 5 comments · Fixed by #3336

Comments

@theHamsta
Copy link
Contributor

theHamsta commented Nov 11, 2023

First, thanks for this awesome project!

When I was playing around with it I tried to use slangd with Neovim's built-in LSP implementation, I realized that it was crashing for some reason. I tried to debug the reason with gdb and saw that slangd was hitting this assert here

SLANG_ASSERT(!"Not a string type");
, because the in value in this function was of type JSONValue::Type::Null. I couldn't debug yet which value slang was expecting or whether this value was allowed to be null/missing according to the LSP specification (no experience with slangs code base yet).

Applying the following workaround
theHamsta@643393c, makes slangd working as expected with Neovim using the following config for https://github.com/neovim/nvim-lspconfig

  local configs = require "lspconfig.configs"
  local nvim_lsp = require "lspconfig"

  if not configs.slangd then
    configs.slangd = {
      default_config = {
        cmd = { "slangd", "--debug" },
        filetypes = { "slang", "hlsl" },
        root_dir = function(fname)
          return nvim_lsp.util.find_git_ancestor(fname)
        end,
        single_file_support = true,
      },
    }
    nvim_lsp.slangd.setup {}
  end

--debug is only used to be able to attach gdb in a sudo shell with gdb -p $(pgrep slangd)

Here Neovim's LSP log when opening a single slang file when using it with unpatched slangd (with require("vim.lsp.log").set_level(vim.log.levels.DEBUG):

[START][2023-11-11 16:03:46] LSP logging initiated
[INFO][2023-11-11 16:03:46] .../vim/lsp/rpc.lua:634	"Starting RPC client"	{  args = { "--debug" },  cmd = "/home/stephan/projects/slang/bin/linux-x64/debug/slangd",  extra = {    cwd = "/home/stephan/projects/slang"  }}
[DEBUG][2023-11-11 16:03:46] .../vim/lsp/rpc.lua:261	"rpc.send"	{  id = 1,  jsonrpc = "2.0",  method = "initialize",  params = {    capabilities = {      general = {        positionEncodings = { "utf-16" }      },      textDocument = {        callHierarchy = {          dynamicRegistration = false        },        codeAction = {          codeActionLiteralSupport = {            codeActionKind = {              valueSet = { "", "quickfix", "refactor", "refactor.extract", "refactor.inline", "refactor.rewrite", "source", "source.organizeImports" }            }          },          dataSupport = true,          dynamicRegistration = true,          isPreferredSupport = true,          resolveSupport = {            properties = { "edit" }          }        },        completion = {          completionItem = {            commitCharactersSupport = true,            deprecatedSupport = true,            documentationFormat = { "markdown", "plaintext" },            insertReplaceSupport = true,            insertTextModeSupport = {              valueSet = { 1, 2 }            },            labelDetailsSupport = true,            preselectSupport = true,            resolveSupport = {              properties = { "documentation", "detail", "additionalTextEdits" }            },            snippetSupport = true,            tagSupport = {              valueSet = { 1 }            }          },          completionItemKind = {            valueSet = { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25 }          },          completionList = {            itemDefaults = { "commitCharacters", "editRange", "insertTextFormat", "insertTextMode", "data" }          },          contextSupport = true,          dynamicRegistration = false,          insertTextMode = 1        },        declaration = {          linkSupport = true        },        definition = {          dynamicRegistration = true,          linkSupport = true        },        diagnostic = {          dynamicRegistration = false        },        documentHighlight = {          dynamicRegistration = false        },        documentSymbol = {          dynamicRegistration = false,          hierarchicalDocumentSymbolSupport = true,          symbolKind = {            valueSet = { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26 }          }        },        formatting = {          dynamicRegistration = true        },        hover = {          contentFormat = { "markdown", "plaintext" },          dynamicRegistration = true        },        implementation = {          linkSupport = true        },        inlayHint = {          dynamicRegistration = true,          resolveSupport = {            properties = {}          }        },        publishDiagnostics = {          dataSupport = true,          relatedInformation = true,          tagSupport = {            valueSet = { 1, 2 }          }        },        rangeFormatting = {          dynamicRegistration = true        },        references = {          dynamicRegistration = false        },        rename = {          dynamicRegistration = true,          prepareSupport = true        },        semanticTokens = {          augmentsSyntaxTokens = true,          dynamicRegistration = false,          formats = { "relative" },          multilineTokenSupport = false,          overlappingTokenSupport = true,          requests = {            full = {              delta = true            },            range = false          },          serverCancelSupport = false,          tokenModifiers = { "declaration", "definition", "readonly", "static", "deprecated", "abstract", "async", "modification", "documentation", "defaultLibrary" },          tokenTypes = { "namespace", "type", "class", "enum", "interface", "struct", "typeParameter", "parameter", "variable", "property", "enumMember", "event", "function", "method", "macro", "keyword", "modifier", "comment", "string", "number", "regexp", "operator", "decorator" }        },        signatureHelp = {          dynamicRegistration = false,          signatureInformation = {            activeParameterSupport = true,            documentationFormat = { "markdown", "plaintext" },            parameterInformation = {              labelOffsetSupport = true            }          }        },        synchronization = {          didSave = true,          dynamicRegistration = false,          willSave = true,          willSaveWaitUntil = true        },        typeDefinition = {          linkSupport = true        }      },      window = {        showDocument = {          support = true        },        showMessage = {          messageActionItem = {            additionalPropertiesSupport = false          }        },        workDoneProgress = true      },      workspace = {        applyEdit = true,        configuration = true,        didChangeWatchedFiles = {          dynamicRegistration = true,          relativePatternSupport = true        },        inlayHint = {          refreshSupport = true        },        semanticTokens = {          refreshSupport = true        },        symbol = {          dynamicRegistration = false,          symbolKind = {            valueSet = { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26 }          }        },        workspaceEdit = {          resourceOperations = { "rename", "create", "delete" }        },        workspaceFolders = true      }    },    clientInfo = {      name = "Neovim",      version = "0.10.0-dev+gb2ca76819"    },    initializationOptions = vim.empty_dict(),    processId = 80421,    rootPath = "/home/stephan/projects/slang",    rootUri = "file:///home/stephan/projects/slang",    trace = "off",    workspaceFolders = { {        name = "/home/stephan/projects/slang",        uri = "file:///home/stephan/projects/slang"      } }  }}
[DEBUG][2023-11-11 16:03:57] .../vim/lsp/rpc.lua:364	"rpc.receive"	{  id = 1,  jsonrpc = "2.0",  result = {    capabilities = {      completionProvider = {        allCommitCharacters = {},        resolveProvider = true,        triggerCharacters = { ".", ":", "[", '"', "/" }      },      definitionProvider = true,      documentFormattingProvider = true,      documentOnTypeFormattingProvider = {        firstTriggerCharacter = "}",        moreTriggerCharacter = { ";", ":", "{" }      },      documentRangeFormattingProvider = true,      documentSymbolProvider = true,      hoverProvider = true,      inlayHintProvider = {        resolveProvider = false      },      positionEncoding = "utf-16",      semanticTokensProvider = {        full = true,        legend = {          tokenModifiers = {},          tokenTypes = { "type", "enumMember", "variable", "parameter", "function", "property", "namespace", "keyword", "macro", "string" }        },        range = false      },      signatureHelpProvider = {        retriggerCharacters = { "," },        triggerCharacters = { "(", "," }      },      textDocumentSync = {        change = 2,        openClose = true      },      workspace = {        workspaceFolders = {          changeNotifications = false,          supported = true        }      }    },    serverInfo = {      name = "SlangLanguageServer",      version = "1.3"    }  }}
[DEBUG][2023-11-11 16:03:57] .../vim/lsp/rpc.lua:261	"rpc.send"	{  jsonrpc = "2.0",  method = "initialized",  params = vim.empty_dict()}
[INFO][2023-11-11 16:03:57] .../lua/vim/lsp.lua:1458	"LSP[slangd]"	"server_capabilities"	{  server_capabilities = {    completionProvider = {      allCommitCharacters = {},      resolveProvider = true,      triggerCharacters = { ".", ":", "[", '"', "/" }    },    definitionProvider = true,    documentFormattingProvider = true,    documentOnTypeFormattingProvider = {      firstTriggerCharacter = "}",      moreTriggerCharacter = { ";", ":", "{" }    },    documentRangeFormattingProvider = true,    documentSymbolProvider = true,    hoverProvider = true,    inlayHintProvider = {      resolveProvider = false    },    positionEncoding = "utf-16",    semanticTokensProvider = {      full = true,      legend = {        tokenModifiers = {},        tokenTypes = { "type", "enumMember", "variable", "parameter", "function", "property", "namespace", "keyword", "macro", "string" }      },      range = false    },    signatureHelpProvider = {      retriggerCharacters = { "," },      triggerCharacters = { "(", "," }    },    textDocumentSync = {      change = 2,      openClose = true    },    workspace = {      workspaceFolders = {        changeNotifications = false,        supported = true      }    }  }}
[DEBUG][2023-11-11 16:03:57] .../vim/lsp/rpc.lua:261	"rpc.send"	{  jsonrpc = "2.0",  method = "textDocument/didOpen",  params = {    textDocument = {      languageId = "hlsl",      text = "// shader-toy.slang\n\n// This file implements the core of a system for executing\n// code from shadertoy.com in the context of Slang.\n//\n// The big idea here is to define an interface so that\n// different shader toy effects can be defined as\n// separately compiled modules, and then \"plug in\" to\n// execution environment for running those effects, wheter\n// via vertex/fragment shaders, compute, or on CPU.\n//\n// An important goal is that we should be able to run effects\n// defined on shadertoy.com with as little modification as\n// possible. This goal isn't 100% achievable because shader\n// toy effects are authored in GLSL, which differs from Slang\n// in several ways, so this is an aspirational goal rather\n// than a requirement.\n//\n// There are a few different kinds of effects supported\n// by shader toy, which are enumerated on the [How To](https://www.shadertoy.com/howto)\n// page of the project. This module focuses only on\n// \"image shaders,\" which are by far the most common.\n//\n// We will start with the interface that all image shaders\n// are expected to implement.\n//\ninterface IShaderToyImageShader\n{\n    // The shader toy \"How To\" page says:\n    //\n    // > Image shaders implement the `mainImage()` function in order\n    // > to generate procedural images by computing a color for\n    // > each pixel. ...\n    //\n    // The GLSL signature given is:\n    //\n    // > void mainImage( out vec4 fragColor, in vec2 fragCoord );\n    //\n    // We can translate that signature almost verbatim into Slang:\n    //\n    void mainImage( out float4 fragColor, in float2 fragCoord );\n\n    // An image shader effect will thus be a Slang `struct` type\n    // that implements the `IShaderToyImageShader` interface.\n    // In most cases, effects will be created by pasting the\n    // GLSL content of an effect into boilerplate `struct`\n    // definition.\n    //\n    // As a result of scoping the GLSL effect code in a Slang\n    // `struct`, any functions declared in the effect will become\n    // methods of the struct, and any global-scope variables declared\n    // in the effect will become members of the `struct` type.\n    //\n    // Note: One caveat that arises here is that any effect that\n    // makes use of mutable global variables in its GLSL code will\n    // fail to compile with our approach. By default methods in\n    // Slang have can only read from `this` and its members, and\n    // need to be marked a `[mutating]` to have read-write access.\n    // Most shader toy effects only use globals to declare constants,\n    // so this limitation may not be a big problem in practice.\n    //\n    // One thing that *does* matter is that global variables/constants in\n    // an effect may have initializers, and the behavior of the\n    // effect is likely to depend on them being initialized correctly.\n    //\n    // In practice, the need to have effect-specific initialization\n    // is another requirement of the image shader interface that doesn't\n    // need to be explicitly explained on shadertoy.com, but does matter\n    // when writing an explicit interface in Slang.\n    //\n    // We express the requirement using a `static` method that\n    // returns the `This` type. Much like `this` refers to the\n    // \"current object\" with whatever type it might have at runtime,\n    // the `This` type refers to the \"current type\" that is implementing\n    // this interface. Static methods that return `This` allow\n    // Slang to express required \"factory\" functions in an interface.\n    //\n    static This getDefault();\n};\n\n// Now that we have defined the interface that all image\n// shader effects are expected to implement, we can define\n// a vertex and fragment shader that can be used to evaluate\n// any effect that conforms to the interface.\n//\n// The vertex shader is just going to implement a full-screen\n// triangle, so it is almost trivial:\n//\n[shader(\"vertex\")]\nfloat4 vertexMain(float2 position : POSITION)\n    : SV_Position\n{\n    // TODO: We could even turn this into a shader that\n    // takes no inputs, and directly computes the XY\n    // location of each vertex based on `SV_VertexID`\n\n    return float4(position, 0.5, 1.0);\n}\n//\n// The body of the effect will run in the fragment shader,\n// so that is where things get more interesting.\n//\n// We will define our fragment shader entry point as a\n// Slang *generic function*, with a generic type parameter\n// `T` that is constrained to implement the `IShaderToyImageShader`\n// interface.\n//\n[shader(\"fragment\")]\nfloat4 fragmentMain<T : IShaderToyImageShader>(\n    float4 sv_position : SV_Position)\n    : SV_Target\n{\n    // Because the Slang compiler knows the interface that `T`\n    // is expected to implement, any code in the function\n    // body will be checked to make sure that it does not\n    // use operations that `T` would not be guaranteed to\n    // support. Unlike with traditional C++ templates or\n    // preprocessor-based shader specialization, it is possible\n    // to copmile and type-check this entry point once,\n    // and use it with multiple different types for `T`.\n\n    // We start by creating an instance of the effect\n    // type `T`, initialized to whatever its default\n    // values are.\n    //\n    // Note: initializing the effect here preserves the\n    // semantics of the original shader toy code. An\n    // alternative approach (that would change the behavior\n    // from the original) would be to pass a value of\n    // type `T` in as a shader parameter of the entry point.\n    //\n    T toy = T.getDefault();\n\n    // Next, we invoke the user-defined effect by calling\n    // its `mainImage` function.\n    //\n    // Recall that the `fragColor` parameter to `mainImage`\n    // was defined as an `out` parameter, so this call\n    // will set a value into our local `fragColor` variable.\n    //\n    float2 fragCoord = sv_position.xy;\n    float4 fragColor = 0;\n    toy.mainImage(fragColor, fragCoord);\n\n    // The output value from our shader is simply the result\n    // from the user-defined effect.\n    //\n    return float4(fragColor.xyz, 1);\n}\n\n// By defining an interface for image shader effects, we\n// have been able to decouple the code for the effects\n// themselves from the code for their execution contexts.\n// A key benefit of that decoupling is that we can introduce\n// both new effects and new execution contexts in a modular\n// fashion.\n//\n// For example, we can easily define a compute shader for\n// executing an image shader effect:\n//\n[shader(\"compute\")]\nvoid computeMain<T : IShaderToyImageShader>(\n    uint3 sv_dispatchThreadID : SV_DispatchThreadID,\n    uniform RWTexture2D<float4> image)\n{\n    // The operations required to set up and execute\n    // the user-defined effect are similar to what\n    // they were for the fragment shader.\n    //\n    T toy = T.getDefault();\n\n    float2 fragCoord = float2(sv_dispatchThreadID.xy);\n    float4 fragColor = 0;\n    toy.mainImage(fragColor, fragCoord);\n\n    // The main difference is that we now write the\n    // output color explicitly to an image pixel\n    // instead of relying on the rasterization pipeline.\n    //\n    image[sv_dispatchThreadID.xy] = fragColor;\n}\n\n// At this point we've described how our module will\n// execute shader toy effects that implement the\n// required interface, but we also need to set up\n// the services that those effects are able to use.\n//\n// The shader toy \"How To\" file describes a large number of uniform\n// shader parameters that are implicitly visible to every effect.\n//\n// If we were able to design an interface from scratch, we might\n// prefer to make the `mainImage` function take some kind of\n// explicit context parameter that provides access to these\n// values, but because our goal is to be compatible with existing\n// effects with their established `mainImage` signature, we will\n// instead define these parameters using old-fashioned global-scope\n// shader parameters.\n//\ncbuffer ShaderToyUniforms\n{\n    // Note: We do not currently define all of the parameters\n    // exposed by Shader Toy, but rather just the most commonly\n    // used ones.\n    //\n    // TODO: We can and should fill in the rest over time.\n    //\n    float4 iMouse;\n    float2 iResolution;\n    float iTime;\n};\n\n// In addition to the above parameters that use ordinary data types,\n// shader toy also exposes the `iChannel*` parameters (`iChannel0`\n// through `iChannel4`). These parameters represent sampled image\n// inputs that can be bound to selected images as part of an effect.\n//\n// Traditional GLSL \"sampler\" types include both the texture image\n// and sampler state, while Slang (like D3D, Vulkan, etc.) has\n// distinct texture and sampler types. In order to define the\n// channel variables in a way that is compatible with shader toy,\n// we will define a `struct` type for a pair of a texture and\n// a sampler:\n//\nstruct TextureSamplerPair\n{\n    Texture2D t;\n    SamplerState s;\n};\n\n// With our texture-sampler pair type defined, we can introduce\n// the variables for the texture channels easily.\n//\nTextureSamplerPair iChannel0;\nTextureSamplerPair iChannel1;\nTextureSamplerPair iChannel2;\nTextureSamplerPair iChannel3;\n\n\n// TODO: Shader toy supports more than just 2D textures, so a good\n// avenue for extension of the module would be to define an interface\n// for the texture channels, and have implementations using various\n// forms of textures.\n//\n// A really ambitious idea would be include one example of the\n// texture-channel interface that uses an existing ShaderToy as a\n// procedural texture.\n\n// Shader toy effects access the contents of the `iChannel*` variables\n// using the `texture()` function, so we need to provide a definition\n// that is suitable:\n//\nfloat4 texture(TextureSamplerPair p, float3 uvw)\n{\n    // TODO: The right implementation to use here (at least\n    // in the context of fragment shaders) is:\n    //\n    //      return p.t.Sample(p.s, uvw.xy);\n    //\n    // However, the current implementation of the main\n    // application code doesn't include texture image\n    // loading, so we will instead just fill in a\n    // placeholder result for \"texture\" lookup:\n    //\n    return 0.5;\n}\n\n// The last major issue we need to address in this module is the way that\n// shader toy effects are authored in GLSL, which has several differences\n// from Slang that could cause problems.\n//\n// Some of these differences can be surmounted relatively easily. For\n// example, GLSL uses different names for its built-in vector types, but\n// for the most part they are compatible with those defined by HLSL/Slang.\n// We can paper over this difference by defining a few helpful type\n// aliases.\n//\ntypealias vec2 = float2;\ntypealias vec3 = float3;\ntypealias vec4 = float4;\n\n// Matrix types in GLSL are a more subtle issue, because they have different\n// semantics from their HLSL/Slang equivalents in a few key ways:\n//\n// * The infix `*` operator always performs component-wise multiplication\n//   in HLSL/Slang, but in GLSL it sometimes performs linear-algebraic\n//   products (whenever we have matrix*matrix, vector*matrix, or matrix*vector).\n//   HLSL/Slang require a distinct `mul()` function for those cases.\n//\n// * Because of differences in terminology and conventions, a linear-algebraic\n//   product like `M*v` in GLSL is equivalent to `mul(v, M)` in HLSL/Slang\n//   (note the reversed order of operands).\n//\n// * Constructing a matrix or vector from a single scalar consistently\n//   replicates that scalar across all components/elements in HLSL/Slang,\n//   but in GLSL it instead produces a diagonal matrix.\n//\n// These differences are not something we can surmount by defining the\n// GLSL matrix types as aliases of the standard Slang ones, so instead we\n// must define the GLSL matrix types as wrappers around the Slang ones.\n//\nstruct mat2\n{\n    float2x2 data;\n\n    __init(float e00, float e01, float e10, float e11)\n    {\n        data = float2x2(e00, e01, e10, e11);\n    }\n\n    // TODO: We need to fill in the other intializers and members\n    // available on matrices here.\n};\n\n// TODO: fill in `mat3` and `mat4`.\n\n// TODO: Ideally we would want to define overloaded operation functions\n// to allow `*`, `*=`, etc. to apply to our user-space matrix types.\n//\n// Unfortunately, implementation bugs in the Slang compiler mean that\n// user-defined operator overloads aren't working right now.\n//\n//      vec2 operator*(vec2 left, mat2 right)\n//      {\n//          return mul(right.data, left);\n//      }\n//\n//      void operator*=(inout vec2 left, mat2 right)\n//      {\n//          left = mul(right.data, left);\n//      }\n//\n// Instead, we will define an ordinary function for the one case that\n// we 've run into in a test effect so far:\n//\nvoid mulAssign(inout vec2 left, mat2 right)\n{\n    left = mul(right.data, left);\n}\n\nfloat fract(float value)\n{\n    return frac(value);\n}\n\nfloat mix(float a, float b, float t)\n{\n    return lerp(a, b, t);\n}\n",      uri = "file:///home/stephan/projects/slang/examples/shader-toy/shader-toy.slang",      version = 0    }  }}
[DEBUG][2023-11-11 16:03:57] .../lua/vim/lsp.lua:1506	"LSP[slangd]"	"client.request"	1	"textDocument/inlayHint"	{  range = {    ["end"] = {      character = 0,      line = 348    },    start = {      character = 0,      line = 0    }  },  textDocument = {    uri = "file:///home/stephan/projects/slang/examples/shader-toy/shader-toy.slang"  }}	<function 1>	1
[DEBUG][2023-11-11 16:03:57] .../vim/lsp/rpc.lua:261	"rpc.send"	{  id = 2,  jsonrpc = "2.0",  method = "textDocument/inlayHint",  params = {    range = {      ["end"] = {        character = 0,        line = 348      },      start = {        character = 0,        line = 0      }    },    textDocument = {      uri = "file:///home/stephan/projects/slang/examples/shader-toy/shader-toy.slang"    }  }}
[DEBUG][2023-11-11 16:03:57] .../vim/lsp/rpc.lua:364	"rpc.receive"	{  id = 999,  jsonrpc = "2.0",  method = "client/registerCapability",  params = {    registrations = { {        id = "workspace/didChangeConfiguration",        method = "workspace/didChangeConfiguration"      } }  }}
[DEBUG][2023-11-11 16:03:57] .../vim/lsp/rpc.lua:364	"rpc.receive"	{  id = 4627,  jsonrpc = "2.0",  method = "workspace/configuration",  params = {    items = { {        section = "slang.predefinedMacros"      }, {        section = "slang.additionalSearchPaths"      }, {        section = "slang.searchInAllWorkspaceDirectories"      }, {        section = "slang.enableCommitCharactersInAutoCompletion"      }, {        section = "slang.format.clangFormatLocation"      }, {        section = "slang.format.clangFormatStyle"      }, {        section = "slang.format.clangFormatFallbackStyle"      }, {        section = "slang.format.allowLineBreakChangesInOnTypeFormatting"      }, {        section = "slang.format.allowLineBreakChangesInRangeFormatting"      }, {        section = "slang.inlayHints.deducedTypes"      }, {        section = "slang.inlayHints.parameterNames"      }, {        section = "slangLanguageServer.trace.server"      } }  }}
[DEBUG][2023-11-11 16:03:57] .../lua/vim/lsp.lua:1506	"LSP[slangd]"	"client.request"	1	"textDocument/inlayHint"	{  range = {    ["end"] = {      character = 0,      line = 348    },    start = {      character = 0,      line = 0    }  },  textDocument = {    uri = "file:///home/stephan/projects/slang/examples/shader-toy/shader-toy.slang"  }}	<function 1>	1
[DEBUG][2023-11-11 16:03:57] .../vim/lsp/rpc.lua:261	"rpc.send"	{  id = 3,  jsonrpc = "2.0",  method = "textDocument/inlayHint",  params = {    range = {      ["end"] = {        character = 0,        line = 348      },      start = {        character = 0,        line = 0      }    },    textDocument = {      uri = "file:///home/stephan/projects/slang/examples/shader-toy/shader-toy.slang"    }  }}
[WARN][2023-11-11 16:03:57] ...lsp/handlers.lua:134	"The language server slangd triggers a registerCapability handler for workspace/didChangeConfiguration despite dynamicRegistration set to false. Report upstream, this warning is harmless"
[DEBUG][2023-11-11 16:03:57] .../vim/lsp/rpc.lua:380	"server_request: callback result"	{  result = vim.NIL,  status = true}
[DEBUG][2023-11-11 16:03:57] .../vim/lsp/rpc.lua:261	"rpc.send"	{  id = 999,  jsonrpc = "2.0",  result = vim.NIL}
[DEBUG][2023-11-11 16:03:57] .../vim/lsp/rpc.lua:380	"server_request: callback result"	{  result = { vim.NIL, vim.NIL, vim.NIL, vim.NIL, vim.NIL, vim.NIL, vim.NIL, vim.NIL, vim.NIL, vim.NIL, vim.NIL, vim.NIL },  status = true}
[DEBUG][2023-11-11 16:03:57] .../vim/lsp/rpc.lua:261	"rpc.send"	{  id = 4627,  jsonrpc = "2.0",  result = { vim.NIL, vim.NIL, vim.NIL, vim.NIL, vim.NIL, vim.NIL, vim.NIL, vim.NIL, vim.NIL, vim.NIL, vim.NIL, vim.NIL }}
[DEBUG][2023-11-11 16:03:57] .../lua/vim/lsp.lua:1506	"LSP[slangd]"	"client.request"	1	"textDocument/semanticTokens/full"	{  textDocument = {    uri = "file:///home/stephan/projects/slang/examples/shader-toy/shader-toy.slang"  }}	<function 1>	1
[DEBUG][2023-11-11 16:03:57] .../vim/lsp/rpc.lua:261	"rpc.send"	{  id = 4,  jsonrpc = "2.0",  method = "textDocument/semanticTokens/full",  params = {    textDocument = {      uri = "file:///home/stephan/projects/slang/examples/shader-toy/shader-toy.slang"    }  }}
[DEBUG][2023-11-11 16:03:57] .../vim/lsp/rpc.lua:364	"rpc.receive"	{  id = 2,  jsonrpc = "2.0",  result = {}}
[DEBUG][2023-11-11 16:03:57] .../vim/lsp/rpc.lua:364	"rpc.receive"	{  id = 0,  jsonrpc = "2.0",  method = "workspace/semanticTokens/refresh"}
[DEBUG][2023-11-11 16:03:57] .../vim/lsp/rpc.lua:364	"rpc.receive"	{  id = 0,  jsonrpc = "2.0",  method = "workspace/inlayHint/refresh"}
[ERROR][2023-11-11 16:03:57] .../vim/lsp/rpc.lua:675	"rpc"	"/home/stephan/projects/slang/bin/linux-x64/debug/slangd"	"stderr"	"terminate called after throwing an instance of 'Slang::InternalError'\n

with

[WARN][2023-11-11 16:03:57] ...lsp/handlers.lua:134	"The language server slangd triggers a registerCapability handler for workspace/didChangeConfiguration despite dynamicRegistration set to false. Report upstream, this warning is harmless"
@csyonghe
Copy link
Collaborator

I think your workaround makes sense by treating null as empty string. Can you open a PR from your branch?

theHamsta added a commit to theHamsta/slang that referenced this issue Nov 16, 2023
This allows using `slangd` with Neovim as Neovim sets some value
`slangd` expects as a String to `null`.

Fixes shader-slang#3322
theHamsta added a commit to theHamsta/slang that referenced this issue Nov 16, 2023
This allows using `slangd` with Neovim as Neovim sets some value
`slangd` expects as a String to `null`.

Fixes shader-slang#3322
@jsmall-zzz
Copy link
Contributor

I think this would probably better to just return UnownedStringSlice() rather than UnownedStringSlice("", 0).
There is a somewhat subtle distinction between UnownedStringSlice("") and UnownedStringSlice() - in that both are in effect empty strings, but it provides a way to differentiate a zero length string and an invalid/null string.

@csyonghe
Copy link
Collaborator

If you are using the release build, that SLANG_ASSERT shouldn't exist to crash slangd. If it does, then it is something up in the callstack that can't handle an empty UnowendStringSlice, which is more worrying.

@theHamsta
Copy link
Contributor Author

If you are using the release build, that SLANG_ASSERT shouldn't exist to crash slangd. If it does, then it is something up in the callstack that can't handle an empty UnowendStringSlice, which is more worrying.

I remember that the release build crashed with new throwing an out of memory error. I could only used debug mode to investigate the issue.

@csyonghe
Copy link
Collaborator

I see. So the real issue is still something different.

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 a pull request may close this issue.

3 participants