Skip to content

Conversation

clason
Copy link
Member

@clason clason commented Dec 10, 2022

Added two utility functions:

  • get_at_pos(bufnr, row, col): returns a table of tokens (to account for possibly multiple servers); defaults to cursor position if called with empty arguments
  • show_at_cursor(): prints the styled token type and modifiers (using above function)
    This is consistent with similar vim.treesitter functions (see feat(treesitter)!: consolidate get_{node,captures} functions #21374).

(Originally done while adding simple implementation of token modifiers, but that was split off to #21390 so it won't get blocked by the API discussion here.)

@clason clason force-pushed the semantic-tokens branch 2 times, most recently from a2bca5a to 5f91c9f Compare December 10, 2022 16:04
@clason
Copy link
Member Author

clason commented Dec 10, 2022

Changed modifiers to have a higher priority than types. (This will prevent us from applying types and tokens as a single extmark later, although we can still combine all modifiers into a single extmark.)

Feedback on whether the priority is needed/wanted @lewis6991 @mfussenegger @bfredl

@jdrouhard
Copy link
Contributor

Changed modifiers to have a higher priority than types. (This will prevent us from applying types and tokens as a single extmark later, although we can still combine all modifiers into a single extmark.)

Feedback on whether the priority is needed/wanted @lewis6991 @mfussenegger @bfredl

It might make sense to only expose one highlight priority for now so we have the option of having a more complicated resolve algorithm down the line when we add the more expressive modifier highlight support.

If we keep them as higher priority we shoehorn ourselves into only being able to add highlight attributes to base token type groups without a breaking change then.

Makes sense to expose as little as possible now.

@clason
Copy link
Member Author

clason commented Dec 11, 2022

OK, kept everything at the same priority for now. Let's wait until someone reports an actual problem with this before deciding what to do.

@clason clason force-pushed the semantic-tokens branch 2 times, most recently from 5729f7a to dd37376 Compare December 11, 2022 12:32
@clason clason force-pushed the semantic-tokens branch 8 times, most recently from d1aa95c to 1ffdd33 Compare December 11, 2022 14:24
@clason clason requested a review from mfussenegger December 11, 2022 14:33
@jdrouhard
Copy link
Contributor

Can't approve PRs but this LGTM.

---@param col number|nil Position column (default cursor position)
---
---@return table[]|nil tokens Table of tokens at position
function M.get_tokens_at_pos(bufnr, row, col)
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

What do you think of calling this get and move the args into an opts table, treating each option like an additive filter? Similar to diagnostic.get: get(bufnr, { lnum = ..., col = ... }) (not sure about row vs. lnum, there's afaik already both used)

The tokens is a bit redundant as it's already in the semantics_token module.
show_tokens_at_cursor could then be changed to either show(tokens) or print(tokens). I'd tend to print to mirror vim.pretty_print. Show has some "special display" behavior in other places (.e.g diagnostic.show doesn't print diagnostics)

One would need to compose them to print: M.print(M.get())

Copy link
Member Author

@clason clason Dec 11, 2022

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hmm, not a fan, I have to admit, but will think about it.

Note that show uses nvim_echo to style the tokens and not just prints them. It's also meant to be used as an interactive (cursor hold) tool, so I want it to be directly callable. That's the big selling point here.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

But I agree that we're in a bit of a bind here, API wise, between diagnostics and treesitter. Personally, I think that treesitter is the closer relative even though as an LSP module there's a relationship to diagnostics.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Setting aside the show vs. print question, .get() seems like a vast improvement over get_tokens_at_pos() and I strongly prefer that. As @mfussenegger said having tokens in the function name is repetitive, as is _at_pos() since we can just use a table argument for the position.

Pre vim.diagnostic the diagnostic functions were something like vim.lsp.diagnostic.get_line_diagnostics() which has many of the same issues that we're discussing here. I think we should stick with the get() pattern which is versatile enough to cover many different use cases.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

But should the behavior be changed for that? Or still just return the one token (per server) for a single position?

Because I think there's a fundamental difference here between diagnostics (where what is more fundamental than where) and extmarks (where it's the opposite).

Copy link
Member Author

@clason clason Dec 11, 2022

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

To be clear, the difference to me here is that it might make sense to get all diagnostics, but it doesn't make sense to get all tokens/captures/extmarks.

(As diagnostics are the exception, but highlighting is the rule.)

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Absolutely no objection to shortening these to get_at_pos and show_at_cursor, though.

useful for debugging; marked private for now
Apply semantic token modifiers as separate extmarks with corresponding
highlight groups (e.g., `@readonly`). This is a low-effort PR to enable
the most common use cases (applying, e.g., italics or backgrounds on top
of type highlights; language-specific fallbacks like `@global.lua` are
also available). This can be replaced by more complicated selector-style
themes later on.
Copy link
Member

@mfussenegger mfussenegger left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Left another question/suggestion, but also fine as is.

end

--- Show the semantic token(s) under the cursor
function M.show_at_cursor()
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Would it be worth to keep the tokens argument and have M.get_at_pos() as fallback? Would make the function more generally usable. (But I guess the name wouldn't fit that well anymore)

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I don't quite understand what the signature and the behavior would be? Can you give an example use case (useful for me to see how to best refactor this)?

@clason clason changed the title feat(lsp): simple semantic token modifiers feat(lsp): utility functions for getting and showing tokens Dec 12, 2022
Comment on lines +633 to +638
--- Show the semantic token(s) under the cursor
function M.show_at_cursor()
local tokens = M.get_at_pos()
if not tokens then
return
end
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
--- Show the semantic token(s) under the cursor
function M.show_at_cursor()
local tokens = M.get_at_pos()
if not tokens then
return
end
--- Show the semantic token(s) under the cursor
---@param tokens table[]|nil tokens to print, defaults to tokens at cursor
function M.show(tokens)
tokens = tokens or M.get_at_pos()
if not tokens then
return
end

This is what I had in mind - it would allow to combine it with M.get_at_pos to print the tokens from another position.
But I don't really have a concrete use-case for it.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Agreed, these "at cursor" functions keep popping up but they really shouldn't. It's easy for callers to provide the cursor position, and taking a position arg is more flexible.

end
msg[#msg + 1] = { ' ' }
end
api.nvim_echo(msg, false, {})
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

what is the difference between "show", "echo", "print", and for some reason "vim.notify"?

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

vim.notify is an interface which plugins can use to implement fancy ui's for notifications. See https://github.com/rcarriga/nvim-notify.

Not sure myself on the difference between show, echo and print.

@clason clason closed this Dec 13, 2022
@jdrouhard
Copy link
Contributor

@clason - wouldn't at least having the function to return a table of highlight info at a specific position for a specific buffer still be useful? That could be used by #21393 for more detailed information, I would imagine.

@folke
Copy link
Member

folke commented Dec 13, 2022

Yes for sure. I was hoping to have the get_at_pos function to get the stuff I need for the general vim.get_at_pos

@clason
Copy link
Member Author

clason commented Dec 13, 2022

@folke feel free to cherry-pick 2e328ba to your PR and make any changes deemed necessary to fit the API guidelines.

@folke
Copy link
Member

folke commented Dec 13, 2022

@clason thanks. Added the commit

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Projects
None yet
Development

Successfully merging this pull request may close these issues.

8 participants