Skip to content
Draft
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
1 change: 1 addition & 0 deletions docs/release-notes/.FSharp.Compiler.Service/11.0.100.md
Original file line number Diff line number Diff line change
Expand Up @@ -221,3 +221,4 @@
* `FSharp.Compiler.Syntax.SynComponentInfo` now holds the type name as `synType: SynType option` instead of the previous `longId: LongIdent` field, so tuple-type extensions such as `type ('T1 * 'T2) with` can be represented. A `member LongIdent` compatibility property returns the long identifier for named types and an empty list for tuple or erroneous type names. AST consumers that pattern-matched on the `longId` field must switch to the `synType` field or the `LongIdent` member. ([PR #19602](https://github.com/dotnet/fsharp/pull/19602))
* Optimizer: don't inline named functions in debug builds ([PR #19548](https://github.com/dotnet/fsharp/pull/19548)
* LexFilter: drop non-strict mode ([PR #20106](https://github.com/dotnet/fsharp/pull/20106))
* `FSharp.Compiler.EditorServices.Structure.getOutliningRanges` now takes the source lines as `ReadOnlyMemory<char>[]` instead of `string[]`, so a caller that already holds the whole text can slice it instead of building a string per line. Callers passing a `string[]` can migrate with `Array.map (fun line -> line.AsMemory())`.
2 changes: 2 additions & 0 deletions docs/release-notes/.VisualStudio/18.vNext.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,9 +2,11 @@

* Code-fixes for FS3888 (compiler-semantic attribute on the `.fs` but not the `.fsi`): copy the attribute into the `.fsi`, or remove it from the `.fs`. ([Issue #19560](https://github.com/dotnet/fsharp/issues/19560), [PR #19880](https://github.com/dotnet/fsharp/pull/19880))
* Expand `<inheritdoc/>` in IDE tooltips, completion, and signature help, inheriting XML documentation from base classes, interfaces, overridden members, and constructors. ([Issue #19175](https://github.com/dotnet/fsharp/issues/19175), [PR #19188](https://github.com/dotnet/fsharp/pull/19188))
* F# types, modules, members and values now appear in the GitHub Copilot Chat `#` mention picker, and attach their declaration source as context. ([PR #20409](https://github.com/dotnet/fsharp/pull/20409))

### Fixed

* Navigate To lists F# declarations while the solution is still loading. Until now the search that runs during load skipped F# entirely, and the full search that follows it is never started, so nothing F# declares could be found until the next search. ([PR #20492](https://github.com/dotnet/fsharp/pull/20492))
* Improve Find All References performance by throttling parallel typechecks. ([PR #20128](https://github.com/dotnet/fsharp/pull/20128))
* Fixed Rename incorrectly renaming `get` and `set` keywords for properties with explicit accessors. ([Issue #18270](https://github.com/dotnet/fsharp/issues/18270), [PR #19252](https://github.com/dotnet/fsharp/pull/19252))
* Fixed Find All References crash when F# project contains non-F# files like `.cshtml`. ([Issue #16394](https://github.com/dotnet/fsharp/issues/16394), [PR #19252](https://github.com/dotnet/fsharp/pull/19252))
Expand Down
3 changes: 3 additions & 0 deletions eng/Packages.props
Original file line number Diff line number Diff line change
Expand Up @@ -64,6 +64,9 @@
ComponentModelHost would otherwise stay at 17.x; that 17.x/18.x split makes S/IComponentModel ambiguous
(CS0433). Pin to the SDK 18.9.496 version so those types resolve to a single assembly. -->
<PackageVersion Include="Microsoft.VisualStudio.ComponentModelHost" Version="18.9.453" />
<!-- Copilot chat contracts. Kept on the 18.9.x wave so its MessagePack and editor dependencies match the pins
above. Compile-only: at runtime the VS-installed contract assembly wins through Copilot's binding redirect. -->
<PackageVersion Include="Microsoft.VisualStudio.Copilot" Version="18.9.918" />
<PackageVersion Include="Microsoft.VisualStudio.Designer.Interfaces" Version="18.9.438" />
<PackageVersion Include="Microsoft.VisualStudio.Editor" Version="$(VisualStudioEditorPackagesVersion)" />
<PackageVersion Include="Microsoft.VisualStudio.Extensibility.Build" Version="17.13.39620" />
Expand Down
75 changes: 38 additions & 37 deletions src/Compiler/Service/ServiceStructure.fs
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@

namespace FSharp.Compiler.EditorServices

open System
open Internal.Utilities.Library
open FSharp.Compiler.Syntax
open FSharp.Compiler.SyntaxTreeOps
Expand Down Expand Up @@ -186,27 +187,35 @@ module Structure =
}

type LineNumber = int
type LineStr = string

type CommentType =
| SingleLine
| XmlDoc

/// Determine if a line is a single line or xml documentation comment.
/// Kept at module scope: a local recursive function capturing a `ReadOnlySpan<char>`-typed
/// helper as a closure field would need to instantiate `FSharpFunc<ReadOnlySpan<char>, _>`,
/// which the CLR disallows for byref-like type arguments (FS0412).
let commentTypeOf (line: ReadOnlySpan<char>) =
if line.StartsWithOrdinal("///") then ValueSome XmlDoc
elif line.StartsWithOrdinal("//") then ValueSome SingleLine
else ValueNone

[<NoComparison>]
type CommentList =
{
Lines: ResizeArray<LineNumber * LineStr>
Lines: ResizeArray<LineNumber>
Type: CommentType
}

static member New ty lineStr =
static member New ty lineNum =
{
Type = ty
Lines = ResizeArray [ lineStr ]
Lines = ResizeArray [ lineNum ]
}

/// Returns outlining ranges for given parsed input.
let getOutliningRanges (sourceLines: string[]) (parsedInput: ParsedInput) =
let getOutliningRanges (sourceLines: ReadOnlyMemory<char>[]) (parsedInput: ParsedInput) =
let acc = ResizeArray()

/// Validation function to ensure that ranges yielded for outlining span 2 or more lines
Expand Down Expand Up @@ -661,7 +670,7 @@ module Structure =
| r :: rest, last :: _ when
r.StartLine = last.EndLine + 1
|| sourceLines[last.EndLine .. r.StartLine - 2]
|> Array.forall System.String.IsNullOrWhiteSpace
|> Array.forall (fun line -> line.Span.IsWhiteSpace())
->
loop rest res (r :: currentBulk)
| r :: rest, _ -> loop rest (currentBulk :: res) [ r ]
Expand Down Expand Up @@ -719,7 +728,7 @@ module Structure =

let collectConditionalDirectives directives sourceLines =
// Adds a fold region from prevRange.Start to the line above nextLine
let addSectionFold (prevRange: range) (nextLine: int) (sourceLines: string array) =
let addSectionFold (prevRange: range) (nextLine: int) (sourceLines: ReadOnlyMemory<char>[]) =
let startLineIndex = nextLine - 2

if startLineIndex >= 0 then
Expand Down Expand Up @@ -753,7 +762,7 @@ module Structure =
| ConditionalDirectiveTrivia.Else r -> ValueSome r
| _ -> ValueNone

let rec group directives stack (sourceLines: string array) =
let rec group directives stack (sourceLines: ReadOnlyMemory<char>[]) =
match directives with
| [] -> ()
| ConditionalDirectiveTrivia.If _ as ifDirective :: directives -> group directives (ifDirective :: stack) sourceLines
Expand Down Expand Up @@ -822,50 +831,42 @@ module Structure =
collectOpens decls
List.iter parseDeclaration decls

/// Determine if a line is a single line or xml documentation comment
let (|Comment|_|) (line: string) =
if line.StartsWithOrdinal("///") then Some XmlDoc
elif line.StartsWithOrdinal("//") then Some SingleLine
else None

let getCommentRanges trivia (lines: string[]) =
let rec loop (lastLineNum, currentComment, result as state) (lines: string list) lineNum =
match lines with
| [] -> state
| lineStr :: rest ->
match lineStr.TrimStart(), currentComment with
| Comment commentType, Some comment ->
let getCommentRanges trivia (lines: ReadOnlyMemory<char>[]) =
let rec loop (lastLineNum, currentComment, result as state) lineNum =
if lineNum = lines.Length then
state
else
match commentTypeOf (lines[lineNum].Span.TrimStart()), currentComment with
| ValueSome commentType, Some comment ->
loop
(if comment.Type = commentType && lineNum = lastLineNum + 1 then
comment.Lines.Add(lineNum, lineStr)
comment.Lines.Add lineNum
lineNum, currentComment, result
else
let comments = CommentList.New commentType (lineNum, lineStr)
let comments = CommentList.New commentType lineNum
lineNum, Some comments, comment :: result)
rest
(lineNum + 1)
| Comment commentType, None ->
let comments = CommentList.New commentType (lineNum, lineStr)
loop (lineNum, Some comments, result) rest (lineNum + 1)
| _, Some comment -> loop (lineNum, None, comment :: result) rest (lineNum + 1)
| _ -> loop (lineNum, None, result) rest (lineNum + 1)
| ValueSome commentType, None ->
let comments = CommentList.New commentType lineNum
loop (lineNum, Some comments, result) (lineNum + 1)
| ValueNone, Some comment -> loop (lineNum, None, comment :: result) (lineNum + 1)
| ValueNone, None -> loop (lineNum, None, result) (lineNum + 1)

let comments =
let _, lastComment, comments = loop (-1, None, []) (List.ofArray lines) 0
let _, lastComment, comments = loop (-1, None, []) 0

match lastComment with
| Some comment -> comment :: comments
| _ -> comments
|> List.rev

comments
|> List.filter (fun comment -> comment.Lines.Count > 1)
|> List.map (fun comment ->
let lines = comment.Lines
let startLine, startStr = lines[0]
let endLine, endStr = lines[lines.Count - 1]
let startCol = startStr.IndexOf '/'
let endCol = endStr.TrimEnd().Length
|> Seq.filter (fun comment -> comment.Lines.Count > 1)
|> Seq.map (fun comment ->
let startLine = comment.Lines[0]
let endLine = comment.Lines[comment.Lines.Count - 1]
let startCol = lines[startLine].Span.IndexOf '/'
let endCol = lines[endLine].Span.TrimEnd().Length

let scopeType =
match comment.Type with
Expand Down
3 changes: 2 additions & 1 deletion src/Compiler/Service/ServiceStructure.fsi
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@

namespace FSharp.Compiler.EditorServices

open System
open FSharp.Compiler.Syntax
open FSharp.Compiler.Text

Expand Down Expand Up @@ -79,4 +80,4 @@ module public Structure =
}

/// Returns outlining ranges for given parsed input.
val getOutliningRanges: sourceLines: string[] -> parsedInput: ParsedInput -> seq<ScopeRange>
val getOutliningRanges: sourceLines: ReadOnlyMemory<char>[] -> parsedInput: ParsedInput -> seq<ScopeRange>
49 changes: 49 additions & 0 deletions src/Compiler/Utilities/illib.fs
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ open System.Collections.Generic
open System.Collections.Concurrent
open System.Diagnostics
open System.IO
open System.Linq
open System.Threading
open System.Threading.Tasks
open System.Runtime.CompilerServices
Expand Down Expand Up @@ -112,6 +113,54 @@ module internal PervasiveAutoOpens =
member inline x.IndexOfOrdinal(value: string, startIndex, count) =
x.IndexOf(value, startIndex, count, StringComparison.Ordinal)

[<AbstractClass; Sealed>]
type ReadOnlySpanCharExtensions =

static member inline StartsWithOrdinal(str: ReadOnlySpan<char>, value: ReadOnlySpan<char>) =
str.StartsWith(value, StringComparison.Ordinal)

static member inline StartsWithOrdinal(str: ReadOnlySpan<char>, value: string) =
str.StartsWith(value.AsSpan(), StringComparison.Ordinal)

static member inline EndsWithOrdinal(str: ReadOnlySpan<char>, value: ReadOnlySpan<char>) =
str.EndsWith(value, StringComparison.Ordinal)

static member inline EndsWithOrdinal(str: ReadOnlySpan<char>, value: string) =
str.EndsWith(value.AsSpan(), StringComparison.Ordinal)

static member inline EndsWithOrdinalIgnoreCase(str: ReadOnlySpan<char>, value: ReadOnlySpan<char>) =
str.EndsWith(value, StringComparison.OrdinalIgnoreCase)

static member inline EndsWithOrdinalIgnoreCase(str: ReadOnlySpan<char>, value: string) =
str.EndsWith(value.AsSpan(), StringComparison.OrdinalIgnoreCase)

static member IndexOf(str: ReadOnlySpan<char>, value: char) =
let mutable index = -1
let mutable i = 0

while i < str.Length && index = -1 do
if str[i] = value then index <- i else i <- i + 1

index

static member inline IndexOfOrdinal(str: ReadOnlySpan<char>, value: ReadOnlySpan<char>) =
str.IndexOf(value, StringComparison.Ordinal)

static member inline IndexOfOrdinal(str: ReadOnlySpan<char>, value: string) =
str.IndexOf(value.AsSpan(), StringComparison.Ordinal)

static member inline IndexOfOrdinal(str: ReadOnlySpan<char>, value: ReadOnlySpan<char>, startIndex) =
str.Slice(startIndex).IndexOf(value, StringComparison.Ordinal)

static member inline IndexOfOrdinal(str: ReadOnlySpan<char>, value: string, startIndex) =
str.Slice(startIndex).IndexOf(value.AsSpan(), StringComparison.Ordinal)

static member inline IndexOfOrdinal(str: ReadOnlySpan<char>, value: ReadOnlySpan<char>, startIndex, count) =
str.Slice(startIndex, count).IndexOf(value, StringComparison.Ordinal)

static member inline IndexOfOrdinal(str: ReadOnlySpan<char>, value: string, startIndex, count) =
str.Slice(startIndex, count).IndexOf(value.AsSpan(), StringComparison.Ordinal)

/// Get an initialization hole
let getHole (r: _ ref) =
match r.Value with
Expand Down
42 changes: 42 additions & 0 deletions src/Compiler/Utilities/illib.fsi
Original file line number Diff line number Diff line change
Expand Up @@ -68,6 +68,48 @@ module internal PervasiveAutoOpens =

member inline IndexOfOrdinal: value: string * startIndex: int * count: int -> int

[<AbstractClass; Sealed; Extension>]
type ReadOnlySpanCharExtensions =

[<Extension>]
static member inline StartsWithOrdinal: str : ReadOnlySpan<char> * value: ReadOnlySpan<char> -> bool

[<Extension>]
static member inline StartsWithOrdinal: str : ReadOnlySpan<char> * value: string -> bool

[<Extension>]
static member inline EndsWithOrdinal: str : ReadOnlySpan<char> * value: ReadOnlySpan<char> -> bool

[<Extension>]
static member inline EndsWithOrdinal: str : ReadOnlySpan<char> * value: string -> bool

[<Extension>]
static member inline EndsWithOrdinalIgnoreCase: str : ReadOnlySpan<char> * value: ReadOnlySpan<char> -> bool

[<Extension>]
static member inline EndsWithOrdinalIgnoreCase: str : ReadOnlySpan<char> * value: string -> bool

[<Extension>]
static member IndexOf: str : ReadOnlySpan<char> * value: char -> int

[<Extension>]
static member inline IndexOfOrdinal: str : ReadOnlySpan<char> * value: ReadOnlySpan<char> -> int

[<Extension>]
static member inline IndexOfOrdinal: str : ReadOnlySpan<char> * value: string -> int

[<Extension>]
static member inline IndexOfOrdinal: str : ReadOnlySpan<char> * value: ReadOnlySpan<char> * startIndex: int -> int

[<Extension>]
static member inline IndexOfOrdinal: str : ReadOnlySpan<char> * value: string * startIndex: int -> int

[<Extension>]
static member inline IndexOfOrdinal: str : ReadOnlySpan<char> * value: ReadOnlySpan<char> * startIndex: int * count: int -> int

[<Extension>]
static member inline IndexOfOrdinal: str : ReadOnlySpan<char> * value: string * startIndex: int * count: int -> int

type Async with

/// Runs the computation synchronously, always starting on the current thread.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4744,7 +4744,7 @@ FSharp.Compiler.EditorServices.Structure+ScopeRange: Void .ctor(Scope, Collapse,
FSharp.Compiler.EditorServices.Structure: FSharp.Compiler.EditorServices.Structure+Collapse
FSharp.Compiler.EditorServices.Structure: FSharp.Compiler.EditorServices.Structure+Scope
FSharp.Compiler.EditorServices.Structure: FSharp.Compiler.EditorServices.Structure+ScopeRange
FSharp.Compiler.EditorServices.Structure: System.Collections.Generic.IEnumerable`1[FSharp.Compiler.EditorServices.Structure+ScopeRange] getOutliningRanges(System.String[], FSharp.Compiler.Syntax.ParsedInput)
FSharp.Compiler.EditorServices.Structure: System.Collections.Generic.IEnumerable`1[FSharp.Compiler.EditorServices.Structure+ScopeRange] getOutliningRanges(System.ReadOnlyMemory`1[System.Char][], FSharp.Compiler.Syntax.ParsedInput)
FSharp.Compiler.EditorServices.ToolTipElement+CompositionError: System.String errorText
FSharp.Compiler.EditorServices.ToolTipElement+CompositionError: System.String get_errorText()
FSharp.Compiler.EditorServices.ToolTipElement+Group: Microsoft.FSharp.Collections.FSharpList`1[FSharp.Compiler.EditorServices.ToolTipElementData] elements
Expand Down
Original file line number Diff line number Diff line change
@@ -1,3 +1,3 @@
<?xml version="1.0" encoding="utf-8"?>
<Project Sdk="Microsoft.NET.Sdk">

Expand Down Expand Up @@ -223,4 +223,10 @@
<ProjectReference Include="..\service\data\CSharp_Analysis\CSharp_Analysis.csproj" />
</ItemGroup>

<ItemGroup>
<!-- FSharp.Compiler.Service's PackageReference to System.Memory (for ReadOnlySpan/ReadOnlyMemory) is only
transitive here, so the SetTargetFramework override above does not carry it in on net472; pin it directly. -->
<PackageReference Include="System.Memory" />
</ItemGroup>

</Project>
Loading
Loading