Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Integrate refactoring changes (pt. XI) #155

Merged
merged 12 commits into from
May 16, 2024
Merged
2 changes: 1 addition & 1 deletion .github/workflows/test.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ jobs:
strategy:
matrix:
os: [windows-latest, ubuntu-22.04]
dotnet: [8.0.200]
dotnet: [8.0.300]
fail-fast: false

runs-on: ${{ matrix.os }}
Expand Down
10 changes: 10 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,16 @@ All notable changes to this project will be documented in this file.
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/).

## [Unreleased]
* Stop collecting symbols in method body
- By Adam Tao @tcx4c70
* Fix TypeHierarchy registration
- By Adam Tao @tcx4c70
* Fix wrong references number
- By Adam Tao @tcx4c70
* Add progress reporting when loading a solution/project
- By Adam Tao @tcx4c70
* More refactoring and fixes from rework branch:
- By Adam Tao @tcx4c70, from https://github.com/tcx4c70/csharp-language-server/commits/rework/
* Bump Ionide.LanguageServerProtocol to 0.5.1, fix some of the types used for dynamic registration

## [0.13.0] - 2024-05-08 / Baltoji Vokė
Expand Down
2 changes: 1 addition & 1 deletion global.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"sdk": {
"version": "8.0.200",
"version": "8.0.300",
"rollForward": "minor"
}
}
3 changes: 2 additions & 1 deletion src/CSharpLanguageServer/CSharpLanguageServer.fsproj
Original file line number Diff line number Diff line change
Expand Up @@ -23,9 +23,10 @@
<Compile Include="Util.fs" />
<Compile Include="Conversions.fs" />
<Compile Include="FormatUtil.fs" />
<Compile Include="Types.fs" />
<Compile Include="ProgressReporter.fs" />
<Compile Include="RoslynHelpers.fs" />
<Compile Include="DocumentationUtil.fs" />
<Compile Include="Types.fs" />
<Compile Include="Lsp/Client.fs" />
<Compile Include="State/ServerState.fs" />
<Compile Include="State/ServerRequestContext.fs" />
Expand Down
1 change: 1 addition & 0 deletions src/CSharpLanguageServer/Handlers/CallHierarchy.fs
Original file line number Diff line number Diff line change
Expand Up @@ -84,6 +84,7 @@ module CallHierarchy =
callers
|> Seq.filter (fun info -> info.IsDirect && isCallableSymbol info.CallingSymbol)
|> Seq.collect toCallHierarchyIncomingCalls
|> Seq.distinct
|> Seq.toArray
|> Some
|> success
Expand Down
32 changes: 14 additions & 18 deletions src/CSharpLanguageServer/Handlers/CodeLens.fs
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,6 @@ type private DocumentSymbolCollectorForCodeLens(semanticModel: SemanticModel) =

override __.VisitEnumMemberDeclaration(node) =
collect node node.Identifier.Span
base.VisitEnumMemberDeclaration(node)

override __.VisitClassDeclaration(node) =
collect node node.Identifier.Span
Expand All @@ -53,35 +52,27 @@ type private DocumentSymbolCollectorForCodeLens(semanticModel: SemanticModel) =

override __.VisitDelegateDeclaration(node) =
collect node node.Identifier.Span
base.VisitDelegateDeclaration(node)

override __.VisitConstructorDeclaration(node) =
collect node node.Identifier.Span
base.VisitConstructorDeclaration(node)

override __.VisitDestructorDeclaration(node) =
collect node node.Identifier.Span
base.VisitDestructorDeclaration(node)

override __.VisitOperatorDeclaration(node) =
collect node node.OperatorToken.Span
base.VisitOperatorDeclaration(node)

override __.VisitIndexerDeclaration(node) =
collect node node.ThisKeyword.Span
base.VisitIndexerDeclaration(node)

override __.VisitConversionOperatorDeclaration(node) =
collect node node.Type.Span
base.VisitConversionOperatorDeclaration(node)

override __.VisitMethodDeclaration(node) =
collect node node.Identifier.Span
base.VisitMethodDeclaration(node)

override __.VisitPropertyDeclaration(node) =
collect node node.Identifier.Span
base.VisitPropertyDeclaration(node)

override __.VisitVariableDeclarator(node) =
let grandparent =
Expand All @@ -91,13 +82,9 @@ type private DocumentSymbolCollectorForCodeLens(semanticModel: SemanticModel) =
// Only show field variables and ignore local variables
if grandparent.IsSome && grandparent.Value :? FieldDeclarationSyntax then
collect node node.Identifier.Span
base.VisitVariableDeclarator(node)
else
base.VisitVariableDeclarator(node)

override __.VisitEventDeclaration(node) =
collect node node.Identifier.Span
base.VisitEventDeclaration(node)

[<RequireQualifiedAccess>]
module CodeLens =
Expand Down Expand Up @@ -144,7 +131,8 @@ module CodeLens =
let! docText = doc.GetTextAsync(ct) |> Async.AwaitTask

let collector = DocumentSymbolCollectorForCodeLens(semanticModel)
collector.Visit(syntaxTree.GetRoot())
let! root = syntaxTree.GetRootAsync(ct) |> Async.AwaitTask
collector.Visit(root)

let makeCodeLens (_symbol: ISymbol, nameSpan: TextSpan) : CodeLens =
let start = nameSpan.Start |> docText.Lines.GetLinePosition
Expand Down Expand Up @@ -176,14 +164,22 @@ module CodeLens =
let! refs = context.FindReferences symbol
// FIXME: refNum is wrong. There are lots of false positive even if we distinct locations by
// (l.Location.SourceTree.FilePath, l.Location.SourceSpan)
let refNum = refs |> Seq.map (fun r -> r.Locations |> Seq.length) |> Seq.fold (+) 0
let refNum =
refs
|> Seq.collect (fun r -> r.Locations)
|> Seq.distinctBy (fun l -> (l.Location.SourceTree.FilePath, l.Location.SourceSpan))
|> Seq.length

let title = sprintf "%d Reference(s)" refNum

let arg: ReferenceParams =
{ TextDocument = { Uri = lensData.DocumentUri }
Position = lensData.Position
Context = { IncludeDeclaration = true } }
let command =
{ Title = title
Command = "csharp.showReferences"
// TODO: we really want to pass some more info to the client
Arguments = None }
Command = "textDocument/references"
Arguments = Some [| arg |> serialize |] }

return { p with Command = Some command } |> success
}
4 changes: 3 additions & 1 deletion src/CSharpLanguageServer/Handlers/DocumentSymbol.fs
Original file line number Diff line number Diff line change
Expand Up @@ -327,7 +327,9 @@ module DocumentSymbol =

let collector = DocumentSymbolCollector(docText, semanticModel)
collector.Init(doc.Name)
collector.Visit(syntaxTree.GetRoot())

let! root = syntaxTree.GetRootAsync(ct) |> Async.AwaitTask
collector.Visit(root)

return collector.GetDocumentSymbols(canEmitDocSymbolHierarchy)
|> U2.Second
Expand Down
8 changes: 6 additions & 2 deletions src/CSharpLanguageServer/Handlers/Initialization.fs
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ namespace CSharpLanguageServer.Handlers
open System
open System.IO
open System.Reflection
open System.Diagnostics

open Microsoft.Build.Locator
open Ionide.LanguageServerProtocol
Expand Down Expand Up @@ -87,10 +88,13 @@ module Initialization =
// setup timer so actors get period ticks
setupTimer()

// TODO: Report server info to client (name, version)
let initializeResult =
{ InitializeResult.Default with
Capabilities = serverCapabilities }
Capabilities = serverCapabilities
ServerInfo =
Some
{ Name = Process.GetCurrentProcess().ProcessName
Version = Some (Assembly.GetExecutingAssembly().GetName().Version.ToString()) }}

return initializeResult |> LspResult.success
}
Expand Down
2 changes: 1 addition & 1 deletion src/CSharpLanguageServer/Handlers/References.fs
Original file line number Diff line number Diff line change
Expand Up @@ -39,11 +39,11 @@ module References =
| None -> return None |> success
| Some symbol ->
let! refs = context.FindReferences symbol
// FIXME: refs is wrong. There are lots of false positive even if we add Seq.distinct before Seq.toArray
return
refs
|> Seq.collect (fun r -> r.Locations)
|> Seq.map (fun rl -> Location.fromRoslynLocation rl.Location)
|> Seq.distinct
|> Seq.toArray
|> Some
|> success
Expand Down
2 changes: 2 additions & 0 deletions src/CSharpLanguageServer/Handlers/Rename.fs
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ open Microsoft.CodeAnalysis
open Microsoft.CodeAnalysis.CSharp.Syntax
open Microsoft.CodeAnalysis.FindSymbols
open Microsoft.CodeAnalysis.Rename
open FSharpPlus
open Ionide.LanguageServerProtocol.Server
open Ionide.LanguageServerProtocol.Types
open Ionide.LanguageServerProtocol.Types.LspResult
Expand Down Expand Up @@ -56,6 +57,7 @@ module Rename =
|> Seq.collect (fun projectChange -> projectChange.GetChangedDocuments())
|> Seq.map (getEdits originalSolution updatedSolution)
|> Async.Parallel
|> map (Seq.distinct >> Array.ofSeq)

let private dynamicRegistration (clientCapabilities: ClientCapabilities option) =
clientCapabilities
Expand Down
4 changes: 2 additions & 2 deletions src/CSharpLanguageServer/Handlers/TypeHierarchy.fs
Original file line number Diff line number Diff line change
Expand Up @@ -33,8 +33,8 @@ module TypeHierarchy =

let registration (clientCapabilities: ClientCapabilities option) : Registration option =
match dynamicRegistration clientCapabilities with
| true -> None
| false ->
| false -> None
| true ->
let registerOptions: TypeHierarchyRegistrationOptions =
{ DocumentSelector = Some defaultDocumentSelector }
Some
Expand Down
11 changes: 11 additions & 0 deletions src/CSharpLanguageServer/Lsp/Client.fs
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,9 @@ namespace CSharpLanguageServer.Lsp

open Ionide.LanguageServerProtocol
open Ionide.LanguageServerProtocol.Server
open Ionide.LanguageServerProtocol.Types

open CSharpLanguageServer.Types

type CSharpLspClient(sendServerNotification: ClientNotificationSender, sendServerRequest: ClientRequestSender) =
inherit LspClient()
Expand Down Expand Up @@ -40,3 +43,11 @@ type CSharpLspClient(sendServerNotification: ClientNotificationSender, sendServe

override __.TextDocumentPublishDiagnostics(p) =
sendServerNotification "textDocument/publishDiagnostics" (box p) |> Async.Ignore

override __.WorkDoneProgressCreate(token) =
let param: WorkDoneProgressCreateParams = { token = token }
sendServerRequest.Send "window/workDoneProgress/create" (box param)

override __.Progress(token, value) =
let param: ProgressParams<_> = { token = token; value = value }
sendServerNotification "$/progress" (box param) |> Async.Ignore
47 changes: 47 additions & 0 deletions src/CSharpLanguageServer/ProgressReporter.fs
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
namespace CSharpLanguageServer

open System
open Ionide.LanguageServerProtocol
open Ionide.LanguageServerProtocol.Types
open Ionide.LanguageServerProtocol.Client

type ProgressReporter(client: ILspClient) =
let mutable canReport = false

let mutable endSent = false

member val Token = ProgressToken.Second(Guid.NewGuid().ToString())

member this.Begin(title, ?cancellable, ?message, ?percentage) = async {
match! client.WorkDoneProgressCreate this.Token with
| Error _ ->
canReport <- false
| Ok() ->
canReport <- true
let param = WorkDoneProgressBegin.Create(
title = title,
?cancellable = cancellable,
?message = message,
?percentage = percentage
)
do! client.Progress(this.Token, param)
}

member this.Report(?cancellable, ?message, ?percentage) = async {
if canReport && not endSent then
let param = WorkDoneProgressReport.Create(
?cancellable = cancellable,
?message = message,
?percentage = percentage
)
do! client.Progress(this.Token, param)
}

member this.End(?message) = async {
if canReport && not endSent then
endSent <- true
let param = WorkDoneProgressEnd.Create(
?message = message
)
do! client.Progress(this.Token, param)
}
Loading
Loading