racket-langserver icon indicating copy to clipboard operation
racket-langserver copied to clipboard

Function Parameter/Field Name Virtual Text Inference

Open jestarray opened this issue 4 years ago • 10 comments
trafficstars

rust-analyzer provides a very neat feature in that it will print out virtual text of struct field names and function param names when you call said function.

This feature would be a huge productivity gain for racket as structs will be much easier to work with, along with functions. Currently I create a lot of keyword functions for structs that are very large because I don't want to keep guessing what they are by position. (I have looked into the rebellion packages and etc but ehh, I prefer to minimize dependencies)

image

pub struct Rect {
    pub x: u32,
    pub y: u32,
    pub w: u32,
    pub h: u32,
}
impl Rect {
    pub fn new(x: u32, y: u32, w: u32, h: u32) -> Self {
        Self { x, y, w, h }
    }
}
//so we can have a more ergonomic syntax for creating structs by position
let r = Rect::new(0, 5, 32, 32);

jestarray avatar Feb 22 '21 18:02 jestarray

Hmm. What would Rust suggest for f in the following code (when written in Rust, of course)?

(define (test f)
  (f 1 2 3))

(define (g a b c)
  (+ a b c))

(define (h x y z)
  (- x y z))

(test g)
(test h)

sorawee avatar Feb 22 '21 19:02 sorawee

image

fn test(f: fn(i32, i32, i32) -> i32, _dummy: i32) -> i32 {
    f(1, 2, 3)
}
fn g(a: i32, b: i32, c: i32) -> i32 {
    a + b + c
}
fn h(x: i32, y: i32, z: i32) -> i32 {
    x - y - z
}

fn main() {
    test(g, 1);
    test(h, 0);

jestarray avatar Feb 22 '21 21:02 jestarray

Is this something that can be handled entirely by the language server? Does the protocol have a standard way for servers to tell editors to show that kind of overlay?

jackfirth avatar Apr 22 '21 02:04 jackfirth

@jackfirth I would think that its something entirely handled by the language server yes.

As for a standard way:

According to https://rust-analyzer.github.io/manual.html#inlay-hints , the source file says: VS Code does not have native support for inlay hints https://github.com/microsoft/vscode/issues/16221[yet] and the hints are implemented using decorations.

https://github.com/microsoft/vscode/issues/16221#issuecomment-363087092

I want to look into implementing this at some point but I don't know much about language server among other things.... If someone does get this working, I will love you forever :heart: !

jestarray avatar Apr 22 '21 05:04 jestarray

Oh wow, that's really cool. This definitely seems worth looking into then.

jackfirth avatar Apr 23 '21 03:04 jackfirth

https://code.visualstudio.com/updates/v1_64#_inlay-hint-api https://code.visualstudio.com/updates/v1_66#_language-server-protocol

the feature is stable now in vscode!

jestarray avatar Apr 18 '22 16:04 jestarray

NOTE: LSP specification

  • https://microsoft.github.io/language-server-protocol/specifications/lsp/3.17/specification/#textDocument_inlayHint

dannypsnl avatar Oct 23 '22 17:10 dannypsnl

@jestarray the method part is merged, the rest is how to collect struct fields information and put them into “inlayHint” method.

dannypsnl avatar Nov 13 '22 13:11 dannypsnl

@dannypsnl awesome work! Read your PR, so the main blocker is official support from racket syncheck-annotations<%>? Should an issue be filed in drracket for it?

jestarray avatar Nov 13 '22 16:11 jestarray

syncheck-annotations<%> supporting will make things easier, but not totally a block, it's still possible to read field information back. The following steps should work

Steps

  1. collects definition from syncheck-annotations<%>(very easy)
  2. use racket:text%'s methods like sexp-backward to ensure if it's a structure or a procedure definition
  3. then read the s-expr (struct s-name ps-name? (fields ...)) and parsing
  4. now stores the meta info into a new hash into the collector for file
  5. when finding an application via syncheck-annotations<%>(very easy), lookup the meta info for procedure location and build a hint

Challenge

Though the above will work, the biggest problem is performance. If you check text-document.rkt and go to definition procedure part, you can find that I use text% rather than racket:text%, use racket:text% will bring significantly slow down the reaction somehow. However, the racket:text% used for did-open! cannot be changed, it has real usage.

The core problem of the architecture is, it should prepare a collector for each file in the project open(as IDE) to provide ready information for usage like inlay hints, but we have did-open!, did-close! only for now.

A redesigned picture would be

  1. did-open! only opens racket:text% that collects certain usage like formatting, etc
  2. prefetching data when workspace ready(related to #74)
    • refers to https://github.com/racket-tw/sauron for the concrete implementation, below are my related blog posts
      • https://dannypsnl.me/blog/2022-07-25-sauron-dev-log/
      • https://dannypsnl.me/blog/2022-09-03-racket-future/
    • parallelly build collectors for files
    • use prepared data for jump to definition and find references such features

However, threading will not help, other solutions also have their own problem

  1. racket/place is too big(basically is a system thread size on a UNIX-like system), in this case, per file per thread is a bad idea
  2. racket/future would be thin enough but lack control and computation properties

Based on the second solution, I'm thinking of a draft idea, hope these will help

dannypsnl avatar Nov 13 '22 20:11 dannypsnl