Skip to content
Closed
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
2 changes: 1 addition & 1 deletion CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/).
* Update Microsoft.Build.Locator to 1.10.12
- By @razzmatazz in https://github.com/razzmatazz/csharp-language-server/pull/296
* Add experimental support for Razor (.cshtml) files. "razor-support" feature needs to be enabled via command line
- By @razzmatazz in https://github.com/razzmatazz/csharp-language-server/pull/273
- By @razzmatazz in https://github.com/razzmatazz/csharp-language-server/pull/273, https://github.com/razzmatazz/csharp-language-server/pull/304, https://github.com/razzmatazz/csharp-language-server/pull/310
* Add suggestion to install specific dotnet sdk version
- By @jhamm in https://github.com/razzmatazz/csharp-language-server/pull/299
- Reported by @pandasoli in https://github.com/razzmatazz/csharp-language-server/issues/215
Expand Down
19 changes: 1 addition & 18 deletions src/CSharpLanguageServer/Handlers/Completion.fs
Original file line number Diff line number Diff line change
Expand Up @@ -263,24 +263,7 @@ module Completion =
//let posInCshtml = Position.toRoslynPosition sourceText.Lines p.Position
//logger.LogInformation("posInCshtml={posInCshtml}", posInCshtml)

let pos = p.Position

let root = cshtmlTree.GetRoot()

let mutable positionAndToken: (int * SyntaxToken) option = None

for t in root.DescendantTokens() do
let cshtmlSpan = cshtmlTree.GetMappedLineSpan(t.Span)

if
cshtmlSpan.StartLinePosition.Line = (int pos.Line)
&& cshtmlSpan.EndLinePosition.Line = (int pos.Line)
&& cshtmlSpan.StartLinePosition.Character <= (int pos.Character)
then
let tokenStartCharacterOffset =
(int pos.Character - cshtmlSpan.StartLinePosition.Character)

positionAndToken <- Some(t.Span.Start + tokenStartCharacterOffset, t)
let positionAndToken = solutionSemanticModelMappedPositionAndToken cshtmlTree p.Position

match positionAndToken with
| None -> return None
Expand Down
32 changes: 22 additions & 10 deletions src/CSharpLanguageServer/Handlers/Rename.fs
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@
open CSharpLanguageServer.State
open CSharpLanguageServer.Logging
open CSharpLanguageServer.Roslyn.Conversions
open CSharpLanguageServer.Roslyn.Solution
open CSharpLanguageServer.Util
open CSharpLanguageServer.Lsp.Workspace
open CSharpLanguageServer.Lsp.WorkspaceFolder
Expand Down Expand Up @@ -113,19 +114,28 @@
RegisterOptions = registerOptions |> serialize |> Some }

let prepare (context: ServerRequestContext) (p: PrepareRenameParams) : AsyncLspResult<PrepareRenameResult option> = async {
let! wf, semModel =
p.TextDocument.Uri |> workspaceDocumentSemanticModel context.Workspace

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

match docForUri with
| None -> return None |> LspResult.success
| Some doc ->
match wf, semModel with
| Some wf, Some semModel ->
let! ct = Async.CancellationToken
let! docSyntaxTree = doc.GetSyntaxTreeAsync(ct) |> Async.AwaitTask
let! docText = doc.GetTextAsync(ct) |> Async.AwaitTask

let positionAndToken =
solutionSemanticModelMappedPositionAndToken semModel.SyntaxTree p.Position

let (position, token) = positionAndToken.Value

let symbol =
token
|> Option.bind (fun x -> x.Parent |> Option.ofObj)

Check failure on line 131 in src/CSharpLanguageServer/Handlers/Rename.fs

View workflow job for this annotation

GitHub Actions / test (ubuntu-24.04, 10.0.x)

Type mismatch. Expecting a� 'SyntaxToken -> 'a' �but given a� 'SelectionRange option -> 'b option' �The type 'SelectionRange option' does not match the type 'SyntaxToken'

Check failure on line 131 in src/CSharpLanguageServer/Handlers/Rename.fs

View workflow job for this annotation

GitHub Actions / test (ubuntu-24.04, 10.0.x)

The type 'SelectionRange option' does not have 'null' as a proper value

Check failure on line 131 in src/CSharpLanguageServer/Handlers/Rename.fs

View workflow job for this annotation

GitHub Actions / test (ubuntu-24.04, 10.0.x)

Type mismatch. Expecting a� 'SyntaxToken -> 'a' �but given a� ''b option -> 'c option' �The type ''a option' does not match the type 'SyntaxToken'

Check failure on line 131 in src/CSharpLanguageServer/Handlers/Rename.fs

View workflow job for this annotation

GitHub Actions / test (ubuntu-24.04, 10.0.x)

Type mismatch. Expecting a� 'SyntaxToken -> 'a' �but given a� 'SelectionRange option -> 'b option' �The type 'SelectionRange option' does not match the type 'SyntaxToken'

Check failure on line 131 in src/CSharpLanguageServer/Handlers/Rename.fs

View workflow job for this annotation

GitHub Actions / test (ubuntu-24.04, 10.0.x)

The type 'SelectionRange option' does not have 'null' as a proper value

Check failure on line 131 in src/CSharpLanguageServer/Handlers/Rename.fs

View workflow job for this annotation

GitHub Actions / test (ubuntu-24.04, 10.0.x)

Type mismatch. Expecting a� 'SyntaxToken -> 'a' �but given a� ''b option -> 'c option' �The type ''a option' does not match the type 'SyntaxToken'

Check failure on line 131 in src/CSharpLanguageServer/Handlers/Rename.fs

View workflow job for this annotation

GitHub Actions / test (windows-latest, 10.0.x)

Type mismatch. Expecting a� 'SyntaxToken -> 'a' �but given a� 'SelectionRange option -> 'b option' �The type 'SelectionRange option' does not match the type 'SyntaxToken'

Check failure on line 131 in src/CSharpLanguageServer/Handlers/Rename.fs

View workflow job for this annotation

GitHub Actions / test (windows-latest, 10.0.x)

The type 'SelectionRange option' does not have 'null' as a proper value

Check failure on line 131 in src/CSharpLanguageServer/Handlers/Rename.fs

View workflow job for this annotation

GitHub Actions / test (windows-latest, 10.0.x)

Type mismatch. Expecting a� 'SyntaxToken -> 'a' �but given a� ''b option -> 'c option' �The type ''a option' does not match the type 'SyntaxToken'

Check failure on line 131 in src/CSharpLanguageServer/Handlers/Rename.fs

View workflow job for this annotation

GitHub Actions / test (windows-latest, 10.0.x)

Type mismatch. Expecting a� 'SyntaxToken -> 'a' �but given a� 'SelectionRange option -> 'b option' �The type 'SelectionRange option' does not match the type 'SyntaxToken'

Check failure on line 131 in src/CSharpLanguageServer/Handlers/Rename.fs

View workflow job for this annotation

GitHub Actions / test (windows-latest, 10.0.x)

The type 'SelectionRange option' does not have 'null' as a proper value

Check failure on line 131 in src/CSharpLanguageServer/Handlers/Rename.fs

View workflow job for this annotation

GitHub Actions / test (windows-latest, 10.0.x)

Type mismatch. Expecting a� 'SyntaxToken -> 'a' �but given a� ''b option -> 'c option' �The type ''a option' does not match the type 'SyntaxToken'
|> Option.map (fun parentToken -> semModel.GetSymbolInfo(parentToken))
|> Option.bind (fun x -> x.Symbol |> Option.ofObj)

let! docText = semModel.SyntaxTree.GetTextAsync(ct) |> Async.AwaitTask
let position = Position.toRoslynPosition docText.Lines p.Position
let! symbolMaybe = SymbolFinder.FindSymbolAtPositionAsync(doc, position, ct) |> Async.AwaitTask

let! symbolMaybe = SymbolFinder.FindSymbolAtPositionAsync(semModel, position, wf.Solution.Value.Workspace, ct) |> Async.AwaitTask

let symbolIsFromMetadata =
symbolMaybe
Expand All @@ -138,7 +148,7 @@

let textSpan = docText.Lines.GetTextSpan(linePositionSpan)

let! rootNode = docSyntaxTree.GetRootAsync(ct) |> Async.AwaitTask
let! rootNode = semModel.SyntaxTree.GetRootAsync(ct) |> Async.AwaitTask

let nodeOnPos =
rootNode.FindNode(textSpan, findInsideTrivia = false, getInnermostNodeForTie = true)
Expand Down Expand Up @@ -170,6 +180,8 @@
| _, _ -> None

return rangeWithPlaceholderMaybe |> LspResult.success

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

let handle (context: ServerRequestContext) (p: RenameParams) : AsyncLspResult<WorkspaceEdit option> = async {
Expand Down
20 changes: 20 additions & 0 deletions src/CSharpLanguageServer/Roslyn/Solution.fs
Original file line number Diff line number Diff line change
Expand Up @@ -460,3 +460,23 @@ let solutionFindSymbolForRazorDocumentPath solution cshtmlPath pos = async {

return symbol |> Option.map (fun sym -> (sym, project, None))
}

let solutionSemanticModelMappedPositionAndToken (cshtmlTree: SyntaxTree) pos =
let root = cshtmlTree.GetRoot()

let mutable positionAndToken: (int * SyntaxToken) option = None

for t in root.DescendantTokens() do
let cshtmlSpan = cshtmlTree.GetMappedLineSpan t.Span

if
cshtmlSpan.StartLinePosition.Line = (int pos.Line)
&& cshtmlSpan.EndLinePosition.Line = (int pos.Line)
&& cshtmlSpan.StartLinePosition.Character <= (int pos.Character)
then
let tokenStartCharacterOffset =
(int pos.Character) - cshtmlSpan.StartLinePosition.Character

positionAndToken <- Some(t.Span.Start + tokenStartCharacterOffset, t)

positionAndToken
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,8 @@
<ItemGroup>
<Compile Include="AssemblyInfo.fs" />
<Compile Include="Tooling.fs" />
<Compile Include="RenameTests.fs" />
<!--
<Compile Include="CodeActionTests.fs" />
<Compile Include="DiagnosticTests.fs" />
<Compile Include="DocumentationTests.fs" />
Expand All @@ -29,6 +31,7 @@
<Compile Include="ImplementationTests.fs" />
<Compile Include="CSharpMetadataTests.fs" />
<Compile Include="ProgressReporterTests.fs" />
-->
</ItemGroup>

<ItemGroup>
Expand Down
2 changes: 1 addition & 1 deletion tests/CSharpLanguageServer.Tests/CSharpMetadataTests.fs
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ let ``test csharp/metadata works`` () =

let typeDefinitionParams0: TypeDefinitionParams =
{ TextDocument = { Uri = classFile.Uri }
Position = { Line = 9u; Character = 16u }
Position = { Line = 10u; Character = 16u }
WorkDoneToken = None
PartialResultToken = None }

Expand Down
6 changes: 3 additions & 3 deletions tests/CSharpLanguageServer.Tests/DocumentHighlightTests.fs
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ let ``test textDocument/documentHighlight works in .cs file`` () =

let highlightParams: DocumentHighlightParams =
{ TextDocument = { Uri = classFile.Uri }
Position = { Line = 9u; Character = 8u }
Position = { Line = 10u; Character = 8u }
WorkDoneToken = None
PartialResultToken = None }

Expand All @@ -29,8 +29,8 @@ let ``test textDocument/documentHighlight works in .cs file`` () =
Kind = Some DocumentHighlightKind.Read }

{ Range =
{ Start = { Line = 9u; Character = 8u }
End = { Line = 9u; Character = 15u } }
{ Start = { Line = 10u; Character = 8u }
End = { Line = 10u; Character = 15u } }
Kind = Some DocumentHighlightKind.Read } ]

Assert.AreEqual(Some expectedHighlights, highlights |> Option.map List.ofArray)
Expand Down
Original file line number Diff line number Diff line change
@@ -1,2 +1,6 @@
@model Project.Models.Test.IndexViewModel
@Model.Output
@{
int x = 1;
x = 2;
}
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ class Class
public void MethodA(string arg)
{
string str = "";
Console.WriteLine(str);
}

public void MethodB(string arg)
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
class Class
{
public void MethodA(string arg)
{
string xxx = "";
Console.WriteLine(xxx);
}

public void MethodB(string arg)
{
MethodA(arg);
}
}
8 changes: 4 additions & 4 deletions tests/CSharpLanguageServer.Tests/ReferenceTests.fs
Original file line number Diff line number Diff line change
Expand Up @@ -41,8 +41,8 @@ let testReferenceWorks () =
let expectedLocations1: Location array =
[| { Uri = classFile.Uri
Range =
{ Start = { Line = 9u; Character = 8u }
End = { Line = 9u; Character = 15u } } } |]
{ Start = { Line = 10u; Character = 8u }
End = { Line = 10u; Character = 15u } } } |]

Assert.AreEqual(expectedLocations1, locations1.Value)

Expand All @@ -68,8 +68,8 @@ let testReferenceWorks () =

{ Uri = classFile.Uri
Range =
{ Start = { Line = 9u; Character = 8u }
End = { Line = 9u; Character = 15u } } } |]
{ Start = { Line = 10u; Character = 8u }
End = { Line = 10u; Character = 15u } } } |]

Assert.AreEqual(expectedLocations2, locations2.Value)

Expand Down
107 changes: 107 additions & 0 deletions tests/CSharpLanguageServer.Tests/RenameTests.fs
Original file line number Diff line number Diff line change
@@ -0,0 +1,107 @@
module CSharpLanguageServer.Tests.RenameTests

open System
open System.IO

open NUnit.Framework
open Ionide.LanguageServerProtocol.Types

open CSharpLanguageServer.Tests.Tooling

[<Test>]
let ``rename can be applied to a variable`` () =
use client = activateFixture "genericProject"

use classFile = client.Open "Project/Class.cs"

let prepareParams: PrepareRenameParams =
{ TextDocument = { Uri = classFile.Uri }
Position = { Line = 4u; Character = 15u }
WorkDoneToken = None }

let prepareResult: option<PrepareRenameResult> =
client.Request("textDocument/prepareRename", prepareParams)

let expectedPrepareResult: PrepareRenameResult =
{ Range =
{ Start = { Line = 4u; Character = 15u }
End = { Line = 4u; Character = 18u } }
Placeholder = "str" }
|> U3.C2

Assert.AreEqual(Some expectedPrepareResult, prepareResult)

let renameParams: RenameParams =
{ TextDocument = { Uri = classFile.Uri }
Position = { Line = 4u; Character = 15u }
WorkDoneToken = None
NewName = "xxx" }

let renameResult: option<WorkspaceEdit> =
client.Request("textDocument/rename", renameParams)

match renameResult with
| None -> failwith "Some WorkspaceEdit was expected"

| Some workspaceEdit ->
let textEdits = workspaceEdit.Changes.Value |> Map.find classFile.Uri

let expectedClassContents =
File
.ReadAllText(Path.Combine(client.SolutionDir, "Project", "Class.cs.str-renamed-to-xxx.txt"))
.ReplaceLineEndings("\n")

let actualClassContents =
classFile.GetFileContentsWithTextEditsApplied(textEdits).ReplaceLineEndings("\n")

Assert.AreEqual(expectedClassContents, actualClassContents)

[<Test>]
let ``rename can be applied to a variable in .cshtml file`` () =
use client = activateFixture "aspnetProject"

use indexCshtmlFile = client.Open "Project/Views/Test/Index.cshtml"

let prepareParams: PrepareRenameParams =
{ TextDocument = { Uri = indexCshtmlFile.Uri }
Position = { Line = 3u; Character = 11u }
WorkDoneToken = None }

let prepareResult: option<PrepareRenameResult> =
client.Request("textDocument/prepareRename", prepareParams)

let expectedPrepareResult: PrepareRenameResult =
{ Range =
{ Start = { Line = 3u; Character = 11u }
End = { Line = 3u; Character = 11u } }
Placeholder = "x" }
|> U3.C2

Assert.AreEqual(Some expectedPrepareResult, prepareResult)

(*
let renameParams: RenameParams =
{ TextDocument = { Uri = classFile.Uri }
Position = { Line = 4u; Character = 15u }
WorkDoneToken = None
NewName = "xxx" }

let renameResult: option<WorkspaceEdit> =
client.Request("textDocument/rename", renameParams)

match renameResult with
| None -> failwith "Some WorkspaceEdit was expected"

| Some workspaceEdit ->
let textEdits = workspaceEdit.Changes.Value |> Map.find classFile.Uri

let expectedClassContents =
File
.ReadAllText(Path.Combine(client.SolutionDir, "Project", "Class.cs.str-renamed-to-xxx.txt"))
.ReplaceLineEndings("\n")

let actualClassContents =
classFile.GetFileContentsWithTextEditsApplied(textEdits).ReplaceLineEndings("\n")

Assert.AreEqual(expectedClassContents, actualClassContents)
*)
2 changes: 1 addition & 1 deletion tests/CSharpLanguageServer.Tests/SignatureHelpTests.fs
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ let ``test textDocument/signatureHelp works`` () =

let signatureHelpParams0: SignatureHelpParams =
{ TextDocument = { Uri = classFile.Uri }
Position = { Line = 9u; Character = 16u }
Position = { Line = 10u; Character = 16u }
WorkDoneToken = None
Context = None }

Expand Down
2 changes: 1 addition & 1 deletion tests/CSharpLanguageServer.Tests/TypeDefinitionTests.fs
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ let ``test textDocument/typeDefinition works`` () =

let typeDefinitionParams0: TypeDefinitionParams =
{ TextDocument = { Uri = classFile.Uri }
Position = { Line = 9u; Character = 16u }
Position = { Line = 10u; Character = 16u }
WorkDoneToken = None
PartialResultToken = None }

Expand Down
Loading