Skip to content

Commit

Permalink
CSharpLanguageServer.Lsp.Server: import request handling from CSharpL…
Browse files Browse the repository at this point in the history
…anguageServer.Server
  • Loading branch information
razzmatazz committed Feb 20, 2024
1 parent 510f0e9 commit ae9230f
Show file tree
Hide file tree
Showing 7 changed files with 447 additions and 435 deletions.
4 changes: 2 additions & 2 deletions src/CSharpLanguageServer/CSharpLanguageServer.fsproj
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@
<Compile Include="RoslynHelpers.fs" />
<Compile Include="Documentation.fs" />
<Compile Include="Types.fs" />
<Compile Include="Lsp/Client.fs" />
<Compile Include="State.fs" />
<Compile Include="Handlers/CSharpMetadata.fs" />
<Compile Include="Handlers/CallHierarchy.fs" />
Expand All @@ -37,6 +38,7 @@
<Compile Include="Handlers/DocumentSymbol.fs" />
<Compile Include="Handlers/Hover.fs" />
<Compile Include="Handlers/Implementation.fs" />
<Compile Include="Handlers/Initialization.fs" />
<Compile Include="Handlers/InlayHint.fs" />
<Compile Include="Handlers/References.fs" />
<Compile Include="Handlers/Rename.fs" />
Expand All @@ -47,9 +49,7 @@
<Compile Include="Handlers/TypeHierarchy.fs" />
<Compile Include="Handlers/Workspace.fs" />
<Compile Include="Handlers/WorkspaceSymbol.fs" />
<Compile Include="Lsp/Client.fs" />
<Compile Include="Lsp/Server.fs" />
<Compile Include="Server.fs" />
<Compile Include="Options.fs" />
<Compile Include="Program.fs" />
</ItemGroup>
Expand Down
182 changes: 182 additions & 0 deletions src/CSharpLanguageServer/Handlers/Initialization.fs
Original file line number Diff line number Diff line change
@@ -0,0 +1,182 @@
namespace CSharpLanguageServer.Handlers

open System
open System.IO

open System.Reflection
open Microsoft.CodeAnalysis
open Microsoft.CodeAnalysis.Text
open Microsoft.CodeAnalysis.FindSymbols
open Microsoft.Build.Locator
open Ionide.LanguageServerProtocol
open Ionide.LanguageServerProtocol.Types
open Ionide.LanguageServerProtocol.Server

open CSharpLanguageServer
open CSharpLanguageServer.State
open CSharpLanguageServer.Types
open CSharpLanguageServer.Logging

[<RequireQualifiedAccess>]
module Initialization =
let private logger = LogProvider.getLoggerByName "Initialization"

let handleInitialize (setupTimer: unit -> unit)
(serverCapabilities: ServerCapabilities)
(scope: ServerRequestScope)
(p: InitializeParams)
: Async<LspResult<InitializeResult>> = async {
let serverName = "csharp-ls"
logger.info (
Log.setMessage "initializing, {name} version {version}"
>> Log.addContext "name" serverName
>> Log.addContext "version" (Assembly.GetExecutingAssembly().GetName().Version)
)
logger.info (
Log.setMessage "{name} is released under MIT license and is not affiliated with Microsoft Corp.; see https://github.com/razzmatazz/csharp-language-server"
>> Log.addContext "name" serverName
)

let vsInstanceQueryOpt = VisualStudioInstanceQueryOptions.Default
let vsInstanceList = MSBuildLocator.QueryVisualStudioInstances(vsInstanceQueryOpt)
if Seq.isEmpty vsInstanceList then
raise (InvalidOperationException("No instances of MSBuild could be detected." + Environment.NewLine + "Try calling RegisterInstance or RegisterMSBuildPath to manually register one."))

// do! infoMessage "MSBuildLocator instances found:"
//
// for vsInstance in vsInstanceList do
// do! infoMessage (sprintf "- SDK=\"%s\", Version=%s, MSBuildPath=\"%s\", DiscoveryType=%s"
// vsInstance.Name
// (string vsInstance.Version)
// vsInstance.MSBuildPath
// (string vsInstance.DiscoveryType))

let vsInstance = vsInstanceList |> Seq.head

logger.info(
Log.setMessage "MSBuildLocator: will register \"{vsInstanceName}\", Version={vsInstanceVersion} as default instance"
>> Log.addContext "vsInstanceName" vsInstance.Name
>> Log.addContext "vsInstanceVersion" (string vsInstance.Version)
)

MSBuildLocator.RegisterInstance(vsInstance)

scope.Emit(ClientCapabilityChange p.Capabilities)

// TODO use p.RootUri
let rootPath = Directory.GetCurrentDirectory()
scope.Emit(RootPathChange rootPath)

// setup timer so actors get period ticks
setupTimer()

// TODO: Report server info to client (name, version)
let initializeResult =
{ InitializeResult.Default with
Capabilities = serverCapabilities }

return initializeResult |> LspResult.success
}

let handleInitialized (lspClient: ILspClient)
(stateActor: MailboxProcessor<ServerStateEvent>)
(scope: ServerRequestScope)
(_p: InitializedParams)
: Async<LspResult<unit>> =
async {
logger.trace (
Log.setMessage "handleInitialized: \"initialized\" notification received from client"
)

//
// registering w/client for didChangeWatchedFiles notifications"
//
let clientSupportsWorkspaceDidChangeWatchedFilesDynamicReg =
scope.ClientCapabilities
|> Option.bind (fun x -> x.Workspace)
|> Option.bind (fun x -> x.DidChangeWatchedFiles)
|> Option.bind (fun x -> x.DynamicRegistration)
|> Option.defaultValue true

match clientSupportsWorkspaceDidChangeWatchedFilesDynamicReg with
| true ->
let fileChangeWatcher = { GlobPattern = U2.First "**/*.{cs,csproj,sln}"
Kind = None }

let didChangeWatchedFilesRegistration: Registration =
{ Id = "id:workspace/didChangeWatchedFiles"
Method = "workspace/didChangeWatchedFiles"
RegisterOptions = { Watchers = [| fileChangeWatcher |] } |> serialize |> Some
}

try
let! regResult =
lspClient.ClientRegisterCapability(
{ Registrations = [| didChangeWatchedFilesRegistration |] })

match regResult with
| Ok _ -> ()
| Error error ->
logger.warn(
Log.setMessage "handleInitialized: didChangeWatchedFiles registration has failed with {error}"
>> Log.addContext "error" (string error)
)
with
| ex ->
logger.warn(
Log.setMessage "handleInitialized: didChangeWatchedFiles registration has failed with {error}"
>> Log.addContext "error" (string ex)
)
| false -> ()

//
// retrieve csharp settings
//
try
let! workspaceCSharpConfig =
lspClient.WorkspaceConfiguration(
{ items=[| { Section=Some "csharp"; ScopeUri=None } |] })

let csharpConfigTokensMaybe =
match workspaceCSharpConfig with
| Ok ts -> Some ts
| _ -> None

let newSettingsMaybe =
match csharpConfigTokensMaybe with
| Some [| t |] ->
let csharpSettingsMaybe = t |> deserialize<ServerSettingsCSharpDto option>

match csharpSettingsMaybe with
| Some csharpSettings ->

match csharpSettings.solution with
| Some solutionPath-> Some { scope.State.Settings with SolutionPath = Some solutionPath }
| _ -> None

| _ -> None
| _ -> None

// do! logMessage (sprintf "handleInitialized: newSettingsMaybe=%s" (string newSettingsMaybe))

match newSettingsMaybe with
| Some newSettings ->
scope.Emit(SettingsChange newSettings)
| _ -> ()
with
| ex ->
logger.warn(
Log.setMessage "handleInitialized: could not retrieve `csharp` workspace configuration section: {error}"
>> Log.addContext "error" (ex |> string)
)

//
// start loading the solution
//
stateActor.Post(SolutionReloadRequest (TimeSpan.FromMilliseconds(100)))

logger.trace(
Log.setMessage "handleInitialized: OK")

return LspResult.Ok()
}
12 changes: 10 additions & 2 deletions src/CSharpLanguageServer/Handlers/WorkspaceSymbol.fs
Original file line number Diff line number Diff line change
Expand Up @@ -16,12 +16,20 @@ module WorkspaceSymbol =
let provider (clientCapabilities: ClientCapabilities option) : U2<bool, WorkspaceSymbolOptions> option =
true |> U2.First |> Some

let handle (scope: ServerRequestScope) (symbolParams: WorkspaceSymbolParams): AsyncLspResult<SymbolInformation [] option> = async {
let handle (scope: ServerRequestScope)
(symbolParams: WorkspaceSymbolParams)
: AsyncLspResult<U2<SymbolInformation array,WorkspaceSymbol array> option> = async {

Check warning on line 21 in src/CSharpLanguageServer/Handlers/WorkspaceSymbol.fs

View workflow job for this annotation

GitHub Actions / build (windows-latest, 8.0.200)

This construct is deprecated. Use DocumentSymbol or WorkspaceSymbol instead.

Check warning on line 21 in src/CSharpLanguageServer/Handlers/WorkspaceSymbol.fs

View workflow job for this annotation

GitHub Actions / build (ubuntu-22.04, 8.0.200)

This construct is deprecated. Use DocumentSymbol or WorkspaceSymbol instead.
let pattern =
if String.IsNullOrEmpty(symbolParams.Query) then
None
else
Some symbolParams.Query
let! symbols = findSymbolsInSolution scope.Solution pattern (Some 20)
return symbols |> Array.ofSeq |> Some |> LspResult.success

return
symbols
|> Array.ofSeq
|> U2.First
|> Some
|> LspResult.success
}

0 comments on commit ae9230f

Please sign in to comment.