chances / lsp-glib

Language Server Protocol SDK for GLib
MIT License
3 stars 0 forks source link

Use generic GLib.Object to GLib.Variant #9

Open esodan opened 3 years ago

esodan commented 3 years ago

Abstract the generic generic GLib.Object to GLib.Variant interface into its own library in the GVls repo.


Original issue written by @esodan:

GVls has a generic GLib.Object to GLib.Variant, is a simple interface. You don't need to implement any method just write your code to implement GVls.VariantObject and mark properties to use; look this example:

public class GVls.HoverInfo : GLib.Object, VariantObject, Hover
{
  [Description(nick="contents")]
  [Version(since="0.16")]
  public GLib.Variant contents { get; set; }

  [Description(nick="range")]
  public Range range { get; set; }
}

nick are use to mark the name of the property you'll see when you convert this object to GLib.Variant and when used this Variant on glib-json you can get a JSON representation of the object.

GVls has lots of basic structures, generic lists, strings, boolean, integers.

A plan is to move GVls.VariantObject to its own library to any one to use outside not wanting to depend on GVls.

GVls support Unit Tests, currently +100 are present and in all of them GVls.VariantObject has been implicit tested.

chances commented 3 years ago

@esodan Do you have a roadmap or timeline to abstract VariantObject into its own library? I'd rather not reinvent your wheel. 😉

esodan commented 3 years ago

@esodan Do you have a roadmap or timeline to abstract VariantObject into its own library? I'd rather not reinvent your wheel.

I need help on. If you want I can guide you to do it your self inside GVls repository as a new independent library.

I'd rather not reinvent your wheel.

May you want to work and improve GVlsp.ClientJsonrpc is an implementation of GVls .Client, it is the LSP client you pretend to write up, with tens of unit tests you can take advantage of, including you can add tests with other servers if you want (I can guide you on).

The big advantage of GVls is that it is a library, so can be reused any where, including any language like Python or JavaScript, thanks to GObject Introspection.

So just don't reinvent the wheel, join GVls and lets make any other IDE have LSP clients.

chances commented 3 years ago

By "wheel" I was referring to the subject of this issue, i.e. your extant GLib.Variant implementation.

The scope of this project is a smaller subset of gvls and vala-language-server. I think the community can benefit from a generic protocol SDK, not only for the existing Vala language servers but any other language's LSP server implementation written in Vala. As proof, see the long list of SDKs for the LSP.

chances commented 3 years ago

@esodan Do you have a roadmap or timeline to abstract VariantObject into its own library? I'd rather not reinvent your wheel.

I need help on. ...

[snip]

I'll look into this... 👀

esodan commented 3 years ago

By "wheel" I was referring to the subject of this issue, i.e. your extant GLib.Variant implementation.

The scope of this project is a smaller subset of gvls and vala-language-server. I think the community can benefit from a generic protocol SDK, not only for the existing Vala language servers but any other language's LSP server implementation written in Vala. As proof, see the long list of SDKs for the LSP.

I'll add GVls in that list, because:

esodan commented 3 years ago

GVls now provides a new independent library libvo, isolating GObject to Variant/JSON serialization framework, so now you can use it without GVls, just use -Dlibgvls=false configuration variable. Use gvo-1 package dependency.

I think I can move out Vala specific implementations out of GVls classes and interfaces, so anyone can use the generic classes to implement their own LSP server or client, as this classes are in line with LSP specification.

esodan commented 3 years ago

In latest master I have split Vala Language specifics out to GVlsp library from GVls. Now just interfaces and classes are present for Generic implementations of LSP In any language; is possible use use C, Vala, Python, JavaScript or any other language supported by GObject Introspection, to write clients or servers.

By integrating GVls in an IED, you empower it to allow plugin developers to write LSP clients and/or servers for any language you want to support.

The use of interfaces makes easy to implement your own, while the IDE can use them to integrate clients into its infrastructure.

Prince781 commented 3 years ago

Alternatively, one could simply have to_variant() / from_variant() serailzation functions for each type. This would make the API much simpler to use from C and other languages.

I've started work on a simple-to-use LSP library at https://github.com/Prince781/lsp-glib

esodan commented 3 years ago

Alternatively, one could simply have to_variant() / from_variant() serailzation functions for each type. This would make the API much simpler to use from C and other languages.

I've started work on a simple-to-use LSP library at https://github.com/Prince781/lsp-glib

Well may you want to use libvo, it is very simple:

public class MyClass : GLib.Object, GVo.Object
{
    [Description (nick="enableSource"]
    public bool enable_source { get; set; }
}

public class App : GLib.Object {
    public int main () {
        var obj = new MyClass ();
        GLib.Variant v = obj.to_variant ();
        var obj2 = new MyClass ();
        obj2.parse_variant (v);
    }
}

In above code you define a class, set a nick name for a property you want to be able to serialize to/from GLib.Variant, using the name you want to see when it is converted to JSON, then convert an instace objto a GLib.Variant using to_variant() provided by GVo.Object interface, you don't need to do any thing else. Then a new instance obj2 use parse_variant() to read the properties from a GLib.Variant/JSON, method also provided by GVo.Object.

There are a set of utility classes in GVo.Object to create lists of objects or basic objects. Almost all basic types are already supported and automatically detected, so you just need to "mark" which property you want to be converted. If you have classes in your class, make sure you want to set its description and the class is a GVo.Object, so you will have a JSON object with the name you set and all its data; GVo.Object just detect that objects and automatically convert/parse them to/from GLib.Variant/JSON.

Implement LSP Specification is a matter to convert specification interfaces to Vala syntax using above method and now you have all to create objects to GLib.Variant/JSON, without extra effort.

Also GVo.Object has been tested a cross of +90 unit tests, for different scenarios.

By the end GVo.Object and related classes, are part of a independent library libvo, no dependency on Vala or GVls.

@Prince781 I invite you to use help in GVo.Object development or test it, so you may find it already providing most of the common applications for data to JSON.

Prince781 commented 3 years ago

@esodan

Using the GObject property description to control the serialized property name is clever, and I'm impressed by the effort you've made. However it seems that using GObject for everything is rather heavy especially when language servers can produce thousands of diagnostics and tens of thousands of symbols. One of my goals with lsp-glib is to have structures as lightweight as possible. Another problem is to design the API in mind with how it will be used from C and other bound languages.

If you can, please take a look at the design ideas I have and let me know what you think.

esodan commented 3 years ago

@esodan

Using the GObject property description to control the serialized property name is clever, and I'm impressed by the effort you've made. However it seems that using GObject for everything is rather heavy especially when language servers can produce thousands of diagnostics and tens of thousands of symbols. One of my goals with lsp-glib is to have structures as lightweight as possible. Another problem is to design the API in mind with how it will be used from C and other bound languages.

If you can, please take a look at the design ideas I have and let me know what you think.

Making it lightweight as much as possible. That is like Vala does it today. But your code still use GLib.Object so I see you'll remove it. Use GObject increase memory foot print and overhead because object construction, but I don't see that as a problem, because the impact is low in my measures.

GVls.Client already hides all JSONRPC details and throws GLib.Error.

On the other hand, consider that GLib.Object based libraries are easy to bind using GObjectIntrospection, to Python and JavaScript, so your clients and servers can be written on other languages.

May you want to keep GLib.Type because that is a power full on Object Oriented Programming in C this days, as Vala compiler does now too.

GVls uses signals, but that is only for GLib.Object derived classes, so consider if you'll need to remove that on the Client side, because may you want to allows other parts of the program to act when a Diagnostic or a message from the server is received.

Avoid Gee from the public API is good, because that makes easy for C written programs. GVls do that also, providing a GVo.Container, implementing GLib.ListModel interface, with a set of other implementations for hashed lists, ordered list and any other. GVls uses internally Gee, but that just open the door for other powerful implementations, while keeps a C and Bindings friendly API.

May you want provide your feedback about new GVls infrastructure, which I consider very mature on its fundamentals for JSON/GLib.Variant and basic data structures. May GVls already provides what you want to accomplish and we can work together.

Prince781 commented 3 years ago

@esodan I took a look at GVls. I agree somewhat with the way Gvls.Client is set up, but I think the type hierarchy is overly complex. Having an interface for every type is a bit much, and the over-reliance on inheritance will make it a pain to update the library to future revisions of the protocol.

It is not necessary to use GObject for bindings because g-ir-scanner can handle structs and other basic types as well. I try to have getters and setters for every property or if not I specify them in a constructor, to avoid issues with having to wrap struct members in other languages like Python.

I think methods could be more strongly typed. You have a lot of protocol methods returning Gvo.Container in the client code. I also prefer to use GUri instead of string although that's more debatable. This is one area that introduces a bit more complexity, but I feel it's worth it since taking for granted URIs are escaped and proper can lead to subtle issues.

My general takeaway, though, is that things are unnecessarily complex, a thing I try to avoid. It's easy to understand the code we write but we also want it to be easy for others to understand, so that they'll be able to use the library.

esodan commented 3 years ago

@esodan I took a look at GVls. I agree somewhat with the way Gvls.Client is set up, but I think the type hierarchy is overly complex. Having an interface for every type is a bit much, and the over-reliance on inheritance will make it a pain to update the library to future revisions of the protocol.

This is a design decision. Using interfaces, makes easy to implement your own, so that push flexibility while any implementation will use the same API. Also consider that LSP specification provides interfaces, so GVls take them and translate it to Vala syntax and GVo.Object syntax, then an implementation of that interfaces are provided by GVls after new methods are implemented and a Unit Test are used to check them up. Split of GVls in generic from Vala LSP implementation, should help to simplify API.

It is not necessary to use GObject for bindings because g-ir-scanner can handle structs and other basic types as well. I try to have getters and setters for every property or if not I specify them in a constructor, to avoid issues with having to wrap struct members in other languages like Python.

Right. Just consider what you loose if no GLib.Object is in use.

I think methods could be more strongly typed. You have a lot of protocol methods returning Gvo.Container in the client code. I also prefer to use GUri instead of string although that's more debatable. This is one area that introduces a bit more complexity, but I feel it's worth it since taking for granted URIs are escaped and proper can lead to subtle issues.

GVo.Container is used because some methods return a list of objects from a JSONRPC. The list is a GLib.ListModel so are easy to use and you'll get a GLib.Object class parsed form GLib.Variant, you don't need to worry about parsing. Use GUri may is a good idea, but just need to know if that is a problem when the URI if for files and you can check if that file exists in your file system; just need to check.

My general takeaway, though, is that things are unnecessarily complex, a thing I try to avoid. It's easy to understand the code we write but we also want it to be easy for others to understand, so that they'll be able to use the library.

Large projects are complex. Gtk+ is complex and full of interfaces. GVls provides flexibility and describes what is missing, no implemented. Also GVls is a library easy to test so that help to test any bit of it before a release and to reproduce and fix future bugs. The design has make easy to split Vala LSP implementation and provide a generic API for any other language.

Also GVls is hiding complexity using classes like GVlsui.ProejctManager and GVlsp.ProjectManager to make even more easy to use, while keeps it easy to test tiny things.

Feel free to re-use the existing work on GVls and create your API over. Your users will gain a fragmented tested library and an easy to use API you can provide.