diff --git a/JSONRPC/.gitignore b/JSONRPC/.gitignore deleted file mode 100644 index ba39cc531..000000000 --- a/JSONRPC/.gitignore +++ /dev/null @@ -1 +0,0 @@ -Manifest.toml diff --git a/JSONRPC/Project.toml b/JSONRPC/Project.toml deleted file mode 100644 index d5a47fd68..000000000 --- a/JSONRPC/Project.toml +++ /dev/null @@ -1,10 +0,0 @@ -name = "JSONRPC" -uuid = "a2756949-8476-49a1-a294-231eace0f283" -version = "0.1.0" -authors = ["Shuhei Kadowaki "] - -[deps] -JSON3 = "0f8b85d8-7281-11e9-16c2-39a750bddbf1" - -[compat] -JSON3 = "1.14.3" diff --git a/LSP/Project.toml b/LSP/Project.toml index c1a7dcd84..5cc4d8de8 100644 --- a/LSP/Project.toml +++ b/LSP/Project.toml @@ -4,7 +4,9 @@ version = "0.1.0" authors = ["Shuhei Kadowaki "] [deps] +JSON3 = "0f8b85d8-7281-11e9-16c2-39a750bddbf1" StructTypes = "856f2bd8-1eba-4b0a-8007-ebc267875bd4" [compat] +JSON3 = "1.14.3" StructTypes = "1.11.0" diff --git a/LSP/src/utils/interface.jl b/LSP/src/DSL/interface.jl similarity index 79% rename from LSP/src/utils/interface.jl rename to LSP/src/DSL/interface.jl index 0d800c26b..897c62307 100644 --- a/LSP/src/utils/interface.jl +++ b/LSP/src/DSL/interface.jl @@ -1,5 +1,82 @@ -const _INTERFACE_DEFS = Dict{Symbol,Expr}() +const _interface_defs_ = Dict{Symbol,Expr}() +""" + @interface InterfaceName [@extends ParentInterface] begin + field::Type + optionalField::Union{Nothing, Type} = nothing + ... + end + +Creates a Julia struct with keyword constructor that mirrors TypeScript interface +definitions from the LSP specification, featuring: +- Keyword constructor: All structs are created with `@kwdef`, enabling keyword-based construction +- Optional fields: Use `Union{Nothing, Type} = nothing` to represent TypeScript's optional properties (`field?: Type`) +- Interface inheritance: Use `@extends` to compose interfaces from parent interfaces (similar to TypeScript's `extends`) +- Anonymous interfaces: Can be used inline within field type declarations (e.g., `Union{Nothing, @interface begin ... end}`) +- Method dispatching: For interfaces extending `RequestMessage` or `NotificationMessage`, + automatically registers the message type in the `method_dispatcher` dictionary for LSP message routing + +# Field declarations + +Fields can be declared with type annotations and optional default values: + +```julia +@interface Example begin + "Required field" + requiredField::String + + "Optional field that can be omitted" + optionalField::Union{Nothing, Bool} = nothing + + "Field with default value" + fieldWithDefault::Int = 0 +end +``` + +# Anonymous interfaces + +Anonymous interfaces can be used within field type declarations for inline type specifications: + +```julia +@interface Outer begin + nested::Union{Nothing, @interface begin + innerField::String + end} = nothing +end +``` + +# Inheritance + +The `@extends` syntax allows composing interfaces from one or more parent interfaces: + +```julia +@interface Child @extends Parent begin + childField::String +end + +@interface MultipleInheritance @extends (Parent1, Parent2) begin + additionalField::Int +end +``` + +When a child interface defines a field with the same name as a parent interface, the child's definition takes precedence. + +# LSP message dispatching + +For interfaces that extend `RequestMessage` or `NotificationMessage`, a `method::String` +field must be defined: +```julia +@interface MyRequest @extends RequestMessage begin + method::String = "textDocument/myRequest" + params::MyParams +end +``` + +This automatically registers the interface in `method_dispatcher["textDocument/myRequest"]`, +enabling routing of incoming LSP messages to the appropriate handler. + +See also: [`@namespace`](@ref) +""" macro interface(exs...) nexs = length(exs) if nexs == 1 @@ -101,7 +178,7 @@ function process_interface_def!(toplevelblk::Expr, structbody::Expr, push!(toplevelblk.args, :(Base.convert(::Type{$Name}, nt::NamedTuple) = $Name(; nt...))) end if !is_anon - push!(toplevelblk.args, :($(GlobalRef(@__MODULE__, :_INTERFACE_DEFS))[$(QuoteNode(Name))] = $(QuoteNode(structbody)))) + push!(toplevelblk.args, :($(GlobalRef(@__MODULE__, :_interface_defs_))[$(QuoteNode(Name))] = $(QuoteNode(structbody)))) end return Name, method end @@ -116,7 +193,7 @@ function add_extended_interface!(toplevelblk::Expr, structbody::Expr, omittable_fields, extended_fields, duplicated_fields, - _INTERFACE_DEFS[extend], + _interface_defs_[extend], __source__; extending = true) end diff --git a/LSP/src/utils/namespace.jl b/LSP/src/DSL/namespace.jl similarity index 62% rename from LSP/src/utils/namespace.jl rename to LSP/src/DSL/namespace.jl index 3df070d00..64732774a 100644 --- a/LSP/src/utils/namespace.jl +++ b/LSP/src/DSL/namespace.jl @@ -1,3 +1,45 @@ +""" + @namespace NamespaceName::Type begin + CONSTANT_NAME = value + ... + end + +Creates a Julia module containing typed constants that correspond to TypeScript `namespace` +definitions from the LSP specification. + +# Type references + +Due to the design that mimics TypeScript `namespaces` using Julia's module system, +namespace types must be referenced with the `.Ty` suffix: + +```julia +@namespace SignatureHelpTriggerKind::Int begin + Invoked = 1 + TriggerCharacter = 2 + ContentChange = 3 +end + +@interface SignatureHelpContext begin + ... + # Use as type annotation + triggerKind::SignatureHelpTriggerKind.Ty + ... +end +``` + +This is a constraint of Julia's module scoping rules, where constants and type aliases +within modules cannot be accessed without explicit qualification. + +# Implementation details + +The macro generates: +1. A Julia module with the specified namespace name +2. Constants with the specified type and values +3. A `Ty` type alias equal to the specified type for convenient type references +4. Automatic export of the namespace name to the parent module + +See also: [`@interface`](@ref) +""" macro namespace(exs...) nexs = length(exs) nexs == 2 || error("`@namespace` expected 2 arguments: ", exs) diff --git a/LSP/src/LSP.jl b/LSP/src/LSP.jl index 1d44a102a..27966eb5c 100644 --- a/LSP/src/LSP.jl +++ b/LSP/src/LSP.jl @@ -8,8 +8,8 @@ using ..URIs2: URI const exports = Set{Symbol}() const method_dispatcher = Dict{String,DataType}() -include("utils/interface.jl") -include("utils/namespace.jl") +include("DSL/interface.jl") +include("DSL/namespace.jl") include("base-protocol.jl") include("basic-json-structures.jl") @@ -40,11 +40,14 @@ include("workspace-features/apply-edit.jl") include("window-features.jl") include("lifecycle-messages/initialize.jl") +include("communication.jl") +module Communication + using ..LSP: Endpoint, send + export Endpoint, send +end + for name in exports Core.eval(@__MODULE__, Expr(:export, name)) end -export - method_dispatcher - end # module LSP diff --git a/JSONRPC/src/JSONRPC.jl b/LSP/src/communication.jl similarity index 60% rename from JSONRPC/src/JSONRPC.jl rename to LSP/src/communication.jl index 218952533..f15c0d4b7 100644 --- a/JSONRPC/src/JSONRPC.jl +++ b/LSP/src/communication.jl @@ -1,9 +1,40 @@ -module JSONRPC +using JSON3: JSON3 -export Endpoint, send +""" + Endpoint -using JSON3 +A bidirectional communication endpoint for Language Server Protocol messages. +`Endpoint` manages asynchronous reading and writing of LSP messages over IO streams. +It spawns two separate tasks: +- A read task that continuously reads messages from the input stream and queues them +- A write task that continuously writes messages from the output queue to the output stream + +Both tasks run on the `:interactive` thread pool to ensure responsive message handling. + +- `in_msg_queue::Channel{Any}`: Queue of incoming messages read from the input stream +- `out_msg_queue::Channel{Any}`: Queue of outgoing messages to be written to the output stream +- `read_task::Task`: Task handling message reading +- `write_task::Task`: Task handling message writing +- `isopen::Bool`: Atomic flag indicating whether the endpoint is open + +There are two constructors: +- `Endpoint(in::IO, out::IO)` +- `Endpoint(err_handler, in::IO, out::IO)` + +The later creates an endpoint with custom error handler or default error handler that logs to `stderr`. +The error handler should have signature `(isread::Bool, err, backtrace) -> nothing`. + +# Example +```julia +endpoint = Endpoint(stdin, stdout) +for msg in endpoint + # Process incoming messages + send(endpoint, response) +end +close(endpoint) +``` +""" mutable struct Endpoint in_msg_queue::Channel{Any} out_msg_queue::Channel{Any} @@ -11,7 +42,7 @@ mutable struct Endpoint write_task::Task @atomic isopen::Bool - function Endpoint(err_handler, in::IO, out::IO, method_dispatcher) + function Endpoint(err_handler, in::IO, out::IO) in_msg_queue = Channel{Any}(Inf) out_msg_queue = Channel{Any}(Inf) @@ -22,7 +53,7 @@ mutable struct Endpoint break end msg = @something try - readmsg(in, method_dispatcher) + readlsp(in) catch err err_handler(#=isread=#true, err, catch_backtrace()) continue @@ -34,7 +65,7 @@ mutable struct Endpoint write_task = Threads.@spawn :interactive for msg in out_msg_queue if isopen(out) try - writemsg(out, msg) + writelsp(out, msg) catch err err_handler(#=isread=#false, err, catch_backtrace()) continue @@ -50,21 +81,15 @@ mutable struct Endpoint end end -function Endpoint(in::IO, out::IO, method_dispatcher) - Endpoint(in, out, method_dispatcher) do isread::Bool, err, bt +function Endpoint(in::IO, out::IO) + Endpoint(in, out) do isread::Bool, err, bt @nospecialize err @error "Error in Endpoint $(isread ? "reading" : "writing") task" Base.display_error(stderr, err, bt) end end -const Parsed = @NamedTuple{method::Union{Nothing,String}} - -function readmsg(io::IO, method_dispatcher) - msg_str = @something read_transport_layer(io) return nothing - parsed = JSON3.read(msg_str, Parsed) - return reparse_msg_str(parsed, msg_str, method_dispatcher) -end +readlsp(io::IO) = to_lsp_object(@something read_transport_layer(io) return nothing) function read_transport_layer(io::IO) line = chomp(readline(io)) @@ -84,22 +109,22 @@ function read_transport_layer(io::IO) return String(read(io, message_length)) end -function reparse_msg_str(parsed::Parsed, msg_str::String, method_dispatcher) +const Parsed = @NamedTuple{method::Union{Nothing,String}} + +function to_lsp_object(msg_str::AbstractString) + parsed = JSON3.read(msg_str, Parsed) parsed_method = parsed.method if parsed_method !== nothing if haskey(method_dispatcher, parsed_method) return JSON3.read(msg_str, method_dispatcher[parsed_method]) end return JSON3.read(msg_str, Dict{Symbol,Any}) - else # TODO parse to ResponseMessage? - return JSON3.read(msg_str, Dict{Symbol,Any}) end + # TODO Parse response message? + return JSON3.read(msg_str, Dict{Symbol,Any}) end -function writemsg(io::IO, @nospecialize msg) - msg_str = JSON3.write(msg) - write_transport_layer(io, msg_str) -end +writelsp(io::IO, @nospecialize msg) = write_transport_layer(io, to_lsp_json(msg)) function write_transport_layer(io::IO, response::String) response_utf8 = transcode(UInt8, response) @@ -110,6 +135,8 @@ function write_transport_layer(io::IO, response::String) return n end +to_lsp_json(@nospecialize msg) = JSON3.write(msg) + function Base.close(endpoint::Endpoint) flush(endpoint) close(endpoint.in_msg_queue) @@ -139,10 +166,23 @@ function Base.iterate(endpoint::Endpoint, _=nothing) return take!(endpoint.in_msg_queue), nothing end +""" + send(endpoint::Endpoint, msg) + +Send a message through the endpoint's output queue. + +The message will be asynchronously written to the output stream by the endpoint's write task. +This function is non-blocking and returns immediately after queueing the message. + +# Arguments +- `endpoint::Endpoint`: The endpoint to send the message through +- `msg`: The message to send (typically an LSP message structure) + +# Throws +- `ErrorException`: If the endpoint is closed +""" function send(endpoint::Endpoint, @nospecialize(msg::Any)) check_dead_endpoint!(endpoint) put!(endpoint.out_msg_queue, msg) - return msg + nothing end - -end # module JSONRPC diff --git a/LSP/src/language-features/completions.jl b/LSP/src/language-features/completions.jl index 6197782dc..8e0c33885 100644 --- a/LSP/src/language-features/completions.jl +++ b/LSP/src/language-features/completions.jl @@ -234,7 +234,6 @@ end export CompletionData @interface CompletionItem begin - """ The label of this completion item. diff --git a/LSP/src/language-features/definition.jl b/LSP/src/language-features/definition.jl index 8c9043eb9..1311bd314 100644 --- a/LSP/src/language-features/definition.jl +++ b/LSP/src/language-features/definition.jl @@ -28,4 +28,4 @@ end @interface DefinitionResponse @extends ResponseMessage begin result::Union{Location, Vector{Location}, Vector{LocationLink}, Null, Nothing} -end \ No newline at end of file +end diff --git a/Project.toml b/Project.toml index a26c1c055..10fcbd06b 100644 --- a/Project.toml +++ b/Project.toml @@ -9,7 +9,6 @@ projects = ["docs", "test"] [deps] Configurations = "5218b696-f38b-4ac9-8b61-a12ec717816d" JET = "c3a54625-cd67-489e-a8e7-0a5a0ff4e31b" -JSONRPC = "a2756949-8476-49a1-a294-231eace0f283" JuliaLowering = "f3c80556-a63f-4383-b822-37d64f81a311" JuliaSyntax = "70703baa-626e-46a2-a12c-08ffd08c73b4" LSP = "880dcf91-6fde-4251-87fc-bfd84012291a" @@ -22,7 +21,6 @@ TOML = "fa267f1f-6049-4f14-aa54-33bafae1ed76" [sources] JET = {rev = "1e84376", url = "https://github.com/aviatesk/JET.jl"} -JSONRPC = {path = "JSONRPC"} JuliaLowering = {rev = "avi/JETLS-JSJL-head", url = "https://github.com/JuliaLang/julia", subdir="JuliaLowering"} JuliaSyntax = {rev = "avi/JETLS-JSJL-head", url = "https://github.com/JuliaLang/julia", subdir="JuliaSyntax"} LSP = {path = "LSP"} @@ -30,7 +28,6 @@ LSP = {path = "LSP"} [compat] Configurations = "0.17.6" JET = "0.10.6" -JSONRPC = "0.1" JuliaLowering = "1" JuliaSyntax = "2" LSP = "0.1" diff --git a/runserver.jl b/runserver.jl index 38d7b04bd..a1f1a2b19 100644 --- a/runserver.jl +++ b/runserver.jl @@ -118,7 +118,7 @@ function (@main)(args::Vector{String})::Cint # Try connecting first (for VSCode), fallback to listen/accept (for other clients). try conn = connect(pipe_name) - endpoint = LSEndpoint(conn, conn) + endpoint = Endpoint(conn, conn) @info "Connected to existing $pipe_type" pipe_name catch # Connection failed - client expects us to create the socket @@ -126,7 +126,7 @@ function (@main)(args::Vector{String})::Cint server_socket = listen(pipe_name) @info "Waiting for connection on $pipe_type: $pipe_name" conn = accept(server_socket) - endpoint = LSEndpoint(conn, conn) + endpoint = Endpoint(conn, conn) @info "Accepted connection on $pipe_type" end catch e @@ -141,7 +141,7 @@ function (@main)(args::Vector{String})::Cint println(stdout, "$actual_port") @info "Waiting for connection on port" actual_port conn = accept(server_socket) - endpoint = LSEndpoint(conn, conn) + endpoint = Endpoint(conn, conn) @info "Connected via TCP socket" actual_port catch e @error "Failed to create socket connection" socket_port @@ -149,7 +149,7 @@ function (@main)(args::Vector{String})::Cint return Cint(1) end else # use stdio as the communication channel - endpoint = LSEndpoint(stdin, stdout) + endpoint = Endpoint(stdin, stdout) @info "Using stdio for communication" end diff --git a/src/JETLS.jl b/src/JETLS.jl index 4dfb3e6aa..fdfec4484 100644 --- a/src/JETLS.jl +++ b/src/JETLS.jl @@ -1,6 +1,6 @@ module JETLS -export Server, LSEndpoint, runserver +export Server, Endpoint, runserver const __init__hooks__ = Any[] push_init_hooks!(hook) = push!(__init__hooks__, hook) @@ -20,11 +20,9 @@ push_init_hooks!() do end using LSP +using LSP: LSP using LSP.URIs2 - -using JSONRPC: JSONRPC, Endpoint -# constructor of `Endpoint` with LSP method dispatcher -LSEndpoint(args...) = Endpoint(args..., method_dispatcher) +using LSP.Communication: Endpoint using Pkg using JET: CC, JET @@ -148,7 +146,7 @@ allowing the caller side to safely `exit` this Julia process. const self_shutdown_token = SelfShutdownNotification() runserver(args...; kwargs...) = runserver(Returns(nothing), args...; kwargs...) # no callback specified -runserver(callback, in::IO, out::IO; kwargs...) = runserver(callback, LSEndpoint(in, out); kwargs...) +runserver(callback, in::IO, out::IO; kwargs...) = runserver(callback, Endpoint(in, out); kwargs...) runserver(callback, endpoint::Endpoint; kwargs...) = runserver(Server(callback, endpoint); kwargs...) function runserver(server::Server; client_process_id::Union{Nothing,Int}=nothing) shutdown_requested = false diff --git a/src/testrunner/testrunner.jl b/src/testrunner/testrunner.jl index 580bf801a..cefea40ad 100644 --- a/src/testrunner/testrunner.jl +++ b/src/testrunner/testrunner.jl @@ -528,7 +528,7 @@ function _testrunner_run_testset( end result = try - JSONRPC.JSON3.read(testrunnerproc, TestRunnerResult) + LSP.JSON3.read(testrunnerproc, TestRunnerResult) catch err @error "Error from testrunner process" err show_error_message(server, """ @@ -648,7 +648,7 @@ function _testrunner_run_testcase( end result = try - JSONRPC.JSON3.read(testrunnerproc, TestRunnerResult) + LSP.JSON3.read(testrunnerproc, TestRunnerResult) catch err @error "Error from testrunner process" err show_error_message(server, """ diff --git a/src/types.jl b/src/types.jl index 6adecfb08..3d6467492 100644 --- a/src/types.jl +++ b/src/types.jl @@ -529,4 +529,4 @@ struct Server{Callback} ServerState()) end end -Server() = Server(Returns(nothing), LSEndpoint(IOBuffer(), IOBuffer())) # used for tests +Server() = Server(Returns(nothing), Endpoint(IOBuffer(), IOBuffer())) # used for tests diff --git a/src/utils/lsp.jl b/src/utils/lsp.jl index 20a232766..8b0778ab8 100644 --- a/src/utils/lsp.jl +++ b/src/utils/lsp.jl @@ -183,12 +183,12 @@ function show_debug_message(server::Server, message::AbstractString) end """ - handle_response_error(server::Server, msg::Dict{Symbol,Any}, context::String) + handle_response_error(server::Server, msg::Dict{Symbol,Any}, context::AbstractString) Common error handling for response messages. Checks for error field and shows appropriate message. Returns true if an error was handled, false otherwise. """ -function handle_response_error(server::Server, msg::Dict{Symbol,Any}, context::String) +function handle_response_error(server::Server, msg::Dict{Symbol,Any}, context::AbstractString) if haskey(msg, :error) error_msg = get(msg[:error], "message", "Unknown error") show_error_message(server, "Failed to $context: $error_msg") diff --git a/src/utils/server.jl b/src/utils/server.jl index e70df6558..7d742eb94 100644 --- a/src/utils/server.jl +++ b/src/utils/server.jl @@ -3,7 +3,7 @@ const DEFAULT_FLUSH_INTERVAL = 0.05 function yield_to_endpoint(interval=DEFAULT_FLUSH_INTERVAL) - # HACK: allow JSONRPC endpoint to process queued messages (e.g. work done progress report) + # HACK: allow LSP endpoint to process queued messages (e.g. work done progress report) yield() sleep(interval) end @@ -17,7 +17,7 @@ This function is used by each handler that processes messages sent from the clie as well as for sending requests and notifications from the server to the client. """ function send(server::Server, @nospecialize msg) - JSONRPC.send(server.endpoint, msg) + LSP.Communication.send(server.endpoint, msg) server.callback !== nothing && server.callback(:sent, msg) nothing end @@ -39,11 +39,6 @@ a `RequestCaller` subtype that encapsulates the context needed to handle the res - `caller::RequestCaller`: An instance of a `RequestCaller` subtype containing the context information needed to handle the client's response -# Usage -The function supports both syntaxes for convenience: -- `addrequest!(server, id=>caller)` - Using Pair syntax -- `addrequest!(server, id, caller)` - Using separate arguments - # Example ```julia # When creating a progress token @@ -81,11 +76,6 @@ The caller is removed from the tracking dictionary after retrieval. # Returns - The `RequestCaller` instance if found, or `nothing` if no matching request exists -# Usage -This function is primarily used in `handle_ResponseMessage` to retrieve the appropriate -handler context when processing client responses. The retrieved `RequestCaller` is then -dispatched to the appropriate handler based on its type. - # Example ```julia # In handle_ResponseMessage diff --git a/test/setup.jl b/test/setup.jl index d1526369a..3c9faa14c 100644 --- a/test/setup.jl +++ b/test/setup.jl @@ -3,7 +3,6 @@ using Pkg using JETLS using JETLS.LSP using JETLS.URIs2 -using JETLS: JSONRPC using JETLS: get_text_and_positions @@ -26,7 +25,7 @@ function withserver(f; out = Base.BufferStream() received_queue = Channel{Any}(Inf) sent_queue = Channel{Any}(Inf) - server = Server(LSEndpoint(in, out)) do s::Symbol, x + server = Server(Endpoint(in, out)) do s::Symbol, x @nospecialize x if s === :received put!(received_queue, x) @@ -77,19 +76,19 @@ function withserver(f; """ function writereadmsg(@nospecialize(msg); read::Int=1) @assert read ≥ 0 "`read::Int` must not be negative" - JSONRPC.writemsg(in, msg) + LSP.writelsp(in, msg) raw_msg = take_with_timeout!(received_queue) raw_res = json_res = nothing if read == 0 elseif read == 1 raw_res = take_with_timeout!(sent_queue) - json_res = JSONRPC.readmsg(out, method_dispatcher) + json_res = LSP.readlsp(out) else raw_res = Any[] json_res = Any[] for _ = 1:read push!(raw_res, take_with_timeout!(sent_queue)) - push!(json_res, JSONRPC.readmsg(out, method_dispatcher)) + push!(json_res, LSP.readlsp(out)) end end @test isempty(received_queue) && isempty(sent_queue) @@ -119,13 +118,13 @@ function withserver(f; if read == 0 elseif read == 1 raw_msg = take_with_timeout!(sent_queue) - json_msg = JSONRPC.readmsg(out, method_dispatcher) + json_msg = LSP.readlsp(out) else raw_msg = Any[] json_msg = Any[] for _ = 1:read push!(raw_msg, take_with_timeout!(sent_queue)) - push!(json_msg, JSONRPC.readmsg(out, method_dispatcher)) + push!(json_msg, LSP.readlsp(out)) end end @test isempty(received_queue) && isempty(sent_queue) @@ -168,7 +167,7 @@ function withserver(f; end writereadmsg(ExitNotification(); read=0) result = fetch(t) - @test result isa @NamedTuple{exit_code::Int, endpoint::JETLS.JSONRPC.Endpoint} + @test result isa @NamedTuple{exit_code::Int, endpoint::LSP.Endpoint} @test result.exit_code == 0 @test !result.endpoint.isopen finally diff --git a/test/test_full_lifecycle.jl b/test/test_full_lifecycle.jl index 9f45a0763..6f6db2141 100644 --- a/test/test_full_lifecycle.jl +++ b/test/test_full_lifecycle.jl @@ -21,7 +21,7 @@ let (pkgcode, positions) = JETLS.get_text_and_positions(""" withpackage("TestFullLifecycle", pkgcode) do pkgpath filepath = normpath(pkgpath, "src", "TestFullLifecycle.jl") - uri = string(JETLS.URIs2.filepath2uri(filepath)) + uri = LSP.URIs2.filepath2uri(filepath) test_full_cycle = function ((; server, writereadmsg, id_counter),) # open the file, and fill in the file cache @@ -138,7 +138,7 @@ let (pkgcode, positions) = JETLS.get_text_and_positions(""" @test length(server.state.currently_handled) == 0 end - rootUri = JETLS.URIs2.filepath2uri(pkgpath) + rootUri = LSP.URIs2.filepath2uri(pkgpath) # test clients that give workspaceFolders let workspaceFolders = [WorkspaceFolder(; uri=rootUri, name="TestFullLifecycle")]