lexical-lsp / lexical

Lexical is a next-generation elixir language server
781 stars 77 forks source link

Workspace symbols #674

Closed scohen closed 2 months ago

scohen commented 3 months ago

Workspace symbols support. Workspace symbols finds all things that match a query in a project.

scohen commented 3 months ago

It's worth noting that this isn't a feature I ever use, so I might be way off the mark here. Here are the current limitations:

I'm not wedded to these limitations, and I'd like to hear from someone who uses these features.

dimitarvp commented 3 months ago

Opening a file / workspace is fine, but the moment I click with the mouse anywhere in the viewport (using LunarVim i.e. Neovim + premade config) I get the following:

Screenshot 2024-04-02 at 10 25 16

Clicking left and right yields the same message, only the ID is increasing. No idea how to troubleshoot further.

Though it also has to be said that my Elixir files are now opening both with elixirls and lexical and I couldn't force my editor to only use lexical yet.

dimitarvp commented 3 months ago

OK, some more screwing around reveals that this is kinda related to opening Neovim in a vertical split mode, I guess?

So I start my editor this way: lvim -O2 lib/file1.ex lib/file2.ex

I start off in file1.ex, I click on file2.ex, then I click back on file1.ex's viewport and the error shows up.

Same happens with -o2.

The error does NOT manifest itself if the editor is opened with a single pane.

Finally, trying to invoke listing of file symbols shows this big error:

LSP[lexical]: Error NO_RESULT_CALLBACK_FOUND: {
  id = 7,
  jsonrpc = "2.0",
  result = { {
      children = { {
          children = {},
          kind = 12,
          name = "receive_srr(%Conn{} = conn, %{oem: _} = params)",
          range = {
            ["end"] = {
              character = 5,
              line = 39
            },
            start = {
              character = 2,
              line = 31
            }
          },
          selectionRange = {
            ["end"] = {
              character = 53,
              line = 31
            },
            start = {
              character = 6,
              line = 31
            }
          }
        }, {
          children = {},
          kind = 12,
          name = "receive_mo(%Conn{} = conn, %{oem: _} = params)",
          range = {
            ["end"] = {
              character = 5,
              line = 62
            },
            start = {
              character = 2,
              line = 54
            }
          },
          selectionRange = {
            ["end"] = {
              character = 52,
              line = 54
            },
            start = {
              character = 6,
              line = 54
            }
          }
        } },
      kind = 2,
      name = "OssM2m.V1.ShortMessages.SmsController",
      range = {
        ["end"] = {
          character = 3,
          line = 63
        },
        start = {
          character = 0,
          line = 0
        }
      },
      selectionRange = {
        ["end"] = {
          character = 47,
          line = 0
        },
        start = {
          character = 10,
          line = 0
        }
      }
    } }
}

Hope any of that helps.

dimitarvp commented 3 months ago

Oh btw, this also happens not just when clicking the buffers with the mouse. Doing C-h and C-l causes the error as well.

scohen commented 3 months ago

This looks to be a problem with the plugin, as evidenced by the fact that it seems to only manifest itself when something editor-specific is happening. The LSP has no idea what's going on with the editor. The response you sent looks like a valid document symbols response. I'm not a vim user, so maybe @scottming has ideas.

The ["end"] in selection range is suspicious though.

dimitarvp commented 3 months ago

Sorry, which plugin?

I can make a super mini Elixir project and see if we can have that reproduced on your machine (if you use NeoVim at all, that is).

scohen commented 3 months ago

whatever you're using to enable language servers with vim. Maybe "LSP client" is better terminology. I did verify that the responses are correct responses; the "end" isn't in a list in the actual JSON.

dimitarvp commented 3 months ago

Well I suppose it's NeoVim's builtin LSP software.

Any ideas how can I troubleshoot this further? I am busy enough and not being versed in the implementation details of my editor is a conscious choice that I am not looking to change, I am afraid.

Is there any other way I can help?

scohen commented 3 months ago

One of our contributors, @scottming has been helpful with this stuff in the past. What's interesting is that the response you've highlighted is from a document symbols response, not a workspace symbols response. This is probably why clicking around is crashing things, as those requests are fired whenever the cursor moves.

scottming commented 3 months ago

@scohen @dimitarvp I can replicate this error, will take a look today or tomorrow.

dimitarvp commented 3 months ago

@scohen My apologies for not posting the output of listing workspace symbols. My bad. I'll do that and post the output Later™.

scohen commented 2 months ago

but when I want to search for modules, it still first returns functions

That's not how workspace symbols works or is supposed to work. You're searching for all symbols, not a specific type, and if a symbol has some text in it, you're going to get that symbol back. We can control ordering to an extent though different editors sort things differently.

it still first returns functions and the functions from dependencies.

I've noticed this too, i don't know what reverted to cause this.

Additionally, when I enter the attribute @user, it doesn't return anything. Is this intentional?

I get module attributes when i search for their names (user in this case), but not when using the @ prefix. I'll investigate.

scohen commented 2 months ago

@scottming I fixed the dependencies issue, I added nil to the app for testing, which pulled in anything that didn't have an app, which included a lot of dependencies. I removed nil from the application list, so you shouldn't see any deps in your completions any more.

As for the module attributes, I pulled out the leading @ sign when creating the subject to normalize them, so that's why they wouldn't appear as results if the query included a leading @. I'm not sure why i did that, it doesn't affect quality, so i removed that.

scottming commented 2 months ago

That's not how workspace symbols works or is supposed to work. You're searching for all symbols, not a specific type, and if a symbol has some text in it, you're going to get that symbol back. We can control ordering to an extent though different editors sort things differently.

So if I want to quickly search for a module using this feature, how should I do it, especially when I don't quite remember the context of the module(Like only remember the User, not LiveDemoAccounts)?

scohen commented 2 months ago

So if I want to quickly search for a module using this feature, how should I do it, especially when I don't quite remember the context of the module(Like only remember the User, not LiveDemoAccounts

Searching for "User" should find it just fine.

scottming commented 2 months ago

Searching for "User" should find it just fine.

But as I commented before, there are some issues with the sorting when searching like this. I actually don't quite understand why the user_fixture function in the fixtures file would be ranked ahead of the User module.

CleanShot 2024-04-15 at 21 40 02@2x

scottming commented 2 months ago

BTW, aside from filtering out dependencies, it seems unnecessary for constants like @impl ... to appear in the results

CleanShot 2024-04-15 at 21 46 08@2x

scohen commented 2 months ago

why the user_fixture function in the fixtures file would be ranked ahead of the User module.

Because your editor is ranking them that way, we merely provide symbols. It's also doing something annoying by implying that the path has something to do with the returned results.

That said, we do rank them higher because the term user comes earlier in the symbol.

I want to be very clear here, we are providing a list of workspace symbols that match a query. Your editor then takes this list and orders it the way it sees fit. You're disagreeing with that ranking; we don't have much control over it.

scohen commented 2 months ago

BTW, aside from filtering out dependencies, it seems unnecessary for constants like @impl ... to appear in the results

That's your opinion. It's quite possible that someone wants to see where they use @impl or search for @impl GenServer.

scottming commented 2 months ago

I want to be very clear here, we are providing a list of workspace symbols that match a query. Your editor then takes this list and orders it the way it sees fit. You're disagreeing with that ranking; we don't have much control over it.

Actually, I double-checked and found that VSCode does indeed reorder, while nvim does not; it simply displays the data returned by lexical. When I search for Product, this is the specific response received by the client.

Response ```lua [DEBUG][2024-04-17 10:15:48] .../vim/lsp/rpc.lua:387 "rpc.receive" { id = 32, jsonrpc = "2.0", result = { { kind = 12, location = { range = { ["end"] = { character = 35, line = 9 }, start = { character = 6, line = 9 } }, uri = "file:///Users/scottming/Code/live_demo/test/support/fixtures/catalog_fixtures.ex" }, name = "LiveDemo.CatalogFixtures.product_fixture/1" }, { kind = 12, location = { range = { ["end"] = { character = 22, line = 37 }, start = { character = 6, line = 37 } }, uri = "file:///Users/scottming/Code/live_demo/lib/live_demo/catalog.ex" }, name = "LiveDemo.Catalog.get_product!/1" }, { kind = 12, location = { range = { ["end"] = { character = 49, line = 72 }, start = { character = 7, line = 72 } }, uri = "file:///Users/scottming/Code/live_demo/lib/live_demo_web/live/product_live/form_component.ex" }, name = "LiveDemoWeb.ProductLive.FormComponent.save_product/3" }, { kind = 12, location = { range = { ["end"] = { character = 50, line = 57 }, start = { character = 7, line = 57 } }, uri = "file:///Users/scottming/Code/live_demo/lib/live_demo_web/live/product_live/form_component.ex" }, name = "LiveDemoWeb.ProductLive.FormComponent.save_product/3" }, { kind = 12, location = { range = { ["end"] = { character = 19, line = 19 }, start = { character = 6, line = 19 } }, uri = "file:///Users/scottming/Code/live_demo/lib/live_demo/catalog.ex" }, name = "LiveDemo.Catalog.list_products/0" }, { kind = 12, location = { range = { ["end"] = { character = 49, line = 69 }, start = { character = 6, line = 69 } }, uri = "file:///Users/scottming/Code/live_demo/lib/live_demo/catalog.ex" }, name = "LiveDemo.Catalog.update_product/2" }, { kind = 12, location = { range = { ["end"] = { character = 34, line = 51 }, start = { character = 6, line = 51 } }, uri = "file:///Users/scottming/Code/live_demo/lib/live_demo/catalog.ex" }, name = "LiveDemo.Catalog.create_product/1" }, { kind = 12, location = { range = { ["end"] = { character = 42, line = 87 }, start = { character = 6, line = 87 } }, uri = "file:///Users/scottming/Code/live_demo/lib/live_demo/catalog.ex" }, name = "LiveDemo.Catalog.delete_product/1" }, { kind = 12, location = { range = { ["end"] = { character = 56, line = 100 }, start = { character = 6, line = 100 } }, uri = "file:///Users/scottming/Code/live_demo/lib/live_demo/catalog.ex" }, name = "LiveDemo.Catalog.change_product/2" }, { kind = 2, location = { range = { ["end"] = { character = 38, line = 0 }, start = { character = 10, line = 0 } }, uri = "file:///Users/scottming/Code/live_demo/lib/live_demo_web/live/product_live/show.ex" }, name = "LiveDemoWeb.ProductLive.Show" }, { kind = 2, location = { range = { ["end"] = { character = 39, line = 0 }, start = { character = 10, line = 0 } }, uri = "file:///Users/scottming/Code/live_demo/lib/live_demo_web/live/product_live/index.ex" }, name = "LiveDemoWeb.ProductLive.Index" }, { kind = 2, location = { range = { ["end"] = { character = 47, line = 0 }, start = { character = 10, line = 0 } }, uri = "file:///Users/scottming/Code/live_demo/lib/live_demo_web/live/product_live/form_component.ex" }, name = "LiveDemoWeb.ProductLive.FormComponent" }, { kind = 2, location = { range = { ["end"] = { character = 34, line = 0 }, start = { character = 10, line = 0 } }, uri = "file:///Users/scottming/Code/live_demo/lib/live_demo/catalog/product.ex" }, name = "LiveDemo.Catalog.Product" } } } ```

Below is the display effect.

effect image

You can use these command to easily create a Phoenix project to replicate these issues.

mix phx.new live_demo
mix phx.gen.live Catalog Product products title:string description:string price:decimal views:integer
scohen commented 2 months ago

I explained why the ordering is the way it is. I feel like we can go back and forth on this, but ordering is more a property of the fuzzy matcher while this pr is about enabling workspace symbols. emacs also reorders for what it’s worth.