Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 2 additions & 2 deletions .devcontainer/Dockerfile
Original file line number Diff line number Diff line change
Expand Up @@ -3,8 +3,8 @@ FROM mcr.microsoft.com/vscode/devcontainers/dotnet:0-6.0-focal
# Install other dotnet versions (required for adr tool and dotnet suggest)
RUN curl -sSL https://dot.net/v1/dotnet-install.sh > /tmp/dotnet-install.sh \
&& chmod u+x /tmp/dotnet-install.sh
RUN sudo bash -s -- --install-dir /usr/share/dotnet --channel 3.1 --runtime dotnet
RUN sudo bash -s -- --install-dir /usr/share/dotnet --channel 3.1 --runtime dotnet
RUN sudo /tmp/dotnet-install.sh --install-dir /usr/share/dotnet --channel 5.0 --runtime dotnet
RUN sudo /tmp/dotnet-install.sh --install-dir /usr/share/dotnet --channel 3.1 --runtime dotnet

# [Choice] Node.js version: none, lts/*, 16, 14, 12, 10
ARG NODE_VERSION="lts/*"
Expand Down
2 changes: 1 addition & 1 deletion .github/workflows/language-server-build.yml
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,7 @@ jobs:
working-directory: ./src
- name: Build and Test Language Server
run: |
dotnet run --project ./src/Ubictionary.LanguageServer.Tests/Ubictionary.LanguageServer.Tests.fsproj -- --nunit-summary TestResults-${{ matrix.dotnet-version }}.xml
dotnet run --project ./src/Ubictionary.LanguageServer.Tests/Ubictionary.LanguageServer.Tests.fsproj -- --fail-on-focused-tests --nunit-summary TestResults-${{ matrix.dotnet-version }}.xml
- name: Upload dotnet test results
uses: actions/upload-artifact@v2
with:
Expand Down
14 changes: 14 additions & 0 deletions .vscode/tasks.json
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,20 @@
},
"group": "build",
"problemMatcher": "$msCompile"
},
{
"label": "watch tests",
"command": "dotnet",
"type": "shell",
"args": [
"watch",
"run"
],
"options": {
"cwd": "${workspaceFolder}/src/Ubictionary.LanguageServer.Tests"
},
"group": "test",
"problemMatcher": "$msCompile"
}
]
}
31 changes: 31 additions & 0 deletions src/Ubictionary.LanguageServer.Tests/ConditionAwaiter.fs
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
module Ubictionary.LanguageServer.Tests.ConditionAwaiter

type WaitForCondition<'T> =
{
ReplyChannel: AsyncReplyChannel<'T>
Condition: 'T -> bool
}

type Message<'T> =
| Received of 'T
| WaitFor of WaitForCondition<'T>


let create<'T>() = MailboxProcessor.Start(fun inbox ->
let rec loop (conditions: WaitForCondition<'T> list) = async {
let! (msg: Message<'T>) = inbox.Receive()
let newState =
match msg with
| Received msg ->
conditions |> Seq.iter (fun c -> if c.Condition msg then c.ReplyChannel.Reply(msg))
conditions
| WaitFor waitFor -> waitFor :: conditions
return! loop newState
}
loop [])

let received (awaiter: MailboxProcessor<Message<'T>>) msg =
awaiter.Post(Received(msg))

let waitFor (awaiter: MailboxProcessor<Message<'T>>) condition timeout =
awaiter.PostAndTryAsyncReply((fun rc -> WaitFor({ReplyChannel=rc;Condition=condition})), timeout)
16 changes: 16 additions & 0 deletions src/Ubictionary.LanguageServer.Tests/ConfigurationSection.fs
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
module Ubictionary.LanguageServer.Tests.ConfigurationSection

open Newtonsoft.Json.Linq
open System.Collections.Generic
open OmniSharp.Extensions.LanguageServer.Protocol.Models

let fromMap values =
let results = new List<JToken>()
let configValue = JObject()
results.Add(configValue)
values |> Map.iter (fun k (v:string) ->
configValue.[k] <- JValue(v))
Container(results)

let includesSection section (configRequest:ConfigurationParams) =
configRequest.Items |> Seq.map (fun ci -> ci.Section) |> Seq.contains section
54 changes: 51 additions & 3 deletions src/Ubictionary.LanguageServer.Tests/InitializationTests.fs
Original file line number Diff line number Diff line change
@@ -1,12 +1,32 @@
module Ubictionary.LanguageServer.Tests.InitializationTests

open System
open System.Threading.Tasks
open Expecto
open Swensen.Unquote
open OmniSharp.Extensions.JsonRpc
open OmniSharp.Extensions.LanguageServer.Protocol.Models
open OmniSharp.Extensions.LanguageServer.Protocol.Client
open OmniSharp.Extensions.LanguageServer.Client

let private initTestClient = async {
let testClient = new TestClient()
return! testClient.Initialize(None) |> Async.AwaitTask
}

let initTestClient = async {
let private initTestClientWithConfig clientConfigBuilder = async {
let testClient = new TestClient()
return! testClient.Initialize() |> Async.AwaitTask
return! testClient.Initialize(Some clientConfigBuilder) |> Async.AwaitTask
}

let private handleConfigurationRequest section configValues =
let configSectionResult = ConfigurationSection.fromMap configValues
fun c ->
if ConfigurationSection.includesSection section c then
Task.FromResult(configSectionResult)
else
Task.FromResult(null)

[<Tests>]
let initializationTests =
testList "Initialization Tests" [
Expand All @@ -20,7 +40,35 @@ let initializationTests =
testAsync "Has Correct ServerInfo Name" {
use! client = initTestClient
test <@ client.ServerSettings.ServerInfo.Name = "Ubictionary" @>
}
}

testAsync "Server requests ubictionary file location configuration" {
let pathValue = Guid.NewGuid().ToString()

let configHandler = handleConfigurationRequest "ubictionary" (Map [("ubictionary_path", pathValue)])

let logAwaiter = ConditionAwaiter.create()
let logHandler (l:LogMessageParams) =
l.Message |> ConditionAwaiter.received logAwaiter
Task.CompletedTask

let clientOptionsBuilder (b:LanguageClientOptions) =
b.OnRequest("workspace/configuration", configHandler, JsonRpcHandlerOptions())
.OnNotification("window/logMessage", logHandler, JsonRpcHandlerOptions())
.WithCapability(Capabilities.DidChangeConfigurationCapability())
|> ignore

use! client = clientOptionsBuilder |> initTestClientWithConfig

test <@ client.ClientSettings.Capabilities.Workspace.Configuration.IsSupported @>
test <@ client.ClientSettings.Capabilities.Workspace.DidChangeConfiguration.IsSupported @>

let logCondition = fun (m:string) -> m.Contains("Loading ubictionary")
let! reply = ConditionAwaiter.waitFor logAwaiter logCondition 1500
test <@ match reply with
| Some replyMsg -> replyMsg.Contains(pathValue)
| None -> false @>
}

]

8 changes: 6 additions & 2 deletions src/Ubictionary.LanguageServer.Tests/TestClient.fs
Original file line number Diff line number Diff line change
@@ -1,7 +1,9 @@
namespace Ubictionary.LanguageServer.Tests

open System
open System.IO.Pipelines
open OmniSharp.Extensions.LanguageProtocol.Testing
open OmniSharp.Extensions.LanguageServer.Client
open OmniSharp.Extensions.JsonRpc.Testing
open Ubictionary.LanguageServer.Server

Expand All @@ -16,5 +18,7 @@ type TestClient() =
|> Async.Start
(clientPipe.Reader.AsStream(), serverPipe.Writer.AsStream())

member _.Initialize() =
base.InitializeClient(null)
member _.Initialize clientOptsBuilder =
match clientOptsBuilder with
| Some f -> base.InitializeClient(Action<LanguageClientOptions>(f))
| None -> base.InitializeClient(null)
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,8 @@
<GenerateProgramFile>false</GenerateProgramFile>
</PropertyGroup>
<ItemGroup>
<Compile Include="ConfigurationSection.fs" />
<Compile Include="ConditionAwaiter.fs" />
<Compile Include="TestClient.fs" />
<Compile Include="InitializationTests.fs" />
<Compile Include="Program.fs" />
Expand Down
2 changes: 1 addition & 1 deletion src/Ubictionary.LanguageServer/Program.fs
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ let setupLogging =
Log.Logger <- LoggerConfiguration()
.MinimumLevel.Verbose()
.Enrich.FromLogContext()
.WriteTo.File("log.txt")
.WriteTo.File("log.txt", rollingInterval = RollingInterval.Day)
.CreateLogger();

let private startWithConsole =
Expand Down
43 changes: 23 additions & 20 deletions src/Ubictionary.LanguageServer/Server.fs
Original file line number Diff line number Diff line change
Expand Up @@ -4,35 +4,38 @@ open System.Threading.Tasks
open OmniSharp.Extensions.LanguageServer.Server
open OmniSharp.Extensions.LanguageServer.Protocol.Server
open OmniSharp.Extensions.LanguageServer.Protocol.Models
open OmniSharp.Extensions.LanguageServer.Protocol.Window
open Microsoft.Extensions.Configuration
open Microsoft.Extensions.Logging
open Microsoft.Extensions.DependencyInjection
open Serilog
open System.IO
open Microsoft.Extensions.DependencyInjection
open Microsoft.Extensions.Logging

let private onInitializeEvent msg server request response _cancellationToken =
Log.Logger.Information $"Ubictionary: {msg} {server} {request} {response}"
Task.CompletedTask
let private onInitialize s r = onInitializeEvent "Initializing" s r None
let private onInitialized = onInitializeEvent "Initialized"
let private onStarted s = onInitializeEvent "Started" s None None
let configSection = "ubictionary"

let getConfig section key (config:IConfiguration) =
config.GetSection("ubictionary").Item("ubictionary_path")

let private requestConfig (s:ILanguageServer) _cancellationToken =
async {
Log.Logger.Information "Getting config..."
let! config =
s.Configuration.GetConfiguration(ConfigurationItem(Section = configSection))
|> Async.AwaitTask
let path = config |> getConfig configSection "ubictionary_path"
Log.Logger.Information $"Got path {path}"
s.Window.LogInfo $"Loading ubictionary from {path}"
} |> Async.StartAsTask :> Task

let configureServer (input: Stream) (output: Stream) (opts:LanguageServerOptions) =
opts
.WithInput(input)
.WithOutput(output)
.ConfigureLogging(fun b ->
b.AddLanguageProtocolLogging()
.AddSerilog(Log.Logger)
.SetMinimumLevel(LogLevel.Trace)
|> ignore)
.OnInitialize(OnLanguageServerInitializeDelegate(onInitialize))
.OnInitialized(OnLanguageServerInitializedDelegate(onInitialized))
.OnStarted(OnLanguageServerStartedDelegate(onStarted))
.OnStarted(OnLanguageServerStartedDelegate(requestConfig))
//.WithConfigurationSection(configSection) // Add back in when implementing didConfigurationChanged handling
.ConfigureLogging(fun z -> z.AddLanguageProtocolLogging().AddSerilog(Log.Logger).SetMinimumLevel(LogLevel.Trace) |> ignore)
// .WithServices(fun s -> s.AddLogging(fun b -> b.SetMinimumLevel(LogLevel.Trace) |> ignore) |> ignore)
.WithServerInfo(ServerInfo(Name = "Ubictionary"))
.WithServices(fun x ->
x.AddLogging(fun b ->
b.SetMinimumLevel(LogLevel.Trace) |> ignore)
|> ignore)
|> ignore


Expand Down