Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
41 commits
Select commit Hold shift + click to select a range
af89350
chore(Lsp.Workspace): add LspWorkspaceFolder and LspWorkspace types, …
razzmatazz Oct 29, 2025
b1244d6
chore(Lsp.Workspace): move ServerState.Solution and .RootPath under L…
razzmatazz Oct 29, 2025
b984564
chore(Lsp.Workspace): move ServerState.DecompiledMetadata under LspWo…
razzmatazz Oct 29, 2025
6d25800
chore(ReferenceTests): fix flaky testReferenceWorksToAspNetRazorPageR…
razzmatazz Oct 29, 2025
8acb688
chore(Lsp.Workspace): move GetDocumentForUriOfType to Lsp.Workspace a…
razzmatazz Oct 29, 2025
a01cff4
chore(ServerState): drop member.GetDocument()
razzmatazz Oct 29, 2025
642770f
chore(ServerState): drop ServerRequestContext.GetUserDocument()
razzmatazz Oct 29, 2025
92be68a
chore(Lsp.Workspace): add workspaceDocumentDetails
razzmatazz Oct 29, 2025
bc6b76d
chore(ServerState): drop unused ServerStateEvent.GetDocumentOfTypeForUri
razzmatazz Oct 30, 2025
f63818a
chore(ServerState): rename some of the events in ServerStateEvent
razzmatazz Oct 30, 2025
dc1635b
chore(Lsp.Workspace): add LspWorkspace.OpenDocs (move from ServerStat…
razzmatazz Oct 30, 2025
da7e5af
chore(Lsp.Workspace): add workspaceDocumentVersion, drop ServerReques…
razzmatazz Oct 30, 2025
b684689
chore(Lsp.Workspace): drop LspWorkspace.RootPath
razzmatazz Oct 30, 2025
053dc46
feat(Initialization): actually process InitializeParams.WorkspaceFold…
razzmatazz Oct 30, 2025
9857348
chore(ServerStateEvent): sort events alphabetically
razzmatazz Oct 30, 2025
2bb83f6
chore(Lsp.Workspace): add stub fn 'workspaceFolder'
razzmatazz Oct 30, 2025
9ebb967
chore(ServerRequestContext): drop ServerRequestContext.FindReferences()
razzmatazz Oct 30, 2025
963a356
chore(ServerRequestContext): drop member FindImplementations()
razzmatazz Oct 30, 2025
c8344aa
chore(Handlers): use ServerSettings.GetEffectiveFormattingOptions thr…
razzmatazz Nov 3, 2025
fe12296
chore(Lsp.Workspace): import workspaceFolderResolveSymbolLocation fro…
razzmatazz Nov 3, 2025
4ee8ff1
chore(ServerRequestContext): drop FindSymbols(), move the code to Han…
razzmatazz Nov 3, 2025
a61be40
chore(ServerRequestContext): drop EmitMany()
razzmatazz Nov 3, 2025
3721825
chore(ServerRequestContext): drop FindCallers(), move code to Handler…
razzmatazz Nov 3, 2025
adb3b03
chore(ServerRequestContext): move FindImplementions & FindDerived* me…
razzmatazz Nov 3, 2025
76a804d
chore(ServerState): drop the WorkspaceFolderSolutionChanged event, us…
razzmatazz Nov 3, 2025
356aa38
chore(ServerRequestContext): drop ServerRequestContext.Solution, use …
razzmatazz Nov 3, 2025
533b333
chore(ServerRequestContext): retire ResolveSymbolLocations()
razzmatazz Nov 3, 2025
71ae183
chore(Lsp.Workspace): drop LspWorkspace.Solution
razzmatazz Nov 3, 2025
1d185e6
chore(Util): drop flip fn
razzmatazz Nov 3, 2025
fa3739a
chore(Util): drop curry, uncurry
razzmatazz Nov 3, 2025
ce3d29e
chore(SignatureHelpTests): add
razzmatazz Nov 4, 2025
e2c610c
chore(TypeDefinitionTests): add
razzmatazz Nov 4, 2025
922bd5b
chore(Handlers.TypeDefinition): do not use ServerRequestContext.Resol…
razzmatazz Nov 4, 2025
79beade
chore(ImplementationTests): add (non-functional) tests for textDocume…
razzmatazz Nov 4, 2025
284d616
chore(ServerRequestContext): drop ServerRequestContext.FindSymbol
razzmatazz Nov 4, 2025
4e40fe9
chore(ServerRequestContext): drop ServerRequestContext.WindowShowMess…
razzmatazz Nov 4, 2025
f5f3015
chore(Handlers.Implementation): drop use of ServerRequestContext.Reso…
razzmatazz Nov 4, 2025
c03060c
chore(Handlers.CallHierarchy): drop the use of ServerRequestContext.R…
razzmatazz Nov 4, 2025
0e05598
chore(Handlers.TypeHierarchy: drop the use of ServerRequestContext.Re…
razzmatazz Nov 4, 2025
19af23c
chore(ServerRequestContext): drop ServerRequestContext.RequestId
razzmatazz Nov 4, 2025
cec00ec
chore(Lsp.Workspace): make `project` arg non-optional for workspaceFo…
razzmatazz Nov 4, 2025
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
1 change: 1 addition & 0 deletions src/CSharpLanguageServer/CSharpLanguageServer.fsproj
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,7 @@
<Compile Include="DocumentationUtil.fs" />
<Compile Include="Diagnostics.fs" />
<Compile Include="Lsp/Client.fs" />
<Compile Include="Lsp/Workspace.fs" />
<Compile Include="State/ServerState.fs" />
<Compile Include="State/ServerRequestContext.fs" />
<Compile Include="Handlers/CSharpMetadata.fs" />
Expand Down
19 changes: 9 additions & 10 deletions src/CSharpLanguageServer/Handlers/CSharpMetadata.fs
Original file line number Diff line number Diff line change
Expand Up @@ -5,20 +5,19 @@ open Ionide.LanguageServerProtocol.JsonRpc

open CSharpLanguageServer.Types
open CSharpLanguageServer.State
open CSharpLanguageServer.Lsp.Workspace

[<RequireQualifiedAccess>]
module CSharpMetadata =
let handle
(context: ServerRequestContext)
(metadataParams: CSharpMetadataParams)
(p: CSharpMetadataParams)
: AsyncLspResult<CSharpMetadataResponse option> =
async {
let uri = metadataParams.TextDocument.Uri

let metadataMaybe =
context.DecompiledMetadata
|> Map.tryFind uri
|> Option.map (fun x -> x.Metadata)

return metadataMaybe |> LspResult.success
}
p.TextDocument.Uri
|> workspaceFolder context.Workspace
|> Option.map _.DecompiledMetadata
|> Option.bind (Map.tryFind p.TextDocument.Uri)
|> Option.map _.Metadata
|> LspResult.success
|> async.Return
39 changes: 27 additions & 12 deletions src/CSharpLanguageServer/Handlers/CallHierarchy.fs
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,9 @@ open Ionide.LanguageServerProtocol.Types
open Ionide.LanguageServerProtocol.JsonRpc

open CSharpLanguageServer.State
open CSharpLanguageServer.State.ServerState
open CSharpLanguageServer.Roslyn.Conversions
open CSharpLanguageServer.Lsp.Workspace

[<RequireQualifiedAccess>]
module CallHierarchy =
Expand All @@ -18,20 +20,27 @@ module CallHierarchy =
Microsoft.CodeAnalysis.SymbolKind.Event
Microsoft.CodeAnalysis.SymbolKind.Property ]

let provider
(clientCapabilities: ClientCapabilities)
: U3<bool, CallHierarchyOptions, CallHierarchyRegistrationOptions> option =
let provider (_cc: ClientCapabilities) : U3<bool, CallHierarchyOptions, CallHierarchyRegistrationOptions> option =
Some(U3.C1 true)

let prepare
(context: ServerRequestContext)
(p: CallHierarchyPrepareParams)
: AsyncLspResult<CallHierarchyItem[] option> =
async {
match! context.FindSymbol p.TextDocument.Uri p.Position with
| Some symbol when isCallableSymbol symbol ->
let! itemList = CallHierarchyItem.fromSymbol context.ResolveSymbolLocations symbol
return itemList |> List.toArray |> Some |> LspResult.success
match! workspaceDocumentSymbol context.Workspace AnyDocument p.TextDocument.Uri p.Position with
| Some wf, Some(symbol, project, _) when isCallableSymbol symbol ->
let! locations, updatedWf = workspaceFolderSymbolLocations symbol project wf

context.Emit(WorkspaceFolderChange updatedWf)

return
locations
|> Seq.map (CallHierarchyItem.fromSymbolAndLocation symbol)
|> Seq.toArray
|> Some
|> LspResult.success

| _ -> return None |> LspResult.success
}

Expand All @@ -40,6 +49,8 @@ module CallHierarchy =
(p: CallHierarchyIncomingCallsParams)
: AsyncLspResult<CallHierarchyIncomingCall[] option> =
async {
let! ct = Async.CancellationToken

let toCallHierarchyIncomingCalls (info: SymbolCallerInfo) : CallHierarchyIncomingCall seq =
let fromRanges =
info.Locations
Expand All @@ -49,13 +60,15 @@ module CallHierarchy =
info.CallingSymbol.Locations
|> Seq.choose Location.fromRoslynLocation
|> Seq.map (fun loc ->
{ From = CallHierarchyItem.fromSymbolAndLocation (info.CallingSymbol) loc
{ From = CallHierarchyItem.fromSymbolAndLocation info.CallingSymbol loc
FromRanges = fromRanges })

match! context.FindSymbol p.Item.Uri p.Item.Range.Start with
| None -> return None |> LspResult.success
| Some symbol ->
let! callers = context.FindCallers symbol
match! workspaceDocumentSymbol context.Workspace AnyDocument p.Item.Uri p.Item.Range.Start with
| Some wf, Some(symbol, _, _) ->
let! callers =
SymbolFinder.FindCallersAsync(symbol, wf.Solution.Value, cancellationToken = ct)
|> Async.AwaitTask

// TODO: If we remove info.IsDirect, then we will get lots of false positive. But if we keep it,
// we will miss many callers. Maybe it should have some change in LSP protocol.
return
Expand All @@ -66,6 +79,8 @@ module CallHierarchy =
|> Seq.toArray
|> Some
|> LspResult.success

| _, _ -> return None |> LspResult.success
}

let outgoingCalls
Expand Down
20 changes: 16 additions & 4 deletions src/CSharpLanguageServer/Handlers/CodeAction.fs
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@ open CSharpLanguageServer.Logging
open CSharpLanguageServer.Roslyn.Conversions
open CSharpLanguageServer.State
open CSharpLanguageServer.Util
open CSharpLanguageServer.Lsp.Workspace


type CSharpCodeActionResolutionData =
Expand Down Expand Up @@ -330,7 +331,10 @@ module CodeAction =
(p: CodeActionParams)
: AsyncLspResult<TextDocumentCodeActionResult option> =
async {
match context.GetDocument p.TextDocument.Uri with
let wf, docForUri =
p.TextDocument.Uri |> workspaceDocument context.Workspace AnyDocument

match docForUri with
| None -> return None |> LspResult.success
| Some doc ->
let! ct = Async.CancellationToken
Expand Down Expand Up @@ -376,7 +380,7 @@ module CodeAction =
let! maybeLspCa =
roslynCodeActionToResolvedLspCodeAction
doc.Project.Solution
context.GetDocumentVersion
(Uri.unescape >> workspaceDocumentVersion context.Workspace)
doc
ct
ca
Expand All @@ -400,7 +404,11 @@ module CodeAction =
let resolutionData =
p.Data |> Option.map deserialize<CSharpCodeActionResolutionData>

match context.GetDocument resolutionData.Value.TextDocumentUri with
let wf, docForUri =
resolutionData.Value.TextDocumentUri
|> workspaceDocument context.Workspace AnyDocument

match docForUri with
| None -> return raise (Exception(sprintf "no document for uri %s" resolutionData.Value.TextDocumentUri))
| Some doc ->
let! ct = Async.CancellationToken
Expand All @@ -414,7 +422,11 @@ module CodeAction =
roslynCodeActions |> Seq.tryFind (fun ca -> ca.Title = p.Title)

let toResolvedLspCodeAction =
roslynCodeActionToResolvedLspCodeAction doc.Project.Solution context.GetDocumentVersion doc ct
roslynCodeActionToResolvedLspCodeAction
doc.Project.Solution
(Uri.unescape >> workspaceDocumentVersion context.Workspace)
doc
ct

let! lspCodeAction =
match selectedCodeAction with
Expand Down
24 changes: 17 additions & 7 deletions src/CSharpLanguageServer/Handlers/CodeLens.fs
Original file line number Diff line number Diff line change
Expand Up @@ -4,12 +4,14 @@ open Microsoft.CodeAnalysis
open Microsoft.CodeAnalysis.CSharp
open Microsoft.CodeAnalysis.CSharp.Syntax
open Microsoft.CodeAnalysis.Text
open Microsoft.CodeAnalysis.FindSymbols
open Ionide.LanguageServerProtocol.Server
open Ionide.LanguageServerProtocol.Types
open Ionide.LanguageServerProtocol.JsonRpc

open CSharpLanguageServer.State
open CSharpLanguageServer.Roslyn.Conversions
open CSharpLanguageServer.Lsp.Workspace

type private DocumentSymbolCollectorForCodeLens(semanticModel: SemanticModel) =
inherit CSharpSyntaxWalker(SyntaxWalkerDepth.Token)
Expand Down Expand Up @@ -89,9 +91,10 @@ module CodeLens =
WorkDoneProgress = None }

let handle (context: ServerRequestContext) (p: CodeLensParams) : AsyncLspResult<CodeLens[] option> = async {
let docMaybe = context.GetDocument p.TextDocument.Uri
let wf, docForUri =
p.TextDocument.Uri |> workspaceDocument context.Workspace AnyDocument

match docMaybe with
match docForUri with
| None -> return None |> LspResult.success
| Some doc ->
let! ct = Async.CancellationToken
Expand Down Expand Up @@ -120,21 +123,26 @@ module CodeLens =
}

let resolve (context: ServerRequestContext) (p: CodeLens) : AsyncLspResult<CodeLens> = async {
let! ct = Async.CancellationToken

let lensData: CodeLensData =
p.Data
|> Option.map _.ToObject<CodeLensData>()
|> Option.bind Option.ofObj
|> Option.defaultValue CodeLensData.Default

match! context.FindSymbol lensData.DocumentUri lensData.Position with
| None -> return p |> LspResult.success
| Some symbol ->
let! locations = context.FindReferences symbol false
match! workspaceDocumentSymbol context.Workspace AnyDocument lensData.DocumentUri lensData.Position with
| Some wf, Some(symbol, _, _) ->
let! refs =
SymbolFinder.FindReferencesAsync(symbol, wf.Solution.Value, cancellationToken = ct)
|> Async.AwaitTask

// FIXME: refNum is wrong. There are lots of false positive even if we distinct locations by
// (l.SourceTree.FilePath, l.SourceSpan)
let refNum =
locations
refs
|> Seq.collect _.Locations
|> Seq.map _.Location
|> Seq.distinctBy (fun l -> (l.GetMappedLineSpan().Path, l.SourceSpan))
|> Seq.length

Expand All @@ -153,4 +161,6 @@ module CodeLens =
Arguments = Some [| arg |> serialize |] }

return { p with Command = Some command } |> LspResult.success

| _, _ -> return p |> LspResult.success
}
6 changes: 5 additions & 1 deletion src/CSharpLanguageServer/Handlers/Completion.fs
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ open CSharpLanguageServer.State
open CSharpLanguageServer.Util
open CSharpLanguageServer.Roslyn.Conversions
open CSharpLanguageServer.Logging
open CSharpLanguageServer.Lsp.Workspace

[<RequireQualifiedAccess>]
module Completion =
Expand Down Expand Up @@ -185,7 +186,10 @@ module Completion =
(p: CompletionParams)
: Async<LspResult<U2<CompletionItem array, CompletionList> option>> =
async {
match context.GetDocument p.TextDocument.Uri with
let wf, docForUri =
p.TextDocument.Uri |> workspaceDocument context.Workspace AnyDocument

match docForUri with
| None -> return None |> LspResult.success
| Some doc ->
let! ct = Async.CancellationToken
Expand Down
14 changes: 10 additions & 4 deletions src/CSharpLanguageServer/Handlers/Definition.fs
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,8 @@ open Ionide.LanguageServerProtocol.Types
open Ionide.LanguageServerProtocol.JsonRpc

open CSharpLanguageServer.State
open CSharpLanguageServer.State.ServerState
open CSharpLanguageServer.Lsp.Workspace

[<RequireQualifiedAccess>]
module Definition =
Expand All @@ -14,9 +16,13 @@ module Definition =
(p: DefinitionParams)
: Async<LspResult<U2<Definition, DefinitionLink array> option>> =
async {
match! context.FindSymbol' p.TextDocument.Uri p.Position with
| None -> return None |> LspResult.success
| Some(symbol, project, _) ->
let! locations = context.ResolveSymbolLocations symbol (Some project)
match! workspaceDocumentSymbol context.Workspace AnyDocument p.TextDocument.Uri p.Position with
| Some wf, Some(symbol, project, _) ->
let! locations, updatedWf = workspaceFolderSymbolLocations symbol project wf

context.Emit(WorkspaceFolderChange updatedWf)

return locations |> Array.ofList |> Definition.C2 |> U2.C1 |> Some |> LspResult.success

| _, _ -> return None |> LspResult.success
}
9 changes: 7 additions & 2 deletions src/CSharpLanguageServer/Handlers/Diagnostic.fs
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,8 @@ open CSharpLanguageServer.Roslyn.Conversions
open CSharpLanguageServer.State
open CSharpLanguageServer.Types
open CSharpLanguageServer.Util
open CSharpLanguageServer.Lsp.Workspace


[<RequireQualifiedAccess>]
module Diagnostic =
Expand Down Expand Up @@ -36,7 +38,10 @@ module Diagnostic =
Items = [||]
RelatedDocuments = None }

match context.GetDocument p.TextDocument.Uri with
let wf, docForUri =
p.TextDocument.Uri |> workspaceDocument context.Workspace AnyDocument

match docForUri with
| None -> return emptyReport |> U2.C1 |> LspResult.success

| Some doc ->
Expand Down Expand Up @@ -107,7 +112,7 @@ module Diagnostic =
let emptyWorkspaceDiagnosticReport: WorkspaceDiagnosticReport =
{ Items = Array.empty }

match context.State.Solution, p.PartialResultToken with
match context.Workspace.SingletonFolder.Solution, p.PartialResultToken with
| None, _ -> return emptyWorkspaceDiagnosticReport |> LspResult.success

| Some solution, None ->
Expand Down
15 changes: 8 additions & 7 deletions src/CSharpLanguageServer/Handlers/DocumentFormatting.fs
Original file line number Diff line number Diff line change
Expand Up @@ -7,9 +7,10 @@ open Ionide.LanguageServerProtocol.JsonRpc
open CSharpLanguageServer.State
open CSharpLanguageServer.Util
open CSharpLanguageServer.Roslyn.Document
open CSharpLanguageServer.Lsp.Workspace

[<RequireQualifiedAccess>]

[<RequireQualifiedAccess>]
module DocumentFormatting =
let provider (_cc: ClientCapabilities) : U2<bool, DocumentFormattingOptions> option = Some(U2.C1 true)

Expand All @@ -22,12 +23,12 @@ module DocumentFormatting =
}

let handle (context: ServerRequestContext) (p: DocumentFormattingParams) : AsyncLspResult<TextEdit[] option> =
let formatDocument =
p.Options
|> context.State.Settings.GetEffectiveFormattingOptions
|> formatDocument
let lspFormattingOptions =
p.Options |> context.State.Settings.GetEffectiveFormattingOptions

let wf, doc = p.TextDocument.Uri |> workspaceDocument context.Workspace UserDocument

context.GetUserDocument p.TextDocument.Uri
doc
|> async.Return
|> Async.bindOption formatDocument
|> Async.bindOption (formatDocument lspFormattingOptions)
|> Async.map LspResult.success
9 changes: 5 additions & 4 deletions src/CSharpLanguageServer/Handlers/DocumentHighlight.fs
Original file line number Diff line number Diff line change
Expand Up @@ -10,10 +10,11 @@ open Ionide.LanguageServerProtocol.JsonRpc
open CSharpLanguageServer.State
open CSharpLanguageServer.Roslyn.Conversions
open CSharpLanguageServer.Util
open CSharpLanguageServer.Lsp.Workspace

[<RequireQualifiedAccess>]
module DocumentHighlight =
let provider (_: ClientCapabilities) : U2<bool, DocumentHighlightOptions> option = Some(U2.C1 true)
let provider (_cc: ClientCapabilities) : U2<bool, DocumentHighlightOptions> option = Some(U2.C1 true)

let private shouldHighlight (symbol: ISymbol) =
match symbol with
Expand Down Expand Up @@ -56,13 +57,13 @@ module DocumentHighlight =
Kind = Some DocumentHighlightKind.Read })
}

match! context.FindSymbol' p.TextDocument.Uri p.Position with
| Some(symbol, _, Some doc) ->
match! workspaceDocumentSymbol context.Workspace AnyDocument p.TextDocument.Uri p.Position with
| Some wf, Some(symbol, _, Some doc) ->
if shouldHighlight symbol then
let! highlights = getHighlights symbol doc
return highlights |> Seq.toArray |> Some |> LspResult.success
else
return None |> LspResult.success

| _ -> return None |> LspResult.success
| _, _ -> return None |> LspResult.success
}
Loading