Skip to content

Commit 4398ab1

Browse files
committed
wip: migrate to JSON.jl v1
1 parent 4f03937 commit 4398ab1

File tree

8 files changed

+26
-65
lines changed

8 files changed

+26
-65
lines changed

JSONRPC/Project.toml

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@ version = "0.1.0"
44
authors = ["Shuhei Kadowaki <[email protected]>"]
55

66
[deps]
7-
JSON3 = "0f8b85d8-7281-11e9-16c2-39a750bddbf1"
7+
JSON = "682c06a0-de6a-54ab-a142-c8b1cf79cde6"
88

99
[compat]
10-
JSON3 = "1.14.3"
10+
JSON = "1.2.1"

JSONRPC/src/JSONRPC.jl

Lines changed: 9 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@ module JSONRPC
22

33
export Endpoint, send
44

5-
using JSON3
5+
using JSON
66

77
mutable struct Endpoint
88
in_msg_queue::Channel{Any}
@@ -55,12 +55,9 @@ function Endpoint(in::IO, out::IO, method_dispatcher)
5555
end
5656
end
5757

58-
const Parsed = @NamedTuple{method::Union{Nothing,String}}
59-
6058
function readmsg(io::IO, method_dispatcher)
6159
msg_str = @something read_transport_layer(io) return nothing
62-
parsed = JSON3.read(msg_str, Parsed)
63-
return reparse_msg_str(parsed, msg_str, method_dispatcher)
60+
return reparse_msg_str(JSON.lazy(msg_str), method_dispatcher)
6461
end
6562

6663
function read_transport_layer(io::IO)
@@ -81,20 +78,19 @@ function read_transport_layer(io::IO)
8178
return String(read(io, message_length))
8279
end
8380

84-
function reparse_msg_str(parsed::Parsed, msg_str::String, method_dispatcher)
85-
parsed_method = parsed.method
86-
if parsed_method !== nothing
87-
if haskey(method_dispatcher, parsed_method)
88-
return JSON3.read(msg_str, method_dispatcher[parsed_method])
81+
function reparse_msg_str(lazyjson::JSON.LazyValue, method_dispatcher)
82+
if hasproperty(lazyjson, :method)
83+
if haskey(method_dispatcher, lazyjson.method)
84+
return JSON.parse(lazyjson, method_dispatcher[parsed_method])
8985
end
90-
return JSON3.read(msg_str, Dict{Symbol,Any})
86+
return JSON.parse(lazyjson, Dict{Symbol,Any})
9187
else # TODO parse to ResponseMessage?
92-
return JSON3.read(msg_str, Dict{Symbol,Any})
88+
return JSON.parse(lazyjson, Dict{Symbol,Any})
9389
end
9490
end
9591

9692
function writemsg(io::IO, @nospecialize msg)
97-
msg_str = JSON3.write(msg)
93+
msg_str = JSON.json(msg)
9894
write_transport_layer(io, msg_str)
9995
end
10096

LSP/Project.toml

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@ version = "0.1.0"
44
authors = ["Shuhei Kadowaki <[email protected]>"]
55

66
[deps]
7-
StructTypes = "856f2bd8-1eba-4b0a-8007-ebc267875bd4"
7+
StructUtils = "ec057cc2-7a8d-4b58-b3b3-92acb9f63b42"
88

99
[compat]
10-
StructTypes = "1.11.0"
10+
StructUtils = "2.6.0"

LSP/README.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,7 @@ to faithfully translate the TypeScript LSP specification into idiomatic Julia co
1212
- Uses `Union{Nothing, Type} = nothing` field to represent TypeScript's optional properties (`field?: Type`)
1313
- Supports inheritance through `@extends` to compose interfaces (similar to TypeScript's `extends`)
1414
- Enables anonymous interface definitions within `Union` types for inline type specifications
15-
- Automatically configures `StructTypes.omitempties()` to omit optional fields during JSON serialization
15+
- Integrates with JSON.jl v1 and StructUtils.jl for JSON serialization/deserialization
1616
- Creates method dispatchers for `RequestMessage` and `NotificationMessage` types to enable LSP message routing
1717

1818
- **`@namespace` macro**: Creates Julia modules containing typed constants that correspond to TypeScript `namespace`s:

LSP/src/LSP.jl

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
module LSP
22

3-
using StructTypes: StructTypes
3+
using StructUtils: StructUtils
44

55
include("URIs2/URIs2.jl")
66
using ..URIs2: URI

LSP/src/URIs2/URIs2.jl

Lines changed: 5 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@ module URIs2
77

88
export URI, uri2filename, uri2filepath, filename2uri, filepath2uri, @uri_str
99

10-
using StructTypes: StructTypes
10+
using StructUtils: StructUtils
1111

1212
include("vendored_from_uris.jl")
1313

@@ -22,7 +22,7 @@ Details of a Unified Resource Identifier.
2222
- query::Union{Nothing, String}
2323
- fragment::Union{Nothing, String}
2424
"""
25-
struct URI
25+
StructUtils.@nonstruct struct URI
2626
scheme::Union{String,Nothing}
2727
authority::Union{String,Nothing}
2828
path::String
@@ -72,10 +72,9 @@ else
7272
end
7373
end
7474

75-
Base.convert(::Type{URI}, s::AbstractString) = URI(s)
76-
77-
# This overload requires `URI(::AbstractString)` as well, which is defined later
78-
StructTypes.StructType(::Type{URI}) = StructTypes.StringType()
75+
# Tell StructUtils how to serialize/deserialize URI as a string
76+
StructUtils.lower(uri::URI) = string(uri)
77+
StructUtils.lift(::Type{URI}, s::String) = URI(s)
7978

8079
function percent_decode(str::AbstractString)
8180
return unescapeuri(str)

LSP/src/base-protocol.jl

Lines changed: 3 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -3,14 +3,13 @@
33

44
"""
55
A special object representing `null` value.
6-
When used as a field specified as `StructTypes.omitempties`, the key-value pair is not
7-
omitted in the serialized JSON but instead appears as `null`.
6+
When used as a field that might be omitted in the serialized JSON, the key-value pair
7+
appears as `null` instead of being omitted.
88
This special object is specifically intended for use in `ResponseMessage`.
99
"""
1010
struct Null end
1111
const null = Null()
12-
StructTypes.StructType(::Type{Null}) = StructTypes.CustomStruct()
13-
StructTypes.lower(::Null) = nothing
12+
StructUtils.lower(::Null) = nothing
1413
push!(exports, :Null, :null)
1514

1615
const boolean = Bool

LSP/src/utils/interface.jl

Lines changed: 3 additions & 36 deletions
Original file line numberDiff line numberDiff line change
@@ -33,22 +33,19 @@ macro interface(exs...)
3333

3434
is_method_dispatchable = false
3535
structbody = Expr(:block, __source__)
36-
omittable_fields = Set{Symbol}()
3736
extended_fields = Dict{Symbol,Vector{Int}}()
3837
duplicated_fields = Int[]
3938
if extends !== nothing
4039
for extend in extends
4140
is_method_dispatchable |= extend === :RequestMessage || extend === :NotificationMessage
4241
add_extended_interface!(toplevelblk, structbody,
43-
omittable_fields,
4442
extended_fields,
4543
duplicated_fields,
4644
extend,
4745
__source__)
4846
end
4947
end
5048
_, method = process_interface_def!(toplevelblk, structbody,
51-
omittable_fields,
5249
extended_fields,
5350
duplicated_fields,
5451
defex,
@@ -68,35 +65,22 @@ macro interface(exs...)
6865
end
6966

7067
function process_interface_def!(toplevelblk::Expr, structbody::Expr,
71-
omittable_fields::Set{Symbol},
7268
extended_fields::Dict{Symbol,Vector{Int}},
7369
duplicated_fields::Vector{Int},
7470
defex::Expr,
7571
__source__::LineNumberNode,
7672
Name::Union{Symbol,Nothing})
77-
method = _process_interface_def!(toplevelblk, structbody, omittable_fields, extended_fields, duplicated_fields, defex, __source__)
73+
method = _process_interface_def!(toplevelblk, structbody, extended_fields, duplicated_fields, defex, __source__)
7874
deleteat!(structbody.args, duplicated_fields)
7975
is_anon = Name === nothing
8076
if is_anon
8177
# Name = Symbol("AnonymousInterface", string(__source__)) # XXX this doesn't work probably due to Julia internal bug
8278
Name = Symbol("AnonymousInterface", gensym())
8379
end
8480
structdef = Expr(:struct, false, Name, structbody)
81+
# TODO Use `StructUtils.@defaults` here?
8582
kwdef = Expr(:macrocall, GlobalRef(Base, Symbol("@kwdef")), __source__, structdef) # `@kwdef` will attach `Core.__doc__` automatically
8683
push!(toplevelblk.args, kwdef)
87-
if !isempty(omittable_fields)
88-
omitempties = Tuple(omittable_fields)
89-
if Name === :InitializeParams
90-
# HACK: In the write->read roundtrip of `InitializationRequest` in the
91-
# `withserver` test, empty `workspaceFolders` needs to be serialized without
92-
# being omitted. So override the `omitempties` for `InitializeParams`.
93-
# In the normal lifecycle of a language server, `InitializeParams` is never
94-
# serialized, so adding this hack doesn't affect the language server's behavior
95-
# (except in the tests)
96-
omitempties = ()
97-
end
98-
push!(toplevelblk.args, :(StructTypes.omitempties(::Type{$Name}) = $omitempties))
99-
end
10084
if is_anon
10185
push!(toplevelblk.args, :(Base.convert(::Type{$Name}, nt::NamedTuple) = $Name(; nt...)))
10286
end
@@ -107,13 +91,11 @@ function process_interface_def!(toplevelblk::Expr, structbody::Expr,
10791
end
10892

10993
function add_extended_interface!(toplevelblk::Expr, structbody::Expr,
110-
omittable_fields::Set{Symbol},
11194
extended_fields::Dict{Symbol,Vector{Int}},
11295
duplicated_fields::Vector{Int},
11396
extend::Symbol,
11497
__source__::LineNumberNode)
11598
return _process_interface_def!(toplevelblk, structbody,
116-
omittable_fields,
11799
extended_fields,
118100
duplicated_fields,
119101
_INTERFACE_DEFS[extend],
@@ -122,7 +104,6 @@ function add_extended_interface!(toplevelblk::Expr, structbody::Expr,
122104
end
123105

124106
function _process_interface_def!(toplevelblk::Expr, structbody::Expr,
125-
omittable_fields::Set{Symbol},
126107
extended_fields::Dict{Symbol,Vector{Int}},
127108
duplicated_fields::Vector{Int},
128109
defex::Expr,
@@ -157,7 +138,6 @@ function _process_interface_def!(toplevelblk::Expr, structbody::Expr,
157138
error("Unsupported syntax found in `@interface`: ", defex)
158139
end
159140
end
160-
omittable = false
161141
if Meta.isexpr(fieldline, :(=))
162142
fielddecl, default = fieldline.args
163143
if Meta.isexpr(fielddecl, :(::))
@@ -177,14 +157,6 @@ function _process_interface_def!(toplevelblk::Expr, structbody::Expr,
177157
if Meta.isexpr(fielddecl, :(::))
178158
fieldname = fielddecl.args[1]
179159
fieldtype = fielddecl.args[2]
180-
if Meta.isexpr(fieldtype, :curly) && fieldtype.args[1] === :Union
181-
for i = 2:length(fieldtype.args)
182-
ufty = fieldtype.args[i]
183-
if ufty === :Nothing
184-
omittable = true
185-
end
186-
end
187-
end
188160
if Meta.isexpr(fieldtype, :curly)
189161
for i = 1:length(fieldtype.args)
190162
ufty = fieldtype.args[i]
@@ -203,12 +175,8 @@ function _process_interface_def!(toplevelblk::Expr, structbody::Expr,
203175
fieldname = fielddecl
204176
end
205177
fieldname isa Symbol || error("Invalid `@interface` syntax: ", defex)
206-
if omittable
207-
push!(omittable_fields, fieldname)
208-
end
209178
if haskey(extended_fields, fieldname)
210179
append!(duplicated_fields, extended_fields[fieldname])
211-
omittable || delete!(omittable_fields, fieldname)
212180
end
213181
push!(structbody.args, fieldline)
214182
if extending
@@ -221,11 +189,10 @@ function _process_interface_def!(toplevelblk::Expr, structbody::Expr,
221189
end
222190

223191
function process_anon_interface_def!(toplevelblk::Expr, defex::Expr, __source__::LineNumberNode) # Anonymous @interface
224-
omittable_fields = Set{Symbol}()
225192
extended_fields = Dict{Symbol,Vector{Int}}()
226193
duplicated_fields = Int[]
227194
res, _ = process_interface_def!(toplevelblk, Expr(:block),
228-
omittable_fields, extended_fields, duplicated_fields, defex, __source__, #=Name=#nothing)
195+
extended_fields, duplicated_fields, defex, __source__, #=Name=#nothing)
229196
if !(isempty(extended_fields) && isempty(duplicated_fields))
230197
error("`Anonymous @interface` does not support extension", defex)
231198
end

0 commit comments

Comments
 (0)