From e147cb44bb2475ade38e82abb13f0214a72a724c Mon Sep 17 00:00:00 2001 From: Ashar Date: Thu, 26 Dec 2024 15:50:46 +0530 Subject: [PATCH 1/5] feat: Add a `--version` cli parameter to check for version (#43) Useful for checking the version manually and also some programs like neovim does ` --version` to display in LSP health checks. --- Cargo.lock | 4 ++-- Cargo.toml | 2 +- src/lsp.rs | 7 +++---- src/main.rs | 8 +++++++- src/workspace/hover.rs | 1 - 5 files changed, 13 insertions(+), 9 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 621a483..fa389f0 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1,6 +1,6 @@ # This file is automatically @generated by Cargo. # It is not intended for manual editing. -version = 3 +version = 4 [[package]] name = "addr2line" @@ -512,7 +512,7 @@ dependencies = [ [[package]] name = "protols" -version = "0.8.0" +version = "0.8.5" dependencies = [ "async-lsp", "futures", diff --git a/Cargo.toml b/Cargo.toml index e5c38d4..66cf42b 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,7 +1,7 @@ [package] name = "protols" description = "Language server for proto3 files" -version = "0.8.0" +version = "0.8.5" edition = "2021" license = "MIT" homepage = "https://github.com/coder3101/protols" diff --git a/src/lsp.rs b/src/lsp.rs index 1939524..1f362b0 100644 --- a/src/lsp.rs +++ b/src/lsp.rs @@ -15,10 +15,9 @@ use async_lsp::lsp_types::{ GotoDefinitionResponse, Hover, HoverContents, HoverParams, HoverProviderCapability, InitializeParams, InitializeResult, Location, OneOf, PrepareRenameResponse, ProgressParams, ReferenceParams, RenameFilesParams, RenameOptions, RenameParams, ServerCapabilities, - ServerInfo, TextDocumentPositionParams, TextDocumentSyncCapability, - TextDocumentSyncKind, TextEdit, Url, WorkspaceEdit, - WorkspaceFileOperationsServerCapabilities, WorkspaceFoldersServerCapabilities, - WorkspaceServerCapabilities, + ServerInfo, TextDocumentPositionParams, TextDocumentSyncCapability, TextDocumentSyncKind, + TextEdit, Url, WorkspaceEdit, WorkspaceFileOperationsServerCapabilities, + WorkspaceFoldersServerCapabilities, WorkspaceServerCapabilities, }; use async_lsp::{LanguageClient, LanguageServer, ResponseError}; use futures::future::BoxFuture; diff --git a/src/main.rs b/src/main.rs index b77c643..ae4e89b 100644 --- a/src/main.rs +++ b/src/main.rs @@ -9,6 +9,7 @@ use server::{ProtoLanguageServer, TickEvent}; use tower::ServiceBuilder; use tracing::Level; +mod formatter; mod lsp; mod nodekind; mod parser; @@ -16,10 +17,15 @@ mod server; mod state; mod utils; mod workspace; -mod formatter; #[tokio::main(flavor = "current_thread")] async fn main() { + let args: Vec = std::env::args().collect(); + if args.len() == 2 && args[1] == "--version" { + println!("{} {}", env!("CARGO_PKG_NAME"), env!("CARGO_PKG_VERSION")); + return; + } + let (server, _) = async_lsp::MainLoop::new_server(|client| { tokio::spawn({ let client = client.clone(); diff --git a/src/workspace/hover.rs b/src/workspace/hover.rs index e2c3ded..bc30ac4 100644 --- a/src/workspace/hover.rs +++ b/src/workspace/hover.rs @@ -6,7 +6,6 @@ use crate::{ formatter::ProtoFormatter, state::ProtoLanguageState, utils::split_identifier_package, }; - static BUITIN_DOCS: LazyLock> = LazyLock::new(|| { HashMap::from([ ( From 10d2a1e3b972b269a5f87cb670665bc069a92751 Mon Sep 17 00:00:00 2001 From: Ashar Date: Thu, 26 Dec 2024 20:26:19 +0530 Subject: [PATCH 2/5] feat: Use markdown for hover text and add well known types hover documentations (#44) --- sample/simple.proto | 6 +- src/lsp.rs | 19 +- src/parser/hover.rs | 7 +- src/workspace/hover.rs | 580 ++++++++++++++++-- ...__hover__test__workspace_test_hover-2.snap | 3 +- ...__hover__test__workspace_test_hover-3.snap | 3 +- ...__hover__test__workspace_test_hover-4.snap | 6 +- ...__hover__test__workspace_test_hover-5.snap | 3 +- ...ce__hover__test__workspace_test_hover.snap | 6 +- 9 files changed, 570 insertions(+), 63 deletions(-) diff --git a/sample/simple.proto b/sample/simple.proto index 1692f33..7e157d3 100644 --- a/sample/simple.proto +++ b/sample/simple.proto @@ -13,7 +13,11 @@ message Book { google.protobuf.Any data = 4; BookState state = 5; - // Author is a author of a book + // # Author is a author of a book + // Usage is as follow: + // ```rust + // println!("hello world") + // ``` message Author { string name = 1; int64 age = 2; diff --git a/src/lsp.rs b/src/lsp.rs index 1f362b0..3674d43 100644 --- a/src/lsp.rs +++ b/src/lsp.rs @@ -175,23 +175,16 @@ impl LanguageServer for ProtoLanguageServer { return Box::pin(async move { Ok(None) }); }; - let comments = self + let result = self .state .hover(current_package_name.as_ref(), identifier.as_ref()); - let response = match comments.len() { - 0 => None, - 1 => Some(Hover { - contents: HoverContents::Scalar(comments[0].clone()), - range: None, - }), - 2.. => Some(Hover { - contents: HoverContents::Array(comments), + Box::pin(async move { + Ok(result.map(|r| Hover { range: None, - }), - }; - - Box::pin(async move { Ok(response) }) + contents: HoverContents::Markup(r), + })) + }) } fn completion( &mut self, diff --git a/src/parser/hover.rs b/src/parser/hover.rs index 110b7f5..02e91e9 100644 --- a/src/parser/hover.rs +++ b/src/parser/hover.rs @@ -47,7 +47,7 @@ impl ParsedTree { } } - pub fn hover(&self, identifier: &str, content: impl AsRef<[u8]>) -> Vec { + pub fn hover(&self, identifier: &str, content: impl AsRef<[u8]>) -> Vec { let mut results = vec![]; self.hover_impl(identifier, self.tree.root_node(), &mut results, content); results @@ -57,7 +57,7 @@ impl ParsedTree { &self, identifier: &str, n: Node, - v: &mut Vec, + v: &mut Vec, content: impl AsRef<[u8]>, ) { if identifier.is_empty() { @@ -77,14 +77,13 @@ impl ParsedTree { } } None => { - let comments: Vec = self + let comments: Vec = self .filter_nodes_from(n, NodeKind::is_userdefined) .into_iter() .filter(|n| { n.utf8_text(content.as_ref()).expect("utf-8 parse error") == identifier }) .filter_map(|n| self.find_preceding_comments(n.id(), content.as_ref())) - .map(MarkedString::String) .collect(); v.extend(comments); diff --git a/src/workspace/hover.rs b/src/workspace/hover.rs index bc30ac4..7eb0e4d 100644 --- a/src/workspace/hover.rs +++ b/src/workspace/hover.rs @@ -1,6 +1,6 @@ use std::{collections::HashMap, sync::LazyLock}; -use async_lsp::lsp_types::MarkedString; +use async_lsp::lsp_types::{MarkupContent, MarkupKind}; use crate::{ formatter::ProtoFormatter, state::ProtoLanguageState, utils::split_identifier_package, @@ -10,106 +10,600 @@ static BUITIN_DOCS: LazyLock> = LazyLock::ne HashMap::from([ ( "int32", - r#"A 32-bit integer (varint encoding) - + r#"*int32* builtin type, A 32-bit integer (varint encoding) +--- Values of this type range between `-2147483648` and `2147483647`. Beware that negative values are encoded as five bytes on the wire!"#, ), ( "int64", - r#"A 64-bit integer (varint encoding) - + r#"*int64* builtin type, A 64-bit integer (varint encoding) +--- Values of this type range between `-9223372036854775808` and `9223372036854775807`. Beware that negative values are encoded as ten bytes on the wire!"#, ), ( "uint32", - r#"A 32-bit unsigned integer (varint encoding) - + r#"*uint32* builtin type, A 32-bit unsigned integer (varint encoding) +--- Values of this type range between `0` and `4294967295`."#, ), ( "uint64", - r#"A 64-bit unsigned integer (varint encoding) - + r#"*uint64* builtin type, A 64-bit unsigned integer (varint encoding) +--- Values of this type range between `0` and `18446744073709551615`."#, ), ( "sint32", - r#"A 32-bit integer (ZigZag encoding) - + r#"*sint32* builtin type, A 32-bit integer (ZigZag encoding) +--- Values of this type range between `-2147483648` and `2147483647`."#, ), ( "sint64", - r#"A 64-bit integer (ZigZag encoding) - + r#"*sint64* builtin type, A 64-bit integer (ZigZag encoding) +--- Values of this type range between `-9223372036854775808` and `9223372036854775807`."#, ), ( "fixed32", - r#"A 32-bit unsigned integer (4-byte encoding) + r#"*fixed32* builtin type, A 32-bit unsigned integer (4-byte encoding) Values of this type range between `0` and `4294967295`."#, ), ( "fixed64", - r#"A 64-bit unsigned integer (8-byte encoding) - + r#"*fixed64* builtin type, A 64-bit unsigned integer (8-byte encoding) +--- Values of this type range between `0` and `18446744073709551615`."#, ), ( "sfixed32", - r#"A 32-bit integer (4-byte encoding) - + r#"*sfixed64* builtin type, A 32-bit integer (4-byte encoding) +--- Values of this type range between `-2147483648` and `2147483647`."#, ), ( "sfixed64", - r#"A 64-bit integer (8-byte encoding) - + r#"*sfixed64* builtin type, A 64-bit integer (8-byte encoding) +--- Values of this type range between `-9223372036854775808` and `9223372036854775807`."#, ), ( "float", - "A single-precision floating point number (IEEE-745.2008 binary32).", + r#"*float* builtin type +--- +A single-precision floating point number (IEEE-745.2008 binary32)."#, ), ( "double", - "A double-precision floating point number (IEEE-745.2008 binary64).", + r#"*double* builtin type, +--- +A double-precision floating point number (IEEE-745.2008 binary64)."#, ), ( "string", - r#"A string of text. - + r#"*string* builtin type, A string of text. +--- Stores at most 4GB of text. Intended to be UTF-8 encoded Unicode; use `bytes` if you need other encodings."#, ), ( "bytes", - r#"A blob of arbitrary bytes. - + r#"*bytes* builtin type, A blob of arbitrary bytes. +--- Stores at most 4GB of binary data. Encoded as base64 in JSON."#, ), ( "bool", - r#"A Boolean value: `true` or `false`. - + r#"*bool* builtin type, A Boolean value: `true` or `false`. +--- Encoded as a single byte: `0x00` or `0xff` (all non-zero bytes decode to `true`)."#, ), ( "default", - r#"A magic option that specifies the field's default value. - + r#"*default* builtin type, A magic option that specifies the field's default value. +--- Unlike every other option on a field, this does not have a corresponding field in `google.protobuf.FieldOptions`; it is implemented by compiler magic."#, ), ]) }); +static WELLKNOWN_DOCS: LazyLock> = LazyLock::new(|| { + HashMap::from([ + ( + "google.protobuf.Any", + r#"*Any* wellknown type +--- +`Any` contains an arbitrary serialized message along with a URL that describes the type of the serialized message. +The JSON representation of an Any value uses the regular representation of the deserialized, embedded message, with an additional field @type which contains the type URL. +--- +```proto +message Any { + string type_url = 1; // A URL/resource name that uniquely identifies the type of the serialized protocol buffer message + bytes value = 2; // Must be a valid serialized protocol buffer +} +``` + +"#, + ), + ( + "google.protobuf.Api", + r#"*Api* well known type +--- +`Api` is a light-weight descriptor for a protocol buffer service. +--- +```proto +message Api { + string name = 1; // The fully qualified name of this api, including package name followed by the api's simple name + repeated Method methods = 2; // The methods of this api, in unspecified order + repeated Option options = 3; // Any metadata attached to the API + string version = 4; // A version string fo this interface + SourceContext source_context = 5; // Source context for the protocol buffer service + repeated Mixin mixins = 6; // Included interfaces + Syntax syntax = 7; // Source syntax of the service +} +``` + +"#, + ), + ( + "google.protobuf.BoolValue", + r#"*BoolValue* well known type, Wrapper message for bool +--- +The JSON representation for `BoolValue` is JSON `true` and `false` +--- +```proto +message BoolValue { + bool value = 1; +} +``` +"#, + ), + ( + "google.protobuf.BytesValue", + r#"*BytesValue* well known type, Wrapper message for bytes +--- +The JSON representation for `BytesValue` is JSON string. +--- +```proto +message BytesValue { + bytes value = 1; +} +``` +"#, + ), + ( + "google.protobuf.DoubleValue", + r#"*DoubleValue* well known type, Wrapper message for double +--- +The JSON representation for `DoubleValue` is JSON number. +--- +```proto +message DoubleValue { + double value = 1; +} +``` +"#, + ), + ( + "google.protobuf.Duration", + r#"*Duration* well known type +--- +A Duration represents a signed, fixed-length span of time represented as a count of seconds and fractions of seconds at nanosecond resolution. +It is independent of any calendar and concepts like "day" or "month". +It is related to Timestamp in that the difference between two Timestamp values is a Duration and it can be added or subtracted from a Timestamp. +Range is approximately +-10,000 years. +--- +```proto +message Duration { + int64 seconds = 1; // Signed seconds of the span of time Must be from -315,576,000,000 to +315,576,000,000 inclusive + int32 nanos = 2; // Signed fractions of a second at nanosecond resolution of the span of time. Durations less than one second are represented with a 0 `seconds` field and a positive or negative `nanos` field. +} +``` +"#, + ), + ( + "google.protobuf.Empty", + r#"*Empty* well known type +--- +A generic empty message that you can re-use to avoid defining duplicated empty messages in your APIs. +The JSON representation for Empty is empty JSON object `{}` +"#, + ), + ( + "google.protobuf.Enum", + r#"*Enum* well known type +--- +Enum type definition +--- +```proto +message Enum { + string name = 1; // Enum type name. + repeated EnumValue enumvalue = 2; // Enum value definitions. + repeated Option options = 3; // Protocol buffer options. + SourceContext source_context = 4; // The source context. + Syntax syntax = 5; // The source syntax. + string edition = 6; // The source edition string, only valid when syntax is SYNTAX_EDITIONS. +} +``` +"#, + ), + ( + "google.protobuf.EnumValue", + r#"*EnumValue* well known type +--- +Enum value definition +--- +```proto +message EnumValue { + string name = 1; // Enum value name. + int32 number = 2; // Enum value number. + repeated Option options = 3; // Protocol buffer options. +} + +``` +"#, + ), + ( + "google.protobuf.Field", + r#"*Field* well known type +--- +A single field of a message type. +--- +```proto +message Field { + Kind kind = 1; // The field type. + Cardinality cardinality = 2; // The field cardinality. + int32 number = 3; // The field number. + string name = 4; // The field name. + string type_url = 6; // The field type URL, without the scheme, for message or enumeration types + int32 oneof_index = 7; // The index of the field type in `Type.oneofs`, for message or enumeration types. + bool packed = 8; // Whether to use alternative packed wire representation. + repeated Option options = 9; // The protocol buffer options. + string json_name = 10; // The field JSON name. + string default_value = 11; // The string value of the default value of this field. Proto2 syntax only. +} +``` +"#, + ), + ( + "google.protobuf.Field.Cardinality", + r#"*Field.Cardinality* well known type +--- +Whether a field is optional, required, or repeated. +--- +```proto +enum Cardinality { + CARDINALITY_UNKNOWN = 0; // For fields with unknown cardinality. + CARDINALITY_OPTIONAL = 1; // For optional fields. + CARDINALITY_REQUIRED = 2; // For required fields. Proto2 syntax only. + CARDINALITY_REPEATED = 3; // For repeated fields. +} + +``` +"#, + ), + ( + "google.protobuf.Field.Kind", + r#"*Field.Kind* well known type +--- +Basic field types. +--- +```proto +enum Kind { + TYPE_UNKNOWN = 0; // Field type unknown. + TYPE_DOUBLE = 1; // Field type double. + TYPE_FLOAT = 2; // Field type float. + TYPE_INT64 = 3; // Field type int64. + TYPE_UINT64 = 4; // Field type uint64. + TYPE_INT32 = 5; // Field type int32. + TYPE_FIXED64 = 6; // Field type fixed64. + TYPE_FIXED32 = 7; // Field type fixed32. + TYPE_BOOL = 8; // Field type bool. + TYPE_STRING = 9; // Field type string. + TYPE_GROUP = 10; // Field type group. Proto2 syntax only, and deprecated. + TYPE_MESSAGE = 11; // Field type message. + TYPE_BYTES = 12; // Field type bytes. + TYPE_UINT32 = 13; // Field type uint32. + TYPE_ENUM = 14; // Field type enum. + TYPE_SFIXED32 = 15; // Field type sfixed32. + TYPE_SFIXED64 = 16; // Field type sfixed64. + TYPE_SINT32 = 17; // Field type sint32. + TYPE_SINT64 = 18; // Field type sint64. +} + +``` +"#, + ), + ( + "google.protobuf.FieldMask", + r#"*FieldMask* well known type +--- +`FieldMask` represents a set of symbolic field paths +--- +```proto +message FieldMask { + repeated string paths = 1; // The set of field mask paths. +} + +``` +"#, + ), + ( + "google.protobuf.FloatValue", + r#"*FloatValue* well known type, Wrapper message for `float` +--- +The JSON representation for `FloatValue` is JSON number. +--- +```proto +message FloatValue { + float value = 1; +} +``` +"#, + ), + ( + "google.protobuf.Int32Value", + r#"*Int32Value* well known type, Wrapper message for `int32` +--- +The JSON representation for `Int32Value` is JSON number. +--- +```proto +message Int32Value { + int32 value = 1; +} +``` +"#, + ), + ( + "google.protobuf.Int64Value", + r#"*Int64Value* well known type, Wrapper message for `int64` +--- +The JSON representation for `Int64Value` is JSON string. +--- +```proto +message Int64Value { + int64 value = 1; +} +``` +"#, + ), + ( + "google.protobuf.ListValue", + r#"*ListValue* well known type, Wrapper around a repeated field of values +--- +The JSON representation for `ListValue` is JSON Array. +--- +```proto +message Int64Value { + Value values = 1; +} +``` +"#, + ), + ( + "google.protobuf.Method", + r#"*Method* well known type +--- +Method represents a method of an api. +--- +```proto +message Method { + string name = 1; // The simple name of this method. + string request_type_url = 2; // A URL of the input message type. + bool request_streaming = 3; // If true, the request is streamed. + string response_type_url = 4; // The URL of the output message type. + bool response_streaming = 5; // If true, the response is streamed. + repeated Option options = 6; // Any metadata attached to the method. + Syntax syntax = 7; // The source syntax of this method. +} +``` +"#, + ), + ( + "google.protobuf.Mixin", + r#"*Mixin* well known type +--- +Declares an API Interface to be included in this interface. The including +interface must redeclare all the methods from the included interface, but +documentation and options are inherited +--- +```proto +message Mixin { + string name = 1; // The fully qualified name of the interface which is included. + string root = 2; // If non-empty specifies a path under which inherited HTTP paths are rooted. +} +``` +"#, + ), + ( + "google.protobuf.NullValue", + r#"*NullValue* well known type +--- +`NullValue` is a singleton enumeration to represent the null value for the Value type union. +The JSON representation for NullValue is JSON `null`. +--- +```proto +enum NullValue { + NULL_VALUE = 1; +} +``` +"#, + ), + ( + "google.protobuf.Option", + r#"*Option* well known type +--- +A protocol buffer option, which can be attached to a message, field, enumeration, etc +--- +```proto +message Option { + string name = 1; // The option's name + Any value = 2; // The option's value +} +``` +"#, + ), + ( + "google.protobuf.SourceContext", + r#"*SourceContext* well known type +--- +`SourceContext` represents information about the source of a protobuf element, like the file in which it is defined +--- +```proto +message SourceContext { + string file_name = 1; // The path-qualified name of the .proto file that contained the associated protobuf element. +} +``` +"#, + ), + ( + "google.protobuf.StringValue", + r#"*StringValue* well known type, Wrapper message for string. +--- +The JSON representation for `StringValue` is JSON string. +--- +```proto +message StringValue { + string value = 1; +} +``` +"#, + ), + ( + "google.protobuf.Struct", + r#"*Struct* well known type +--- +`Struct` represents a structured data value, consisting of fields +which map to dynamically typed values. +--- +```proto +message Struct { + map fields = 1; // Unordered map of dynamically typed values. +} +``` +"#, + ), + ( + "google.protobuf.Syntax", + r#"*Syntax* well known type +--- +The syntax in which a protocol buffer element is defined +--- +```proto +enum Syntax { + SYNTAX_PROTO2 = 1; + SYNTAX_PROTO3 = 2; + SYNTAX_EDITIONS = 3; +} +``` +"#, + ), + ( + "google.protobuf.Timestamp", + r#"*Timestamp* well known type +--- +`Timestamp` represents a point in time independent of any time zone or calendar, represented as seconds and fractions of seconds at nanosecond resolution in UTC Epoch time +--- +```proto +message Timestamp { + int64 seconds = 1; // Represents seconds of UTC time since Unix epoch + int32 nanos = 2; // Non-negative fractions of a second at nanosecond resolution. +} +``` +"#, + ), + ( + "google.protobuf.Type", + r#"*Type* well known type +--- +A protocol buffer message type +--- +```proto +message Type { + string name = 1; // The fully qualified message name. + repeated Field fields = 2; // The list of fields. + repeated string oneofs = 3; // The list of types appearing in `oneof` definitions in this type. + repeated Option options = 4; // The protocol buffer options. + SourceContext source_context = 5; // The source context. + Syntax syntax = 6; // The source syntax. + string edition = 7; // The source edition string, only valid when syntax is SYNTAX_EDITIONS. +} +``` +"#, + ), + ( + "google.protobuf.UInt32Value", + r#"*UInt32Value* well known type, Wrapper message for `uint32` +--- +The JSON representation for `UInt32Value` is JSON number. +--- +```proto +message UInt32Value { + uint32 value = 1; +} +``` +"#, + ), + ( + "google.protobuf.UInt64Value", + r#"*UInt64Value* well known type, Wrapper message for `uint64` +--- +The JSON representation for `UInt64Value` is JSON string. +--- +```proto +message UInt64Value { + uint64 value = 1; +} +``` +"#, + ), + ( + "google.protobuf.Value", + r#"*Value* well known type +--- +`Value` represents a dynamically typed value which can be either +null, a number, a string, a boolean, a recursive struct value, or a +list of values. + +The JSON representation for `Value` is JSON value. +--- +```proto +message Value { + oneof kind { + NullValue null_value = 1; // Represents a null value. + double number_value = 2; // Represents a double value. + string string_value = 3; // Represents a string value. + bool bool_value = 4; // Represents a boolean value. + Struct struct_value = 5; // Represents a structured value. + ListValue list_value = 6; // Represents a repeated `Value`. + } +} +``` +"#, + ), + ]) +}); + impl ProtoLanguageState { - pub fn hover(&self, curr_package: &str, identifier: &str) -> Vec { + pub fn hover(&self, curr_package: &str, identifier: &str) -> Option { if let Some(docs) = BUITIN_DOCS.get(identifier) { - return vec![MarkedString::String(docs.to_string())]; + return Some(MarkupContent { + kind: MarkupKind::Markdown, + value: docs.to_string(), + }); + } + + if let Some(wellknown) = WELLKNOWN_DOCS + .get(identifier) + .or(WELLKNOWN_DOCS.get(format!("google.protobuf.{identifier}").as_str())) + { + return Some(MarkupContent { + kind: MarkupKind::Markdown, + value: wellknown.to_string(), + }); } let (mut package, identifier) = split_identifier_package(identifier); @@ -117,12 +611,22 @@ impl ProtoLanguageState { package = curr_package; } - self.get_trees_for_package(package) - .into_iter() - .fold(vec![], |mut v, tree| { - v.extend(tree.hover(identifier, self.get_content(&tree.uri))); - v - }) + for tree in self.get_trees_for_package(package) { + let res = tree.hover(identifier, self.get_content(&tree.uri)); + if !res.is_empty() { + return Some(MarkupContent { + kind: MarkupKind::Markdown, + value: format!( + r#"`{identifier}` message or enum type, package: `{package}` +--- +{}"#, + res[0].clone() + ), + }); + } + } + + None } } diff --git a/src/workspace/snapshots/protols__workspace__hover__test__workspace_test_hover-2.snap b/src/workspace/snapshots/protols__workspace__hover__test__workspace_test_hover-2.snap index 1dbb2d6..be32ca1 100644 --- a/src/workspace/snapshots/protols__workspace__hover__test__workspace_test_hover-2.snap +++ b/src/workspace/snapshots/protols__workspace__hover__test__workspace_test_hover-2.snap @@ -3,4 +3,5 @@ source: src/workspace/hover.rs expression: "state.hover(\"com.workspace\", \"int64\")" snapshot_kind: text --- -- "A 64-bit integer (varint encoding)\n\nValues of this type range between `-9223372036854775808` and `9223372036854775807`.\nBeware that negative values are encoded as ten bytes on the wire!" +kind: markdown +value: "*int64* builtin type, A 64-bit integer (varint encoding)\n---\nValues of this type range between `-9223372036854775808` and `9223372036854775807`.\nBeware that negative values are encoded as ten bytes on the wire!" diff --git a/src/workspace/snapshots/protols__workspace__hover__test__workspace_test_hover-3.snap b/src/workspace/snapshots/protols__workspace__hover__test__workspace_test_hover-3.snap index 00bbfa0..8200b12 100644 --- a/src/workspace/snapshots/protols__workspace__hover__test__workspace_test_hover-3.snap +++ b/src/workspace/snapshots/protols__workspace__hover__test__workspace_test_hover-3.snap @@ -3,4 +3,5 @@ source: src/workspace/hover.rs expression: "state.hover(\"com.workspace\", \"Author.Address\")" snapshot_kind: text --- -- Address is a Address +kind: markdown +value: "`Author.Address` message or enum type, package: `com.workspace`\n---\nAddress is a Address" diff --git a/src/workspace/snapshots/protols__workspace__hover__test__workspace_test_hover-4.snap b/src/workspace/snapshots/protols__workspace__hover__test__workspace_test_hover-4.snap index 130490c..40d58b4 100644 --- a/src/workspace/snapshots/protols__workspace__hover__test__workspace_test_hover-4.snap +++ b/src/workspace/snapshots/protols__workspace__hover__test__workspace_test_hover-4.snap @@ -1,5 +1,7 @@ --- source: src/workspace/hover.rs -expression: "state.hover(\"com.utility\", \"Baz\")" +expression: "state.hover(\"com.workspace\", \"com.utility.Foobar.Baz\")" +snapshot_kind: text --- -- What is baz? +kind: markdown +value: "`Foobar.Baz` message or enum type, package: `com.utility`\n---\nWhat is baz?" diff --git a/src/workspace/snapshots/protols__workspace__hover__test__workspace_test_hover-5.snap b/src/workspace/snapshots/protols__workspace__hover__test__workspace_test_hover-5.snap index 152acde..6ca79f3 100644 --- a/src/workspace/snapshots/protols__workspace__hover__test__workspace_test_hover-5.snap +++ b/src/workspace/snapshots/protols__workspace__hover__test__workspace_test_hover-5.snap @@ -3,4 +3,5 @@ source: src/workspace/hover.rs expression: "state.hover(\"com.utility\", \"Baz\")" snapshot_kind: text --- -- What is baz? +kind: markdown +value: "`Baz` message or enum type, package: `com.utility`\n---\nWhat is baz?" diff --git a/src/workspace/snapshots/protols__workspace__hover__test__workspace_test_hover.snap b/src/workspace/snapshots/protols__workspace__hover__test__workspace_test_hover.snap index 43180c0..fe89f2c 100644 --- a/src/workspace/snapshots/protols__workspace__hover__test__workspace_test_hover.snap +++ b/src/workspace/snapshots/protols__workspace__hover__test__workspace_test_hover.snap @@ -1,5 +1,7 @@ --- source: src/workspace/hover.rs -expression: "state.hover(\"com.library\", \"Author\")" +expression: "state.hover(\"com.workspace\", \"Author\")" +snapshot_kind: text --- -- A author is a author +kind: markdown +value: "`Author` message or enum type, package: `com.workspace`\n---\nA author is a author" From d867b1b3040cdc94766f77b4424002ee2045804b Mon Sep 17 00:00:00 2001 From: Asaf Flescher Date: Mon, 30 Dec 2024 21:48:27 -0500 Subject: [PATCH 3/5] Formatter uses stdin instead of filepath (#46) --- src/formatter/clang.rs | 16 +++++++++------- src/formatter/mod.rs | 4 ++-- src/lsp.rs | 4 ++-- 3 files changed, 13 insertions(+), 11 deletions(-) diff --git a/src/formatter/clang.rs b/src/formatter/clang.rs index d2e72d5..08e54c1 100644 --- a/src/formatter/clang.rs +++ b/src/formatter/clang.rs @@ -85,13 +85,14 @@ impl ClangFormatter { Some(p) } - fn get_command(&self, u: &Path) -> Command { + fn get_command(&self, f: &str, u: &Path) -> Option { let mut c = Command::new(self.path.as_str()); if let Some(wd) = self.working_dir.as_ref() { c.current_dir(wd.as_str()); } - c.args([u.to_str().unwrap(), "--output-replacements-xml"]); - c + c.stdin(File::open(u).ok()?); + c.args(["--output-replacements-xml", format!("--assume-filename={f}").as_str()]); + Some(c) } fn output_to_textedit(&self, output: &str, content: &str) -> Option> { @@ -107,9 +108,10 @@ impl ClangFormatter { } impl ProtoFormatter for ClangFormatter { - fn format_document(&self, content: &str) -> Option> { + fn format_document(&self, filename: &str, content: &str) -> Option> { let p = self.get_temp_file_path(content)?; - let output = self.get_command(p.as_ref()).output().ok()?; + let mut cmd = self.get_command(filename, p.as_ref())?; + let output = cmd.output().ok()?; if !output.status.success() { tracing::error!( status = output.status.code(), @@ -120,12 +122,12 @@ impl ProtoFormatter for ClangFormatter { self.output_to_textedit(&String::from_utf8_lossy(&output.stdout), content) } - fn format_document_range(&self, r: &Range, content: &str) -> Option> { + fn format_document_range(&self, r: &Range, filename: &str, content: &str) -> Option> { let p = self.get_temp_file_path(content)?; let start = r.start.line + 1; let end = r.end.line + 1; let output = self - .get_command(p.as_ref()) + .get_command(filename, p.as_ref())? .args(["--lines", format!("{start}:{end}").as_str()]) .output() .ok()?; diff --git a/src/formatter/mod.rs b/src/formatter/mod.rs index 67f6913..d5e1993 100644 --- a/src/formatter/mod.rs +++ b/src/formatter/mod.rs @@ -3,6 +3,6 @@ use async_lsp::lsp_types::{Range, TextEdit}; pub mod clang; pub trait ProtoFormatter: Sized { - fn format_document(&self, content: &str) -> Option>; - fn format_document_range(&self, r: &Range, content: &str) -> Option>; + fn format_document(&self, filename: &str, content: &str) -> Option>; + fn format_document_range(&self, r: &Range, filename: &str, content: &str) -> Option>; } diff --git a/src/lsp.rs b/src/lsp.rs index 3674d43..0d9b59a 100644 --- a/src/lsp.rs +++ b/src/lsp.rs @@ -375,7 +375,7 @@ impl LanguageServer for ProtoLanguageServer { let response = self .state .get_formatter() - .and_then(|f| f.format_document(content.as_str())); + .and_then(|f| f.format_document(uri.path(), content.as_str())); Box::pin(async move { Ok(response) }) } @@ -390,7 +390,7 @@ impl LanguageServer for ProtoLanguageServer { let response = self .state .get_formatter() - .and_then(|f| f.format_document_range(¶ms.range, content.as_str())); + .and_then(|f| f.format_document_range(¶ms.range, uri.path(), content.as_str())); Box::pin(async move { Ok(response) }) } From e33e1e68f2a7ec879b3919489d049a2060dd1aea Mon Sep 17 00:00:00 2001 From: Ashar Date: Sat, 4 Jan 2025 21:39:43 +0530 Subject: [PATCH 4/5] chore: bump version to 0.9.0 --- Cargo.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Cargo.toml b/Cargo.toml index 66cf42b..2e8010d 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,7 +1,7 @@ [package] name = "protols" description = "Language server for proto3 files" -version = "0.8.5" +version = "0.9.0" edition = "2021" license = "MIT" homepage = "https://github.com/coder3101/protols" From 8ffe219b120e6da99c7c4af9e9032b12423d900d Mon Sep 17 00:00:00 2001 From: Ashar Date: Sat, 4 Jan 2025 22:14:29 +0530 Subject: [PATCH 5/5] chore: release 0.9.0 with misc fixes (#47) --- Cargo.lock | 2 +- src/formatter/clang.rs | 2 +- src/parser/hover.rs | 1 - src/parser/rename.rs | 2 +- src/utils.rs | 2 +- 5 files changed, 4 insertions(+), 5 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index fa389f0..c91a326 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -512,7 +512,7 @@ dependencies = [ [[package]] name = "protols" -version = "0.8.5" +version = "0.9.0" dependencies = [ "async-lsp", "futures", diff --git a/src/formatter/clang.rs b/src/formatter/clang.rs index 08e54c1..fa59344 100644 --- a/src/formatter/clang.rs +++ b/src/formatter/clang.rs @@ -39,7 +39,7 @@ struct Replacement<'a> { text: Cow<'a, str>, } -impl<'a> Replacement<'a> { +impl Replacement<'_> { fn offset_to_position(offset: usize, content: &str) -> Option { if offset > content.len() { return None; diff --git a/src/parser/hover.rs b/src/parser/hover.rs index 02e91e9..b80cfa7 100644 --- a/src/parser/hover.rs +++ b/src/parser/hover.rs @@ -1,4 +1,3 @@ -use async_lsp::lsp_types::MarkedString; use tree_sitter::Node; use crate::nodekind::NodeKind; diff --git a/src/parser/rename.rs b/src/parser/rename.rs index fbce333..56ad4a8 100644 --- a/src/parser/rename.rs +++ b/src/parser/rename.rs @@ -119,7 +119,7 @@ impl ParsedTree { .filter(|n| { let ntext = n.utf8_text(content.as_ref()).expect("utf-8 parse error"); let sc = format!("{old_identifier}."); - return ntext == old_identifier || ntext.starts_with(&sc); + ntext == old_identifier || ntext.starts_with(&sc) }) .map(|n| { let text = n.utf8_text(content.as_ref()).expect("utf-8 parse error"); diff --git a/src/utils.rs b/src/utils.rs index d14ed7a..2f6301d 100644 --- a/src/utils.rs +++ b/src/utils.rs @@ -55,7 +55,7 @@ pub fn split_identifier_package(s: &str) -> (&str, &str) { }); let (package, identifier) = s.split_at(i); - return (package, identifier.trim_matches('.')); + (package, identifier.trim_matches('.')) } #[cfg(test)]