diff --git a/Directory.Build.props b/Directory.Build.props
index 4b7f3db85..efd7cdc05 100644
--- a/Directory.Build.props
+++ b/Directory.Build.props
@@ -6,6 +6,8 @@
Apache-2.0
$(NoWarn);3186,0042
$(NoWarn);NU1902
+ $(WarnOn);1182
+ $(WarnOn);3390
true
$(MSBuildThisFileDirectory)CHANGELOG.md
diff --git a/benchmarks/Program.fs b/benchmarks/Program.fs
index a4eb20197..faeecc5b3 100644
--- a/benchmarks/Program.fs
+++ b/benchmarks/Program.fs
@@ -1,4 +1,5 @@
namespace Benchmarks
+
open System
open BenchmarkDotNet
open BenchmarkDotNet.Attributes
@@ -11,6 +12,6 @@ open System.Security.Cryptography
module EntryPoint =
[]
- let main argv =
- let summary = BenchmarkRunner.Run();
+ let main _argv =
+ let _summary = BenchmarkRunner.Run()
0
diff --git a/build/Program.fs b/build/Program.fs
index 55afc5000..3b5b6f060 100644
--- a/build/Program.fs
+++ b/build/Program.fs
@@ -43,8 +43,6 @@ let init args =
Context.setExecutionContext (Context.RuntimeContext.Fake execContext)
Target.initEnvironment ()
- let fsacAssemblies = "FsAutoComplete|FsAutoComplete.Core|LanguageServerProtocol"
-
let packAsToolProp = "PackAsTool", "true"
Target.create "LspTest" (fun _ ->
diff --git a/src/FsAutoComplete.Core/AbstractClassStubGenerator.fs b/src/FsAutoComplete.Core/AbstractClassStubGenerator.fs
index 397636557..9266c09ac 100644
--- a/src/FsAutoComplete.Core/AbstractClassStubGenerator.fs
+++ b/src/FsAutoComplete.Core/AbstractClassStubGenerator.fs
@@ -35,7 +35,7 @@ let private (|ExplicitCtor|_|) =
| _ -> None
/// checks to see if a type definition inherits an abstract class, and if so collects the members defined at that
-let private walkTypeDefn (SynTypeDefn(info, repr, members, implicitCtor, range, trivia)) =
+let private walkTypeDefn (SynTypeDefn(_, repr, members, implicitCtor, _, _)) =
option {
let reprMembers =
match repr with
@@ -47,7 +47,7 @@ let private walkTypeDefn (SynTypeDefn(info, repr, members, implicitCtor, range,
let! inheritType, inheritMemberRange = // this must exist for abstract types
allMembers
|> List.tryPick (function
- | SynMemberDefn.ImplicitInherit(inheritType, inheritArgs, alias, range) -> Some(inheritType, range)
+ | SynMemberDefn.ImplicitInherit(inheritType, _, _, range) -> Some(inheritType, range)
| _ -> None)
let furthestMemberToSkip, otherMembers =
@@ -82,13 +82,14 @@ let private tryFindAbstractClassExprInParsedInput
{ new SyntaxVisitorBase<_>() with
member _.VisitExpr(path, traverseExpr, defaultTraverse, expr) =
match expr with
- | SynExpr.ObjExpr(baseTy, constructorArgs, withKeyword, bindings, members, extraImpls, newExprRange, range) ->
+ | SynExpr.ObjExpr(
+ objType = baseTy; withKeyword = withKeyword; bindings = bindings; newExprRange = newExprRange) ->
Some(AbstractClassData.ObjExpr(baseTy, bindings, newExprRange, withKeyword))
| _ -> defaultTraverse expr
override _.VisitModuleDecl(_, defaultTraverse, decl) =
match decl with
- | SynModuleDecl.Types(types, m) -> List.tryPick walkTypeDefn types
+ | SynModuleDecl.Types(types, _) -> List.tryPick walkTypeDefn types
| _ -> defaultTraverse decl }
)
@@ -104,12 +105,12 @@ let tryFindAbstractClassExprInBufferAtPos
return! tryFindAbstractClassExprInParsedInput pos parseResults.ParseTree
}
-let getMemberNameAndRanges (abstractClassData) =
+let getMemberNameAndRanges abstractClassData =
match abstractClassData with
| AbstractClassData.ExplicitImpl(members = members) ->
members
|> Seq.choose (function
- | (SynMemberDefn.Member(binding, _)) -> Some binding
+ | SynMemberDefn.Member(binding, _) -> Some binding
| _ -> None)
|> Seq.choose (|MemberNamePlusRangeAndKeywordRange|_|)
|> Seq.toList
@@ -130,7 +131,7 @@ let inferStartColumn
| AbstractClassData.ExplicitImpl(inheritExpressionRange = inheritRange) ->
// 'interface ISomething with' is often in a new line, we use the indentation of that line
inheritRange.StartColumn
- | AbstractClassData.ObjExpr(newExpression = newExpr; withKeyword = withKeyword; bindings = bindings) ->
+ | AbstractClassData.ObjExpr(newExpression = newExpr; withKeyword = withKeyword; bindings = _) ->
// two cases here to consider:
// * has a with keyword on same line as newExpr
match withKeyword with
@@ -153,19 +154,19 @@ let writeAbstractClassStub
(codeGenServer: ICodeGenerationService)
(checkResultForFile: ParseAndCheckResults)
(doc: IFSACSourceText)
- (lineStr: string)
+ (_: string)
(abstractClassData: AbstractClassData)
=
asyncOption {
let pos =
Position.mkPos
abstractClassData.AbstractTypeIdentRange.Start.Line
- (abstractClassData.AbstractTypeIdentRange.End.Column)
+ abstractClassData.AbstractTypeIdentRange.End.Column
- let! (_lexerSym, usages) = codeGenServer.GetSymbolAndUseAtPositionOfKind(doc.FileName, pos, SymbolKind.Ident)
+ let! _lexerSym, usages = codeGenServer.GetSymbolAndUseAtPositionOfKind(doc.FileName, pos, SymbolKind.Ident)
let! usage = usages
- let! (displayContext, entity) =
+ let! displayContext, entity =
asyncOption {
// need the enclosing entity because we're always looking at a ctor, which isn't an Entity, but a MemberOrFunctionOrValue
match usage.Symbol with
@@ -178,7 +179,7 @@ let writeAbstractClassStub
| _ -> return! None
}
- let getMemberByLocation (name: string, range: Range, keywordRange: Range) =
+ let getMemberByLocation (_: string, range: Range, _: Range) =
match doc.GetLine range.Start with
| Some lineText ->
match Lexer.getSymbol range.Start.Line range.Start.Column lineText SymbolLookupKind.ByLongIdent [||] with
diff --git a/src/FsAutoComplete.Core/AdaptiveExtensions.fs b/src/FsAutoComplete.Core/AdaptiveExtensions.fs
index 34799c675..7d9ba5fce 100644
--- a/src/FsAutoComplete.Core/AdaptiveExtensions.fs
+++ b/src/FsAutoComplete.Core/AdaptiveExtensions.fs
@@ -77,7 +77,7 @@ type MapDisposableTupleVal<'T1, 'T2, 'Disposable when 'Disposable :> IDisposable
match cache with
| ValueSome(struct (a, b, _)) when Utils.cheapEqual a i -> b
- | ValueSome(struct (a, b, c)) ->
+ | ValueSome(struct (_, _, c)) ->
(c :> IDisposable).Dispose()
let (b, c) = mapping i
cache <- ValueSome(struct (i, b, c))
@@ -209,7 +209,7 @@ module AMap =
dirty <- HashMap.empty
d)
- override x.InputChangedObject(t, o) =
+ override x.InputChangedObject(_, o) =
#if FABLE_COMPILER
if isNull o.Tag then
let o = unbox> o
@@ -316,7 +316,7 @@ module AMap =
=
let mapping =
mapping
- >> HashMap.map (fun _ v -> AVal.constant v |> AVal.mapWithAdditionalDependencies (id))
+ >> HashMap.map (fun _ v -> AVal.constant v |> AVal.mapWithAdditionalDependencies id)
batchRecalcDirty mapping map
@@ -416,7 +416,7 @@ module Async =
return! ct.Task |> Async.AwaitTask
}
-[]
+[]
module Extensions =
type IcedTasks.CancellableTaskBase.CancellableTaskBuilderBase with
diff --git a/src/FsAutoComplete.Core/CodeGeneration.fs b/src/FsAutoComplete.Core/CodeGeneration.fs
index 35ca9c10f..49a3706d2 100644
--- a/src/FsAutoComplete.Core/CodeGeneration.fs
+++ b/src/FsAutoComplete.Core/CodeGeneration.fs
@@ -554,7 +554,7 @@ module CodeGenerationUtils =
let getAbstractNonVirtualMembers (e: FSharpEntity) =
seq {
- let genericParams = e.GenericParameters :> seq<_>
+ let _genericParams = e.GenericParameters :> seq<_>
// todo: generic param instantiations?
yield!
e.MembersFunctionsAndValues
diff --git a/src/FsAutoComplete.Core/Commands.fs b/src/FsAutoComplete.Core/Commands.fs
index 843143048..7b1e34125 100644
--- a/src/FsAutoComplete.Core/Commands.fs
+++ b/src/FsAutoComplete.Core/Commands.fs
@@ -248,7 +248,6 @@ module Commands =
(tyRes: ParseAndCheckResults)
(pos: Position)
(lines: IFSACSourceText)
- (line: LineStr)
=
async {
let doc = docForText lines tyRes
@@ -363,7 +362,6 @@ module Commands =
(tyRes: ParseAndCheckResults)
(pos: Position)
(lines: ISourceText)
- (line: LineStr)
=
async {
@@ -560,8 +558,8 @@ module Commands =
let getStartingPipe =
function
- | y :: xs when y.TokenName.ToUpper() = "INFIX_BAR_OP" -> Some y
- | x :: y :: xs when x.TokenName.ToUpper() = "WHITESPACE" && y.TokenName.ToUpper() = "INFIX_BAR_OP" -> Some y
+ | y :: _ when y.TokenName.ToUpper() = "INFIX_BAR_OP" -> Some y
+ | x :: y :: _ when x.TokenName.ToUpper() = "WHITESPACE" && y.TokenName.ToUpper() = "INFIX_BAR_OP" -> Some y
| _ -> None
let folder (lastExpressionLine, lastExpressionLineWasPipe, acc) (currentIndex, currentTokens) =
@@ -632,8 +630,8 @@ module Commands =
let getStartingPipe =
function
- | y :: xs when y.TokenName.ToUpper() = "INFIX_BAR_OP" -> Some y
- | x :: y :: xs when x.TokenName.ToUpper() = "WHITESPACE" && y.TokenName.ToUpper() = "INFIX_BAR_OP" -> Some y
+ | y :: _ when y.TokenName.ToUpper() = "INFIX_BAR_OP" -> Some y
+ | x :: y :: _ when x.TokenName.ToUpper() = "WHITESPACE" && y.TokenName.ToUpper() = "INFIX_BAR_OP" -> Some y
| _ -> None
let folder (lastExpressionLine, lastExpressionLineWasPipe, acc) (currentIndex, currentTokens) =
@@ -714,7 +712,7 @@ module Commands =
// adjust column
let pos =
match pos with
- | Pos(1, c) -> pos
+ | Pos(1, _) -> pos
| Pos(l, 0) ->
let prev = getLine (pos.DecLine())
let indentation = detectIndentation prev
@@ -724,7 +722,7 @@ module Commands =
Position.mkPos l indentation
else
pos
- | Pos(_, c) -> pos
+ | Pos(_, _) -> pos
{ Namespace = n
Position = pos
@@ -793,7 +791,6 @@ module Commands =
match scope with
| Some(SymbolDeclarationLocation.Projects(projects (*isLocalForProject=*) , true)) -> return projects
| Some(SymbolDeclarationLocation.Projects(projects (*isLocalForProject=*) , false)) ->
- let output = ResizeArray<_>()
let! resolvedProjects =
[ for project in projects do
@@ -977,7 +974,7 @@ module Commands =
///
/// Also does very basic validation of `newName`:
/// * Must be valid operator name when operator
- let adjustRenameSymbolNewName pos lineStr (text: IFSACSourceText) (tyRes: ParseAndCheckResults) (newName: string) =
+ let adjustRenameSymbolNewName pos lineStr (tyRes: ParseAndCheckResults) (newName: string) =
asyncResult {
let! symbolUse =
tyRes.TryGetSymbolUse pos lineStr
diff --git a/src/FsAutoComplete.Core/CompilerServiceInterface.fs b/src/FsAutoComplete.Core/CompilerServiceInterface.fs
index d0392fa32..4fa32b589 100644
--- a/src/FsAutoComplete.Core/CompilerServiceInterface.fs
+++ b/src/FsAutoComplete.Core/CompilerServiceInterface.fs
@@ -65,7 +65,7 @@ type FSharpCompilerServiceChecker(hasAnalyzers, typecheckCacheSize, parallelRefe
| None, _
| _, None -> p
| Some fsc, Some fsi ->
- let toReplace, otherOpts =
+ let _toReplace, otherOpts =
p.OtherOptions
|> Array.partition (fun opt ->
opt.EndsWith("FSharp.Core.dll", StringComparison.Ordinal)
@@ -94,20 +94,6 @@ type FSharpCompilerServiceChecker(hasAnalyzers, typecheckCacheSize, parallelRefe
else
opts
- let filterBadRuntimeRefs =
- let badRefs =
- [ "System.Private"
- "System.Runtime.WindowsRuntime"
- "System.Runtime.WindowsRuntime.UI.Xaml"
- "mscorlib" ]
- |> List.map (fun p -> p + ".dll")
-
- let containsBadRef (s: string) = badRefs |> List.exists (fun r -> s.EndsWith(r, StringComparison.Ordinal))
-
- fun (projOptions: FSharpProjectOptions) ->
- { projOptions with
- OtherOptions = projOptions.OtherOptions |> Array.where (containsBadRef >> not) }
-
/// ensures that any user-configured include/load files are added to the typechecking context
let addLoadedFiles (projectOptions: FSharpProjectOptions) =
let files = Array.append fsiAdditionalFiles projectOptions.SourceFiles
@@ -436,7 +422,7 @@ type FSharpCompilerServiceChecker(hasAnalyzers, typecheckCacheSize, parallelRefe
)
}
- member __.GetDeclarations(fileName: string, source, options, version) =
+ member __.GetDeclarations(fileName: string, source, options, _) =
async {
checkerLogger.info (
Log.setMessage "GetDeclarations - {file}"
diff --git a/src/FsAutoComplete.Core/Debug.fs b/src/FsAutoComplete.Core/Debug.fs
index aee09eaf5..5cce1516b 100644
--- a/src/FsAutoComplete.Core/Debug.fs
+++ b/src/FsAutoComplete.Core/Debug.fs
@@ -48,7 +48,7 @@ module Debug =
return r
}
- let toggleVerboseLogging (verbose: bool) = () // todo: set logging latch
+ let toggleVerboseLogging (_verbose: bool) = () // todo: set logging latch
let waitForDebugger () =
while not (Diagnostics.Debugger.IsAttached) do
@@ -56,7 +56,13 @@ module Debug =
let logger = LogProvider.getLoggerByName "Debugging"
- let waitForDebuggerAttached (programName) =
+ let waitForDebuggerAttached
+#if DEBUG
+ programName
+#else
+ _
+#endif
+ =
#if DEBUG
if not (System.Diagnostics.Debugger.IsAttached) then
logger.info (
@@ -73,7 +79,13 @@ module Debug =
#else
()
#endif
- let waitForDebuggerAttachedAndBreak (programName) =
+ let waitForDebuggerAttachedAndBreak
+#if DEBUG
+ programName
+#else
+ _
+#endif
+ =
#if DEBUG
waitForDebuggerAttached programName
System.Diagnostics.Debugger.Break()
diff --git a/src/FsAutoComplete.Core/DocumentationFormatter.fs b/src/FsAutoComplete.Core/DocumentationFormatter.fs
index 2512352cf..8423b0347 100644
--- a/src/FsAutoComplete.Core/DocumentationFormatter.fs
+++ b/src/FsAutoComplete.Core/DocumentationFormatter.fs
@@ -587,7 +587,7 @@ module DocumentationFormatter =
sprintf "active pattern %s: %s" apc.Name findVal
- let getAttributeSignature displayContext (attr: FSharpAttribute) =
+ let getAttributeSignature (attr: FSharpAttribute) =
let name =
formatShowDocumentationLink
attr.AttributeType.DisplayName
@@ -717,8 +717,7 @@ module DocumentationFormatter =
|> Seq.map (fun inf -> fst (format displayContext inf))
|> Seq.toArray
- let attrs =
- fse.Attributes |> Seq.map (getAttributeSignature displayContext) |> Seq.toArray
+ let attrs = fse.Attributes |> Seq.map getAttributeSignature |> Seq.toArray
let types =
fse.NestedEntities
diff --git a/src/FsAutoComplete.Core/DotnetNewTemplate.fs b/src/FsAutoComplete.Core/DotnetNewTemplate.fs
index 3ec42f8bc..d792b0c92 100644
--- a/src/FsAutoComplete.Core/DotnetNewTemplate.fs
+++ b/src/FsAutoComplete.Core/DotnetNewTemplate.fs
@@ -130,8 +130,11 @@ module DotnetNewTemplate =
let templates =
templateDetails ()
|> List.map (fun t -> t, extractDetailedString t)
- |> List.filter (fun (t, strings) -> strings |> List.exists (nameMatch userInput))
- |> List.map (fun (t, strings) -> t)
+ |> List.choose (fun (t, strings) ->
+ if strings |> List.exists (nameMatch userInput) then
+ Some t
+ else
+ None)
match templates with
| [] -> failwithf "No template exists with name : %s" userInput
diff --git a/src/FsAutoComplete.Core/FCSPatches.fs b/src/FsAutoComplete.Core/FCSPatches.fs
index 9ecaeb359..2398f9252 100644
--- a/src/FsAutoComplete.Core/FCSPatches.fs
+++ b/src/FsAutoComplete.Core/FCSPatches.fs
@@ -478,7 +478,7 @@ module SyntaxTreeOps =
(match copyInfo with
| Some(e, _) -> walkExpr e
| None -> false)
- || walkExprs (recordFields |> List.map (fun (ident, range, expr) -> expr))
+ || walkExprs (recordFields |> List.map (fun (_ident, _range, expr) -> expr))
| SynExpr.Record(copyInfo = copyInfo; recordFields = recordFields) ->
(match copyInfo with
@@ -542,11 +542,11 @@ module SyntaxTreeOps =
| SynInterpolatedStringPart.String _ -> None
| SynInterpolatedStringPart.FillExpr(x, _) -> Some x)
)
- | SynExpr.IndexRange(expr1, opm, expr2, range1, range2, range3) ->
+ | SynExpr.IndexRange(expr1 = expr1; expr2 = expr2) ->
Option.map walkExpr expr1
|> Option.orElseWith (fun _ -> Option.map walkExpr expr2)
|> Option.defaultValue false
- | SynExpr.IndexFromEnd(expr, range) -> walkExpr expr
+ | SynExpr.IndexFromEnd(expr, _) -> walkExpr expr
| SynExpr.DebugPoint(innerExpr = expr) -> walkExpr expr
| SynExpr.Dynamic(funcExpr = funcExpr; argExpr = argExpr) -> walkExpr funcExpr || walkExpr argExpr
diff --git a/src/FsAutoComplete.Core/Fsdn.fs b/src/FsAutoComplete.Core/Fsdn.fs
index 30108d1cf..5aa8fb2e2 100644
--- a/src/FsAutoComplete.Core/Fsdn.fs
+++ b/src/FsAutoComplete.Core/Fsdn.fs
@@ -67,7 +67,7 @@ let query (queryStr: string) =
let info2 = v.api.name
//return a list of strings
- let infoNamespace = info2.``namespace``
+ let _infoNamespace = info2.``namespace``
let infoClass = info2.class_name
let infoMethod = info2.id
diff --git a/src/FsAutoComplete.Core/InlayHints.fs b/src/FsAutoComplete.Core/InlayHints.fs
index d86d58706..18dda8872 100644
--- a/src/FsAutoComplete.Core/InlayHints.fs
+++ b/src/FsAutoComplete.Core/InlayHints.fs
@@ -100,7 +100,7 @@ type private FSharp.Compiler.CodeAnalysis.FSharpParseFileResults with
match binding with
| SynBinding(
headPat = SynPat.Named(range = patRange)
- returnInfo = Some(SynBindingReturnInfo(typeName = SynType.LongIdent(idents)))) -> Some patRange
+ returnInfo = Some(SynBindingReturnInfo(typeName = SynType.LongIdent _))) -> Some patRange
| _ -> defaultTraverse binding }
let result = SyntaxTraversal.Traverse(pos, x.ParseTree, visitor)
@@ -316,12 +316,12 @@ module private ShouldCreate =
isPostfixOf funcName paramName
- ///
+ ///
/// We filter out parameters that generate lots of noise in hints.
/// * parameter has no name
/// * parameter has length > 2
/// * parameter is one of a set of 'known' names that clutter (like printfn formats)
- /// * param & function is "well known"/commonly used
+ /// * param & function is "well known"/commonly used
/// * parameter does match or is a pre/postfix of user-entered text
/// * user-entered text does match or is a pre/postfix of parameter
/// * parameter is postfix of function name
@@ -620,7 +620,7 @@ let tryGetExplicitTypeInfo (text: IFSACSourceText, ast: ParsedInput) (pos: Posit
| _ -> defaultTraverse expr
member visitor.VisitPat(path, defaultTraverse, pat) =
- let invalidPositionForTypeAnnotation (pos: Position) (path: SyntaxNode list) =
+ let invalidPositionForTypeAnnotation (path: SyntaxNode list) =
match path with
| SyntaxNode.SynExpr(SynExpr.LetOrUseBang(isUse = true)) :: _ ->
// use! value =
@@ -641,7 +641,7 @@ let tryGetExplicitTypeInfo (text: IFSACSourceText, ast: ParsedInput) (pos: Posit
// see dotnet/fsharp#13115
// | _ when not (rangeContainsPos pat.Range pos) -> None
| SynPat.Named(ident = SynIdent(ident = ident)) when
- rangeContainsPos ident.idRange pos && invalidPositionForTypeAnnotation pos path
+ rangeContainsPos ident.idRange pos && invalidPositionForTypeAnnotation path
->
ExplicitType.Invalid |> Some
| SynPat.Named(ident = SynIdent(ident = ident); isThisVal = false) when rangeContainsPos ident.idRange pos ->
@@ -770,7 +770,7 @@ let private getArgRangesOfFunctionApplication (ast: ParsedInput) pos =
{ new SyntaxVisitorBase<_>() with
member _.VisitExpr(_, traverseSynExpr, defaultTraverse, expr) =
match expr with
- | SynExpr.App(isInfix = false; funcExpr = funcExpr; argExpr = argExpr; range = range) when pos = range.Start ->
+ | SynExpr.App(isInfix = false; funcExpr = funcExpr; range = range) when pos = range.Start ->
let isInfixFuncExpr =
match funcExpr with
| SynExpr.App(_, isInfix, _, _, _) -> isInfix
@@ -828,7 +828,7 @@ let private getArgRangesOfFunctionApplication (ast: ParsedInput) pos =
/// `let map f v = f v` -> `f` is target
let isPotentialTargetForTypeAnnotation
(allowFunctionValues: bool)
- (symbolUse: FSharpSymbolUse, mfv: FSharpMemberOrFunctionOrValue)
+ (_symbolUse: FSharpSymbolUse, mfv: FSharpMemberOrFunctionOrValue)
=
//ENHANCEMENT: extract settings
(mfv.IsValue || (allowFunctionValues && mfv.IsFunction))
diff --git a/src/FsAutoComplete.Core/ParseAndCheckResults.fs b/src/FsAutoComplete.Core/ParseAndCheckResults.fs
index 306cf4f48..d912e4e19 100644
--- a/src/FsAutoComplete.Core/ParseAndCheckResults.fs
+++ b/src/FsAutoComplete.Core/ParseAndCheckResults.fs
@@ -111,12 +111,12 @@ type ParseAndCheckResults
(sym: FindDeclExternalSymbol)
: (string * Position) option =
match sym with
- | FindDeclExternalSymbol.Type name -> None
- | FindDeclExternalSymbol.Constructor(typeName, args) -> None
- | FindDeclExternalSymbol.Method(typeName, name, paramSyms, genericArity) -> None
- | FindDeclExternalSymbol.Field(typeName, name) -> None
- | FindDeclExternalSymbol.Event(typeName, name) -> None
- | FindDeclExternalSymbol.Property(typeName, name) -> None
+ | FindDeclExternalSymbol.Type _ -> None
+ | FindDeclExternalSymbol.Constructor _ -> None
+ | FindDeclExternalSymbol.Method _ -> None
+ | FindDeclExternalSymbol.Field _ -> None
+ | FindDeclExternalSymbol.Event _ -> None
+ | FindDeclExternalSymbol.Property _ -> None
// attempts to manually discover symbol use and external symbol information for a range that doesn't exist in a local file
// bugfix/workaround for FCS returning invalid decl found for f# members.
@@ -217,7 +217,7 @@ type ParseAndCheckResults
{ File = UMX.untag localFilePath
Position = pos }
)
- | Error reason ->
+ | Error _ ->
logger.info (
Log.setMessage "no sourcelink info for {assembly}, decompiling instead"
>> Log.addContextDestructured "assembly" assembly
diff --git a/src/FsAutoComplete.Core/State.fs b/src/FsAutoComplete.Core/State.fs
index a3c80dbe4..dafcc3e8d 100644
--- a/src/FsAutoComplete.Core/State.fs
+++ b/src/FsAutoComplete.Core/State.fs
@@ -16,41 +16,6 @@ open FCSPatches
[]
module ProjInfoExtensions =
- let private internalGetCSharpReferenceInfo =
- fun (r: FSharpReferencedProject) ->
- let rCase, fields =
- FSharp.Reflection.FSharpValue.GetUnionFields(
- r,
- typeof,
- System.Reflection.BindingFlags.Public
- ||| System.Reflection.BindingFlags.NonPublic
- ||| System.Reflection.BindingFlags.Instance
- )
-
- if rCase.Name = "PEReference" then
- let getStamp: unit -> DateTime = fields[0] :?> _
- let reader = fields[1]
- Some(getStamp, reader)
- else
- None
-
- let private internalGetProjectOptions =
- fun (r: FSharpReferencedProject) ->
- let rCase, fields =
- FSharp.Reflection.FSharpValue.GetUnionFields(
- r,
- typeof,
- System.Reflection.BindingFlags.Public
- ||| System.Reflection.BindingFlags.NonPublic
- ||| System.Reflection.BindingFlags.Instance
- )
-
- if rCase.Name = "FSharpReference" then
- let projOptions: FSharpProjectOptions = rCase.GetFields().[1].GetValue(box r) :?> _
- Some projOptions
- else
- None
-
type FSharpReferencedProject with
member x.ProjectFilePath =
@@ -90,7 +55,7 @@ module ProjInfoExtensions =
match Array.tryFindIndex ((=) untagged) x.SourceFiles with
| None -> [||]
| Some index when index < x.SourceFiles.Length -> x.SourceFiles[index + 1 ..]
- | Some index -> [||] // at the end, so return empty list
+ | Some _ -> [||] // at the end, so return empty list
type ProjectController with
diff --git a/src/FsAutoComplete.Core/TypedAstPatterns.fs b/src/FsAutoComplete.Core/TypedAstPatterns.fs
index 2959e1921..55c765a87 100644
--- a/src/FsAutoComplete.Core/TypedAstPatterns.fs
+++ b/src/FsAutoComplete.Core/TypedAstPatterns.fs
@@ -70,7 +70,7 @@ module SymbolUse =
| :? FSharpParameter as param -> Some param
| _ -> None
- let (|StaticParameter|_|) (symbol: FSharpSymbolUse) = Some
+ let (|StaticParameter|_|) (_symbol: FSharpSymbolUse) = Some
let (|UnionCase|_|) (symbol: FSharpSymbolUse) =
@@ -411,7 +411,7 @@ module SymbolPatterns =
else
None
- let (|ProvidedType|_|) (e: FSharpEntity) = None
+ let (|ProvidedType|_|) (_e: FSharpEntity) = None
let (|ByRef|_|) (e: FSharpEntity) = if e.IsByRef then Some() else None
@@ -421,7 +421,7 @@ module SymbolPatterns =
let (|Namespace|_|) (entity: FSharpEntity) = if entity.IsNamespace then Some() else None
- let (|ProvidedAndErasedType|_|) (entity: FSharpEntity) = None
+ let (|ProvidedAndErasedType|_|) (_entity: FSharpEntity) = None
let (|Enum|_|) (entity: FSharpEntity) = if entity.IsEnum then Some() else None
diff --git a/src/FsAutoComplete.Core/TypedAstPatterns.fsi b/src/FsAutoComplete.Core/TypedAstPatterns.fsi
index 91ff92fc3..2cdc67b71 100644
--- a/src/FsAutoComplete.Core/TypedAstPatterns.fsi
+++ b/src/FsAutoComplete.Core/TypedAstPatterns.fsi
@@ -13,7 +13,7 @@ module SymbolUse =
val (|MemberFunctionOrValue|_|): symbol: FSharpSymbolUse -> FSharpMemberOrFunctionOrValue option
val (|ActivePattern|_|): (FSharpSymbolUse -> FSharpMemberOrFunctionOrValue option)
val (|Parameter|_|): symbol: FSharpSymbolUse -> FSharpParameter option
- val (|StaticParameter|_|): symbol: FSharpSymbolUse -> ('a -> 'a option)
+ val (|StaticParameter|_|): FSharpSymbolUse -> ('a -> 'a option)
val (|UnionCase|_|): symbol: FSharpSymbolUse -> FSharpUnionCase option
val (|Constructor|_|): (FSharpSymbolUse -> FSharpMemberOrFunctionOrValue option)
val (|TypeAbbreviation|_|): (FSharpSymbolUse -> FSharpEntity option)
@@ -56,12 +56,12 @@ module SymbolPatterns =
val (|Interface|_|): e: FSharpEntity -> unit option
val (|AbstractClass|_|): e: FSharpEntity -> unit option
val (|FSharpType|_|): e: FSharpEntity -> unit option
- val (|ProvidedType|_|): e: FSharpEntity -> 'a option
+ val (|ProvidedType|_|): FSharpEntity -> 'a option
val (|ByRef|_|): e: FSharpEntity -> unit option
val (|Array|_|): e: FSharpEntity -> unit option
val (|FSharpModule|_|): entity: FSharpEntity -> unit option
val (|Namespace|_|): entity: FSharpEntity -> unit option
- val (|ProvidedAndErasedType|_|): entity: FSharpEntity -> 'a option
+ val (|ProvidedAndErasedType|_|): FSharpEntity -> 'a option
val (|Enum|_|): entity: FSharpEntity -> unit option
val (|Tuple|_|): ty: FSharpType option -> unit option
val (|RefCell|_|): ty: FSharpType -> unit option
diff --git a/src/FsAutoComplete.Core/TypedAstUtils.fs b/src/FsAutoComplete.Core/TypedAstUtils.fs
index dab90c7fa..601167e30 100644
--- a/src/FsAutoComplete.Core/TypedAstUtils.fs
+++ b/src/FsAutoComplete.Core/TypedAstUtils.fs
@@ -287,7 +287,7 @@ module TypedAstExtensionHelpers =
| UnionCase fsu -> fsu.XmlDoc
| ActivePattern apc -> apc.XmlDoc
| GenericParameter gp -> gp.XmlDoc
- | Parameter p -> FSharpXmlDoc.None
+ | Parameter _ -> FSharpXmlDoc.None
type FSharpGenericParameterMemberConstraint with
diff --git a/src/FsAutoComplete.Core/UnionPatternMatchCaseGenerator.fs b/src/FsAutoComplete.Core/UnionPatternMatchCaseGenerator.fs
index 8dbc3963b..066f15385 100644
--- a/src/FsAutoComplete.Core/UnionPatternMatchCaseGenerator.fs
+++ b/src/FsAutoComplete.Core/UnionPatternMatchCaseGenerator.fs
@@ -2,10 +2,8 @@
module FsAutoComplete.UnionPatternMatchCaseGenerator
open System
-open FsAutoComplete.UntypedAstUtils
open FSharp.Compiler.Syntax
open FSharp.Compiler.Text
-open FSharp.Compiler.Syntax
open FsAutoComplete.CodeGenerationUtils
open FSharp.Compiler.Symbols
open FsToolkit.ErrorHandling
@@ -40,11 +38,11 @@ let private clauseIsCandidateForCodeGen (cursorPos: Position) (SynMatchClause(pa
match pat with
| SynPat.Paren(innerPat, _)
| SynPat.Attrib(innerPat, _, _) -> patIsCandidate innerPat
- | SynPat.Const(_, _) -> false
- | SynPat.Wild(_) -> false
+ | SynPat.Const _ -> false
+ | SynPat.Wild _ -> false
// TODO: check if we have to handle these cases
| SynPat.Typed(innerPat, _, _) -> patIsCandidate innerPat
- | SynPat.OptionalVal(_, _) -> false
+ | SynPat.OptionalVal _ -> false
| SynPat.Or(lhsPat = leftPat; rhsPat = rightPat) -> patIsCandidate leftPat || patIsCandidate rightPat
| SynPat.Ands(innerPatList, _) -> List.exists patIsCandidate innerPatList
// This is the 'hd :: tail -> ...' pattern
@@ -53,7 +51,7 @@ let private clauseIsCandidateForCodeGen (cursorPos: Position) (SynMatchClause(pa
// The cursor should not be in the nested patterns
Range.rangeContainsPos r cursorPos
&& List.forall (not << patIsCandidate) nestedPats
- | SynPat.ListCons(lhs, rhs, r, _) -> patIsCandidate lhs || patIsCandidate rhs
+ | SynPat.ListCons(lhs, rhs, _, _) -> patIsCandidate lhs || patIsCandidate rhs
| SynPat.Tuple _
| SynPat.ArrayOrList _
| SynPat.Record _
@@ -122,7 +120,7 @@ let private tryFindPatternMatchExprInParsedInput (pos: Position) (parsedInput: P
| SynMemberDefn.NestedType(typeDef, _access, _range) -> walkSynTypeDefn typeDef
| SynMemberDefn.ValField(_field, _range) -> None
| SynMemberDefn.LetBindings(bindings, _isStatic, _isRec, _range) -> List.tryPick walkBinding bindings
- | SynMemberDefn.GetSetMember(_get, _set, _range, trivia) -> None
+ | SynMemberDefn.GetSetMember(_get, _set, _range, _) -> None
| SynMemberDefn.Open _
| SynMemberDefn.ImplicitInherit _
| SynMemberDefn.Inherit _
@@ -242,7 +240,7 @@ let private tryFindPatternMatchExprInParsedInput (pos: Position) (parsedInput: P
| SynExpr.DotSet(synExpr1, _longIdent, synExpr2, _range) -> List.tryPick walkExpr [ synExpr1; synExpr2 ]
- | SynExpr.DotIndexedGet(synExpr, argList, _range, _range2) -> walkExpr argList
+ | SynExpr.DotIndexedGet(_, argList, _range, _range2) -> walkExpr argList
| SynExpr.DotIndexedSet(synExpr1, argList, synExpr2, _, _range, _range2) ->
[ synExpr1; argList; synExpr2 ] |> List.tryPick walkExpr
@@ -297,16 +295,16 @@ let getWrittenCases (patMatchExpr: PatternMatchExpr) =
match pat with
| SynPat.Const(_const, _) -> false
// TODO: figure out if these cases are supposed to happen or not
- | SynPat.Or(_)
- | SynPat.Ands(_, _)
- | SynPat.LongIdent(_)
- | SynPat.ArrayOrList(_, _, _)
- | SynPat.Null(_)
- | SynPat.InstanceMember(_, _, _, _, _)
- | SynPat.IsInst(_, _)
- | SynPat.QuoteExpr(_, _)
+ | SynPat.Or _
+ | SynPat.Ands _
+ | SynPat.LongIdent _
+ | SynPat.ArrayOrList _
+ | SynPat.Null _
+ | SynPat.InstanceMember _
+ | SynPat.IsInst _
+ | SynPat.QuoteExpr _
| SynPat.ListCons _
- | SynPat.FromParseError(_, _) -> false
+ | SynPat.FromParseError _ -> false
| SynPat.Tuple(elementPats = innerPatList) -> List.forall checkPattern innerPatList
@@ -315,9 +313,9 @@ let getWrittenCases (patMatchExpr: PatternMatchExpr) =
|> List.map (fun (_, _, innerPat) -> innerPat)
|> List.forall checkPattern
- | SynPat.OptionalVal(_, _) -> true
- | SynPat.Named(_)
- | SynPat.Wild(_) -> true
+ | SynPat.OptionalVal _ -> true
+ | SynPat.Named _
+ | SynPat.Wild _ -> true
| SynPat.Typed(innerPat, _, _)
| SynPat.Attrib(innerPat, _, _)
| SynPat.Paren(innerPat, _) -> checkPattern innerPat
@@ -365,7 +363,7 @@ let getWrittenCases (patMatchExpr: PatternMatchExpr) =
| SynMatchClause(pat, None, _, _, _, _) -> getCasesInPattern pat
| _ -> []
- patMatchExpr.Clauses |> List.collect (getCasesInClause) |> Set.ofList
+ patMatchExpr.Clauses |> List.collect getCasesInClause |> Set.ofList
let shouldGenerateUnionPatternMatchCases (patMatchExpr: PatternMatchExpr) (entity: FSharpEntity) =
let caseCount = entity.UnionCases.Count
@@ -504,7 +502,7 @@ let tryFindCaseInsertionParamsAtPos (codeGenService: ICodeGenerationService) pos
let tryFindUnionDefinitionFromPos (codeGenService: ICodeGenerationService) pos document =
asyncOption {
let! patMatchExpr, insertionParams = tryFindCaseInsertionParamsAtPos codeGenService pos document
- let! symbol, symbolUse = codeGenService.GetSymbolAndUseAtPositionOfKind(document.FullName, pos, SymbolKind.Ident)
+ let! _, symbolUse = codeGenService.GetSymbolAndUseAtPositionOfKind(document.FullName, pos, SymbolKind.Ident)
let! superficialTypeDefinition =
@@ -518,12 +516,12 @@ let tryFindUnionDefinitionFromPos (codeGenService: ICodeGenerationService) pos d
| _ -> return None
}
- let! realTypeDefinition = superficialTypeDefinition
+ let! _ = superficialTypeDefinition
let! realTypeDefinition =
match superficialTypeDefinition with
| Some(AbbreviatedType(TypeWithDefinition typeDef)) when typeDef.IsFSharpUnion -> Some typeDef
- | Some(UnionType(_)) -> superficialTypeDefinition
+ | Some(UnionType _) -> superficialTypeDefinition
| _ -> None
return patMatchExpr, realTypeDefinition, insertionParams
@@ -554,7 +552,7 @@ let private formatCase (ctxt: Context) (case: FSharpUnionCase) =
// De-duplicate field names if there are conflicts
let newFieldNames =
Seq.unfold
- (fun ((i, currentNamesWithIndices) as _state) ->
+ (fun (i, currentNamesWithIndices as _state) ->
if i < fieldNames.Length then
let name = fieldNames.[i]
diff --git a/src/FsAutoComplete.Core/UntypedAstUtils.fs b/src/FsAutoComplete.Core/UntypedAstUtils.fs
index 59c5d9513..17c923bcc 100644
--- a/src/FsAutoComplete.Core/UntypedAstUtils.fs
+++ b/src/FsAutoComplete.Core/UntypedAstUtils.fs
@@ -28,61 +28,61 @@ module Syntax =
type SyntaxCollectorBase() =
abstract WalkSynModuleOrNamespace: SynModuleOrNamespace -> unit
- default _.WalkSynModuleOrNamespace m = ()
+ default _.WalkSynModuleOrNamespace _ = ()
abstract WalkAttribute: SynAttribute -> unit
- default _.WalkAttribute a = ()
+ default _.WalkAttribute _ = ()
abstract WalkSynModuleDecl: SynModuleDecl -> unit
- default _.WalkSynModuleDecl m = ()
+ default _.WalkSynModuleDecl _ = ()
abstract WalkExpr: SynExpr -> unit
- default _.WalkExpr s = ()
+ default _.WalkExpr _ = ()
abstract WalkTypar: SynTypar -> unit
- default _.WalkTypar s = ()
+ default _.WalkTypar _ = ()
abstract WalkTyparDecl: SynTyparDecl -> unit
- default _.WalkTyparDecl s = ()
+ default _.WalkTyparDecl _ = ()
abstract WalkTypeConstraint: SynTypeConstraint -> unit
- default _.WalkTypeConstraint s = ()
+ default _.WalkTypeConstraint _ = ()
abstract WalkType: SynType -> unit
- default _.WalkType s = ()
+ default _.WalkType _ = ()
abstract WalkMemberSig: SynMemberSig -> unit
- default _.WalkMemberSig s = ()
+ default _.WalkMemberSig _ = ()
abstract WalkPat: SynPat -> unit
- default _.WalkPat s = ()
+ default _.WalkPat _ = ()
abstract WalkValTyparDecls: SynValTyparDecls -> unit
- default _.WalkValTyparDecls s = ()
+ default _.WalkValTyparDecls _ = ()
abstract WalkBinding: SynBinding -> unit
- default _.WalkBinding s = ()
+ default _.WalkBinding _ = ()
abstract WalkSimplePat: SynSimplePat -> unit
- default _.WalkSimplePat s = ()
+ default _.WalkSimplePat _ = ()
abstract WalkInterfaceImpl: SynInterfaceImpl -> unit
- default _.WalkInterfaceImpl s = ()
+ default _.WalkInterfaceImpl _ = ()
abstract WalkClause: SynMatchClause -> unit
- default _.WalkClause s = ()
+ default _.WalkClause _ = ()
abstract WalkInterpolatedStringPart: SynInterpolatedStringPart -> unit
- default _.WalkInterpolatedStringPart s = ()
+ default _.WalkInterpolatedStringPart _ = ()
abstract WalkMeasure: SynMeasure -> unit
- default _.WalkMeasure s = ()
+ default _.WalkMeasure _ = ()
abstract WalkComponentInfo: SynComponentInfo -> unit
- default _.WalkComponentInfo s = ()
+ default _.WalkComponentInfo _ = ()
abstract WalkTypeDefnSigRepr: SynTypeDefnSigRepr -> unit
- default _.WalkTypeDefnSigRepr s = ()
+ default _.WalkTypeDefnSigRepr _ = ()
abstract WalkUnionCaseType: SynUnionCaseKind -> unit
- default _.WalkUnionCaseType s = ()
+ default _.WalkUnionCaseType _ = ()
abstract WalkEnumCase: SynEnumCase -> unit
- default _.WalkEnumCase s = ()
+ default _.WalkEnumCase _ = ()
abstract WalkField: SynField -> unit
- default _.WalkField s = ()
+ default _.WalkField _ = ()
abstract WalkTypeDefnSimple: SynTypeDefnSimpleRepr -> unit
- default _.WalkTypeDefnSimple s = ()
+ default _.WalkTypeDefnSimple _ = ()
abstract WalkValSig: SynValSig -> unit
- default _.WalkValSig s = ()
+ default _.WalkValSig _ = ()
abstract WalkMember: SynMemberDefn -> unit
- default _.WalkMember s = ()
+ default _.WalkMember _ = ()
abstract WalkUnionCase: SynUnionCase -> unit
- default _.WalkUnionCase s = ()
+ default _.WalkUnionCase _ = ()
abstract WalkTypeDefnRepr: SynTypeDefnRepr -> unit
- default _.WalkTypeDefnRepr s = ()
+ default _.WalkTypeDefnRepr _ = ()
abstract WalkTypeDefn: SynTypeDefn -> unit
- default _.WalkTypeDefn s = ()
+ default _.WalkTypeDefn _ = ()
let walkAst (walker: SyntaxCollectorBase) (input: ParsedInput) : unit =
@@ -90,7 +90,7 @@ module Syntax =
List.iter walkSynModuleOrNamespace moduleOrNamespaceList
()
- and walkSynModuleOrNamespace (SynModuleOrNamespace(decls = decls; attribs = AllAttrs attrs; range = r) as s) =
+ and walkSynModuleOrNamespace (SynModuleOrNamespace(decls = decls; attribs = AllAttrs attrs; range = _) as s) =
walker.WalkSynModuleOrNamespace s
List.iter walkAttribute attrs
List.iter walkSynModuleDecl decls
@@ -112,64 +112,64 @@ module Syntax =
walker.WalkTypeConstraint s
match s with
- | SynTypeConstraint.WhereTyparIsValueType(t, r)
- | SynTypeConstraint.WhereTyparIsReferenceType(t, r)
- | SynTypeConstraint.WhereTyparIsUnmanaged(t, r)
- | SynTypeConstraint.WhereTyparSupportsNull(t, r)
- | SynTypeConstraint.WhereTyparIsComparable(t, r)
- | SynTypeConstraint.WhereTyparIsEquatable(t, r) -> walkTypar t
- | SynTypeConstraint.WhereTyparDefaultsToType(t, ty, r)
- | SynTypeConstraint.WhereTyparSubtypeOfType(t, ty, r) ->
+ | SynTypeConstraint.WhereTyparIsValueType(t, _)
+ | SynTypeConstraint.WhereTyparIsReferenceType(t, _)
+ | SynTypeConstraint.WhereTyparIsUnmanaged(t, _)
+ | SynTypeConstraint.WhereTyparSupportsNull(t, _)
+ | SynTypeConstraint.WhereTyparIsComparable(t, _)
+ | SynTypeConstraint.WhereTyparIsEquatable(t, _) -> walkTypar t
+ | SynTypeConstraint.WhereTyparDefaultsToType(t, ty, _)
+ | SynTypeConstraint.WhereTyparSubtypeOfType(t, ty, _) ->
walkTypar t
walkType ty
- | SynTypeConstraint.WhereTyparIsEnum(t, ts, r)
- | SynTypeConstraint.WhereTyparIsDelegate(t, ts, r) ->
+ | SynTypeConstraint.WhereTyparIsEnum(t, ts, _)
+ | SynTypeConstraint.WhereTyparIsDelegate(t, ts, _) ->
walkTypar t
List.iter walkType ts
- | SynTypeConstraint.WhereTyparSupportsMember(t, sign, r) ->
+ | SynTypeConstraint.WhereTyparSupportsMember(t, sign, _) ->
walkType t
walkMemberSig sign
- | SynTypeConstraint.WhereSelfConstrained(t, r) -> walkType t
+ | SynTypeConstraint.WhereSelfConstrained(t, _) -> walkType t
and walkPat s =
walker.WalkPat s
match s with
- | SynPat.Tuple(elementPats = pats; range = r)
- | SynPat.ArrayOrList(_, pats, r)
- | SynPat.Ands(pats, r) -> List.iter walkPat pats
- | SynPat.Named(ident, _, _, r) -> ()
- | SynPat.Typed(pat, t, r) ->
+ | SynPat.Tuple(elementPats = pats)
+ | SynPat.ArrayOrList(elementPats = pats)
+ | SynPat.Ands(pats = pats) -> List.iter walkPat pats
+ | SynPat.Named _ -> ()
+ | SynPat.Typed(pat, t, _) ->
walkPat pat
walkType t
- | SynPat.Attrib(pat, AllAttrs attrs, r) ->
+ | SynPat.Attrib(pat, AllAttrs attrs, _) ->
walkPat pat
List.iter walkAttribute attrs
- | SynPat.Or(pat1, pat2, r, _) -> List.iter walkPat [ pat1; pat2 ]
- | SynPat.LongIdent(typarDecls = typars; argPats = ConstructorPats pats; range = r) ->
+ | SynPat.Or(pat1, pat2, _, _) -> List.iter walkPat [ pat1; pat2 ]
+ | SynPat.LongIdent(typarDecls = typars; argPats = ConstructorPats pats; range = _) ->
Option.iter walkSynValTyparDecls typars
List.iter walkPat pats
- | SynPat.Paren(pat, r) -> walkPat pat
- | SynPat.IsInst(t, r) -> walkType t
- | SynPat.QuoteExpr(e, r) -> walkExpr e
- | SynPat.Const(_, r) -> ()
- | SynPat.Wild(r) -> ()
- | SynPat.Record(_, r) -> ()
- | SynPat.Null(r) -> ()
- | SynPat.OptionalVal(_, r) -> ()
- | SynPat.InstanceMember(_, _, _, accessibility, r) -> ()
- | SynPat.FromParseError(_, r) -> ()
- | SynPat.As(lpat, rpat, r) ->
+ | SynPat.Paren(pat, _) -> walkPat pat
+ | SynPat.IsInst(t, _) -> walkType t
+ | SynPat.QuoteExpr(e, _) -> walkExpr e
+ | SynPat.Const _ -> ()
+ | SynPat.Wild _ -> ()
+ | SynPat.Record _ -> ()
+ | SynPat.Null _ -> ()
+ | SynPat.OptionalVal _ -> ()
+ | SynPat.InstanceMember _ -> ()
+ | SynPat.FromParseError _ -> ()
+ | SynPat.As(lpat, rpat, _) ->
walkPat lpat
walkPat rpat
- | SynPat.ListCons(lpat, rpat, r, _) ->
+ | SynPat.ListCons(lpat, rpat, _, _) ->
walkPat lpat
walkPat rpat
- and walkTypar (SynTypar(_, _, _) as s) = walker.WalkTypar s
+ and walkTypar (SynTypar _ as s) = walker.WalkTypar s
and walkBinding
- (SynBinding(attributes = AllAttrs attrs; headPat = pat; returnInfo = returnInfo; expr = e; range = r) as s)
+ (SynBinding(attributes = AllAttrs attrs; headPat = pat; returnInfo = returnInfo; expr = e; range = _) as s)
=
walker.WalkBinding s
List.iter walkAttribute attrs
@@ -177,14 +177,14 @@ module Syntax =
walkExpr e
returnInfo
- |> Option.iter (fun (SynBindingReturnInfo(t, r, attrs, _)) ->
+ |> Option.iter (fun (SynBindingReturnInfo(t, _, attrs, _)) ->
walkType t
walkAttributes attrs)
and walkAttributes (attrs: SynAttributes) =
List.iter (fun (attrList: SynAttributeList) -> List.iter walkAttribute attrList.Attributes) attrs
- and walkInterfaceImpl (SynInterfaceImpl(bindings = bindings; range = r) as s) =
+ and walkInterfaceImpl (SynInterfaceImpl(bindings = bindings; range = _) as s) =
walker.WalkInterfaceImpl s
List.iter walkBinding bindings
@@ -192,43 +192,43 @@ module Syntax =
walker.WalkType s
match s with
- | SynType.Array(_, t, r)
- | SynType.HashConstraint(t, r)
- | SynType.MeasurePower(t, _, r) -> walkType t
- | SynType.Fun(t1, t2, r, _) ->
+ | SynType.Array(_, t, _)
+ | SynType.HashConstraint(t, _)
+ | SynType.MeasurePower(t, _, _) -> walkType t
+ | SynType.Fun(t1, t2, _, _) ->
// | SynType.MeasureDivide(t1, t2, r) ->
walkType t1
walkType t2
- | SynType.App(ty, _, types, _, _, _, r) ->
+ | SynType.App(ty, _, types, _, _, _, _) ->
walkType ty
List.iter walkType types
- | SynType.LongIdentApp(_, _, _, types, _, _, r) -> List.iter walkType types
- | SynType.Tuple(_, ts, r) ->
+ | SynType.LongIdentApp(_, _, _, types, _, _, _) -> List.iter walkType types
+ | SynType.Tuple(_, ts, _) ->
ts
|> List.iter (function
| SynTupleTypeSegment.Type t -> walkType t
| _ -> ())
- | SynType.WithGlobalConstraints(t, typeConstraints, r) ->
+ | SynType.WithGlobalConstraints(t, typeConstraints, _) ->
walkType t
List.iter walkTypeConstraint typeConstraints
- | SynType.LongIdent(longDotId) -> ()
- | SynType.AnonRecd(isStruct, typeNames, r) -> ()
- | SynType.Var(genericName, r) -> ()
- | SynType.Anon(r) -> ()
- | SynType.StaticConstant(constant, r) -> ()
- | SynType.StaticConstantExpr(expr, r) -> ()
- | SynType.StaticConstantNamed(expr, _, r) -> ()
- | SynType.Paren(innerType, r) -> walkType innerType
- | SynType.SignatureParameter(usedType = t; range = r) -> walkType t
- | SynType.Or(lhs, rhs, r, _) ->
+ | SynType.LongIdent _ -> ()
+ | SynType.AnonRecd _ -> ()
+ | SynType.Var _ -> ()
+ | SynType.Anon _ -> ()
+ | SynType.StaticConstant _ -> ()
+ | SynType.StaticConstantExpr _ -> ()
+ | SynType.StaticConstantNamed _ -> ()
+ | SynType.Paren(innerType, _) -> walkType innerType
+ | SynType.SignatureParameter(usedType = t; range = _) -> walkType t
+ | SynType.Or(lhs, rhs, _, _) ->
walkType lhs
walkType rhs
- | SynType.FromParseError(r) -> ()
+ | SynType.FromParseError _ -> ()
| SynType.Intersection(typar, types, _, _) ->
Option.iter walkTypar typar
List.iter walkType types
- and walkClause (SynMatchClause(pat, e1, e2, r, _, _) as s) =
+ and walkClause (SynMatchClause(pat, e1, e2, _, _, _) as s) =
walker.WalkClause s
walkPat pat
walkExpr e2
@@ -236,14 +236,14 @@ module Syntax =
and walkSimplePats =
function
- | SynSimplePats.SimplePats(pats = pats; range = r) -> List.iter walkSimplePat pats
+ | SynSimplePats.SimplePats(pats = pats; range = _) -> List.iter walkSimplePat pats
and walkInterpolatedStringPart s =
walker.WalkInterpolatedStringPart s
match s with
- | SynInterpolatedStringPart.FillExpr(expr, ident) -> walkExpr expr
- | SynInterpolatedStringPart.String(s, r) -> ()
+ | SynInterpolatedStringPart.FillExpr(expr, _) -> walkExpr expr
+ | SynInterpolatedStringPart.String _ -> ()
and walkExpr s =
walker.WalkExpr s
@@ -266,115 +266,115 @@ module Syntax =
| SynExpr.Quote(operator, _, quotedExpr, _, _) ->
walkExpr operator
walkExpr quotedExpr
- | SynExpr.SequentialOrImplicitYield(_, e1, e2, ifNotE, r) ->
+ | SynExpr.SequentialOrImplicitYield(_, e1, e2, ifNotE, _) ->
walkExpr e1
walkExpr e2
walkExpr ifNotE
- | SynExpr.Lambda(args = pats; body = e; range = r) ->
+ | SynExpr.Lambda(args = pats; body = e; range = _) ->
walkSimplePats pats
walkExpr e
- | SynExpr.New(_, t, e, r)
- | SynExpr.TypeTest(e, t, r)
- | SynExpr.Upcast(e, t, r)
- | SynExpr.Downcast(e, t, r) ->
+ | SynExpr.New(_, t, e, _)
+ | SynExpr.TypeTest(e, t, _)
+ | SynExpr.Upcast(e, t, _)
+ | SynExpr.Downcast(e, t, _) ->
walkExpr e
walkType t
| SynExpr.Tuple(_, es, _, _)
| Sequentials es -> List.iter walkExpr es //TODO??
- | SynExpr.ArrayOrList(_, es, r) -> List.iter walkExpr es
- | SynExpr.App(_, _, e1, e2, r)
- | SynExpr.TryFinally(e1, e2, r, _, _, _)
- | SynExpr.While(_, e1, e2, r) -> List.iter walkExpr [ e1; e2 ]
- | SynExpr.Record(_, _, fields, r) ->
+ | SynExpr.ArrayOrList(_, es, _) -> List.iter walkExpr es
+ | SynExpr.App(_, _, e1, e2, _)
+ | SynExpr.TryFinally(e1, e2, _, _, _, _)
+ | SynExpr.While(_, e1, e2, _) -> List.iter walkExpr [ e1; e2 ]
+ | SynExpr.Record(_, _, fields, _) ->
fields
- |> List.iter (fun (SynExprRecordField(fieldName = (ident, _); expr = e)) -> e |> Option.iter walkExpr)
- | SynExpr.ObjExpr(ty, argOpt, _, bindings, _, ifaces, _, r) ->
+ |> List.iter (fun (SynExprRecordField(fieldName = (_, _); expr = e)) -> e |> Option.iter walkExpr)
+ | SynExpr.ObjExpr(ty, argOpt, _, bindings, _, ifaces, _, _) ->
- argOpt |> Option.iter (fun (e, ident) -> walkExpr e)
+ argOpt |> Option.iter (fun (e, _) -> walkExpr e)
walkType ty
List.iter walkBinding bindings
List.iter walkInterfaceImpl ifaces
- | SynExpr.For(identBody = e1; toBody = e2; doBody = e3; range = r) -> List.iter walkExpr [ e1; e2; e3 ]
- | SynExpr.ForEach(_, _, _, _, pat, e1, e2, r) ->
+ | SynExpr.For(identBody = e1; toBody = e2; doBody = e3; range = _) -> List.iter walkExpr [ e1; e2; e3 ]
+ | SynExpr.ForEach(_, _, _, _, pat, e1, e2, _) ->
walkPat pat
List.iter walkExpr [ e1; e2 ]
- | SynExpr.MatchLambda(_, _, synMatchClauseList, _, r) -> List.iter walkClause synMatchClauseList
- | SynExpr.Match(expr = e; clauses = synMatchClauseList; range = r) ->
+ | SynExpr.MatchLambda(_, _, synMatchClauseList, _, _) -> List.iter walkClause synMatchClauseList
+ | SynExpr.Match(expr = e; clauses = synMatchClauseList; range = _) ->
walkExpr e
List.iter walkClause synMatchClauseList
- | SynExpr.TypeApp(e, _, tys, _, _, tr, r) ->
+ | SynExpr.TypeApp(e, _, tys, _, _, _, _) ->
List.iter walkType tys
walkExpr e
- | SynExpr.LetOrUse(bindings = bindings; body = e; range = r) ->
+ | SynExpr.LetOrUse(bindings = bindings; body = e; range = _) ->
List.iter walkBinding bindings
walkExpr e
- | SynExpr.TryWith(tryExpr = e; withCases = clauses; range = r) ->
+ | SynExpr.TryWith(tryExpr = e; withCases = clauses; range = _) ->
List.iter walkClause clauses
walkExpr e
- | SynExpr.IfThenElse(ifExpr = e1; thenExpr = e2; elseExpr = e3; range = r) ->
+ | SynExpr.IfThenElse(ifExpr = e1; thenExpr = e2; elseExpr = e3; range = _) ->
List.iter walkExpr [ e1; e2 ]
e3 |> Option.iter walkExpr
- | SynExpr.LongIdentSet(ident, e, r)
- | SynExpr.DotGet(e, _, ident, r) -> walkExpr e
- | SynExpr.DotSet(e1, idents, e2, r) ->
+ | SynExpr.LongIdentSet(_, e, _)
+ | SynExpr.DotGet(e, _, _, _) -> walkExpr e
+ | SynExpr.DotSet(e1, _, e2, _) ->
walkExpr e1
walkExpr e2
- | SynExpr.DotIndexedGet(e, args, _, r) ->
+ | SynExpr.DotIndexedGet(e, args, _, _) ->
walkExpr e
walkExpr args
- | SynExpr.DotIndexedSet(e1, args, e2, _, _, r) ->
+ | SynExpr.DotIndexedSet(e1, args, e2, _, _, _) ->
walkExpr e1
walkExpr args
walkExpr e2
- | SynExpr.NamedIndexedPropertySet(ident, e1, e2, r) -> List.iter walkExpr [ e1; e2 ]
- | SynExpr.DotNamedIndexedPropertySet(e1, ident, e2, e3, r) -> List.iter walkExpr [ e1; e2; e3 ]
- | SynExpr.JoinIn(e1, _, e2, r) -> List.iter walkExpr [ e1; e2 ]
- | SynExpr.LetOrUseBang(pat = pat; rhs = e1; andBangs = ands; body = e2; range = r) ->
+ | SynExpr.NamedIndexedPropertySet(_, e1, e2, _) -> List.iter walkExpr [ e1; e2 ]
+ | SynExpr.DotNamedIndexedPropertySet(e1, _, e2, e3, _) -> List.iter walkExpr [ e1; e2; e3 ]
+ | SynExpr.JoinIn(e1, _, e2, _) -> List.iter walkExpr [ e1; e2 ]
+ | SynExpr.LetOrUseBang(pat = pat; rhs = e1; andBangs = ands; body = e2; range = _) ->
walkPat pat
walkExpr e1
- for (SynExprAndBang(pat = pat; body = body; range = r)) in ands do
+ for SynExprAndBang(pat = pat; body = body; range = _) in ands do
walkPat pat
walkExpr body
walkExpr e2
- | SynExpr.TraitCall(t, sign, e, r) ->
+ | SynExpr.TraitCall(t, sign, e, _) ->
walkType t
walkMemberSig sign
walkExpr e
- | SynExpr.Const(SynConst.Measure(synMeasure = m), r) -> walkMeasure m
- | SynExpr.Const(_, r) -> ()
- | SynExpr.AnonRecd(isStruct, copyInfo, recordFields, r, trivia) -> ()
- | SynExpr.Sequential(seqPoint, isTrueSeq, expr1, expr2, r) -> ()
- | SynExpr.Ident(_) -> ()
- | SynExpr.LongIdent(isOptional, longDotId, altNameRefCell, r) -> ()
- | SynExpr.Set(_, _, r) -> ()
- | SynExpr.Null(r) -> ()
- | SynExpr.ImplicitZero(r) -> ()
- | SynExpr.MatchBang(range = r) -> ()
- | SynExpr.LibraryOnlyILAssembly(_, _, _, _, r) -> ()
- | SynExpr.LibraryOnlyStaticOptimization(_, _, _, r) -> ()
- | SynExpr.LibraryOnlyUnionCaseFieldGet(expr, longId, _, r) -> ()
- | SynExpr.LibraryOnlyUnionCaseFieldSet(_, longId, _, _, r) -> ()
- | SynExpr.ArbitraryAfterError(debugStr, r) -> ()
- | SynExpr.FromParseError(expr, r) -> ()
- | SynExpr.DiscardAfterMissingQualificationAfterDot(_, _, r) -> ()
- | SynExpr.Fixed(expr, r) -> ()
- | SynExpr.InterpolatedString(parts, kind, r) ->
+ | SynExpr.Const(SynConst.Measure(synMeasure = m), _) -> walkMeasure m
+ | SynExpr.Const _ -> ()
+ | SynExpr.AnonRecd _ -> ()
+ | SynExpr.Sequential _ -> ()
+ | SynExpr.Ident _ -> ()
+ | SynExpr.LongIdent _ -> ()
+ | SynExpr.Set _ -> ()
+ | SynExpr.Null _ -> ()
+ | SynExpr.ImplicitZero _ -> ()
+ | SynExpr.MatchBang(range = _) -> ()
+ | SynExpr.LibraryOnlyILAssembly _ -> ()
+ | SynExpr.LibraryOnlyStaticOptimization _ -> ()
+ | SynExpr.LibraryOnlyUnionCaseFieldGet _ -> ()
+ | SynExpr.LibraryOnlyUnionCaseFieldSet _ -> ()
+ | SynExpr.ArbitraryAfterError _ -> ()
+ | SynExpr.FromParseError _ -> ()
+ | SynExpr.DiscardAfterMissingQualificationAfterDot _ -> ()
+ | SynExpr.Fixed _ -> ()
+ | SynExpr.InterpolatedString(parts, _, _) ->
for part in parts do
walkInterpolatedStringPart part
- | SynExpr.IndexFromEnd(itemExpr, r) -> walkExpr itemExpr
- | SynExpr.IndexRange(e1, _, e2, _, _, r) ->
+ | SynExpr.IndexFromEnd(itemExpr, _) -> walkExpr itemExpr
+ | SynExpr.IndexRange(e1, _, e2, _, _, _) ->
Option.iter walkExpr e1
Option.iter walkExpr e2
| SynExpr.DebugPoint(innerExpr = expr) -> walkExpr expr
- | SynExpr.Dynamic(funcExpr = e1; argExpr = e2; range = range) ->
+ | SynExpr.Dynamic(funcExpr = e1; argExpr = e2; range = _) ->
walkExpr e1
walkExpr e2
- | SynExpr.Typar(t, r) -> walkTypar t
+ | SynExpr.Typar(t, _) -> walkTypar t
| SynExpr.WhileBang(whileExpr = whileExpr; doExpr = doExpr) ->
walkExpr whileExpr
walkExpr doExpr
@@ -386,36 +386,36 @@ module Syntax =
| SynMeasure.Product(measure1 = m1; measure2 = m2) ->
walkMeasure m1
walkMeasure m2
- | SynMeasure.Divide(m1, _, m2, r) ->
+ | SynMeasure.Divide(m1, _, m2, _) ->
Option.iter walkMeasure m1
walkMeasure m2
- | SynMeasure.Named(longIdent, r) -> ()
- | SynMeasure.Seq(ms, r) -> List.iter walkMeasure ms
- | SynMeasure.Power(m, _, _, r) -> walkMeasure m
- | SynMeasure.Var(ty, r) -> walkTypar ty
- | SynMeasure.Paren(m, r) -> walkMeasure m
- | SynMeasure.One(_)
- | SynMeasure.Anon(_) -> ()
+ | SynMeasure.Named _ -> ()
+ | SynMeasure.Seq(ms, _) -> List.iter walkMeasure ms
+ | SynMeasure.Power(m, _, _, _) -> walkMeasure m
+ | SynMeasure.Var(ty, _) -> walkTypar ty
+ | SynMeasure.Paren(m, _) -> walkMeasure m
+ | SynMeasure.One _
+ | SynMeasure.Anon _ -> ()
and walkSimplePat s =
walker.WalkSimplePat s
match s with
- | SynSimplePat.Attrib(pat, AllAttrs attrs, r) ->
+ | SynSimplePat.Attrib(pat, AllAttrs attrs, _) ->
walkSimplePat pat
List.iter walkAttribute attrs
- | SynSimplePat.Typed(pat, t, r) ->
+ | SynSimplePat.Typed(pat, t, _) ->
walkSimplePat pat
walkType t
- | SynSimplePat.Id(ident, altNameRefCell, isCompilerGenerated, isThisVar, isOptArg, r) -> ()
+ | SynSimplePat.Id _ -> ()
- and walkField (SynField(attributes = AllAttrs attrs; fieldType = t; range = r) as s) =
+ and walkField (SynField(attributes = AllAttrs attrs; fieldType = t; range = _) as s) =
walker.WalkField s
List.iter walkAttribute attrs
walkType t
and walkValSig
- (SynValSig(attributes = AllAttrs attrs; synType = t; arity = SynValInfo(argInfos, argInfo); range = r) as s)
+ (SynValSig(attributes = AllAttrs attrs; synType = t; arity = SynValInfo(argInfos, argInfo); range = _) as s)
=
walker.WalkValSig s
List.iter walkAttribute attrs
@@ -429,11 +429,11 @@ module Syntax =
walker.WalkMemberSig s
match s with
- | SynMemberSig.Inherit(t, r)
- | SynMemberSig.Interface(t, r) -> walkType t
- | SynMemberSig.Member(vs, _, r, _) -> walkValSig vs
- | SynMemberSig.ValField(f, r) -> walkField f
- | SynMemberSig.NestedType(SynTypeDefnSig(typeInfo = info; typeRepr = repr; members = memberSigs), r) ->
+ | SynMemberSig.Inherit(t, _)
+ | SynMemberSig.Interface(t, _) -> walkType t
+ | SynMemberSig.Member(vs, _, _, _) -> walkValSig vs
+ | SynMemberSig.ValField(f, _) -> walkField f
+ | SynMemberSig.NestedType(SynTypeDefnSig(typeInfo = info; typeRepr = repr; members = memberSigs), _) ->
walkComponentInfo info
walkTypeDefnSigRepr repr
@@ -443,31 +443,31 @@ module Syntax =
walker.WalkMember s
match s with
- | SynMemberDefn.AbstractSlot(valSig, _, r, _) -> walkValSig valSig
- | SynMemberDefn.Member(binding, r) -> walkBinding binding
- | SynMemberDefn.ImplicitCtor(_, AllAttrs attrs, AllSimplePats pats, _, _, r, _) ->
+ | SynMemberDefn.AbstractSlot(valSig, _, _, _) -> walkValSig valSig
+ | SynMemberDefn.Member(binding, _) -> walkBinding binding
+ | SynMemberDefn.ImplicitCtor(_, AllAttrs attrs, AllSimplePats pats, _, _, _, _) ->
List.iter walkAttribute attrs
List.iter walkSimplePat pats
- | SynMemberDefn.ImplicitInherit(t, e, _, r) ->
+ | SynMemberDefn.ImplicitInherit(t, e, _, _) ->
walkType t
walkExpr e
- | SynMemberDefn.LetBindings(bindings, _, _, r) -> List.iter walkBinding bindings
- | SynMemberDefn.Interface(t, _, members, r) ->
+ | SynMemberDefn.LetBindings(bindings, _, _, _) -> List.iter walkBinding bindings
+ | SynMemberDefn.Interface(t, _, members, _) ->
walkType t
members |> Option.iter (List.iter walkMember)
- | SynMemberDefn.Inherit(t, _, r) -> walkType t
- | SynMemberDefn.ValField(field, r) -> walkField field
- | SynMemberDefn.NestedType(tdef, _, r) -> walkTypeDefn tdef
- | SynMemberDefn.AutoProperty(attributes = AllAttrs attrs; typeOpt = t; synExpr = e; range = r) ->
+ | SynMemberDefn.Inherit(t, _, _) -> walkType t
+ | SynMemberDefn.ValField(field, _) -> walkField field
+ | SynMemberDefn.NestedType(tdef, _, _) -> walkTypeDefn tdef
+ | SynMemberDefn.AutoProperty(attributes = AllAttrs attrs; typeOpt = t; synExpr = e; range = _) ->
List.iter walkAttribute attrs
Option.iter walkType t
walkExpr e
- | SynMemberDefn.Open(longId, r) -> ()
- | SynMemberDefn.GetSetMember(memberDefnForGet = getter; memberDefnForSet = setter; range = range) ->
+ | SynMemberDefn.Open _ -> ()
+ | SynMemberDefn.GetSetMember(memberDefnForGet = getter; memberDefnForSet = setter; range = _) ->
Option.iter walkBinding getter
Option.iter walkBinding setter
- and walkEnumCase (SynEnumCase(attributes = AllAttrs attrs; range = r) as s) =
+ and walkEnumCase (SynEnumCase(attributes = AllAttrs attrs; range = _) as s) =
walker.WalkEnumCase s
List.iter walkAttribute attrs
@@ -478,7 +478,7 @@ module Syntax =
| SynUnionCaseKind.Fields fields -> List.iter walkField fields
| SynUnionCaseKind.FullType(t, _) -> walkType t
- and walkUnionCase (SynUnionCase(attributes = AllAttrs attrs; caseType = t; range = r) as s) =
+ and walkUnionCase (SynUnionCase(attributes = AllAttrs attrs; caseType = t; range = _) as s) =
walker.WalkUnionCase s
List.iter walkAttribute attrs
walkUnionCaseType t
@@ -487,18 +487,18 @@ module Syntax =
walker.WalkTypeDefnSimple s
match s with
- | SynTypeDefnSimpleRepr.Enum(cases, r) -> List.iter walkEnumCase cases
- | SynTypeDefnSimpleRepr.Union(_, cases, r) -> List.iter walkUnionCase cases
- | SynTypeDefnSimpleRepr.Record(_, fields, r) -> List.iter walkField fields
- | SynTypeDefnSimpleRepr.TypeAbbrev(_, t, r) -> walkType t
- | SynTypeDefnSimpleRepr.General(_, _, _, _, _, _, _, r) -> ()
- | SynTypeDefnSimpleRepr.LibraryOnlyILAssembly(_, r) -> ()
- | SynTypeDefnSimpleRepr.None(r) -> ()
- | SynTypeDefnSimpleRepr.Exception(_) -> ()
+ | SynTypeDefnSimpleRepr.Enum(cases, _) -> List.iter walkEnumCase cases
+ | SynTypeDefnSimpleRepr.Union(_, cases, _) -> List.iter walkUnionCase cases
+ | SynTypeDefnSimpleRepr.Record(_, fields, _) -> List.iter walkField fields
+ | SynTypeDefnSimpleRepr.TypeAbbrev(_, t, _) -> walkType t
+ | SynTypeDefnSimpleRepr.General _ -> ()
+ | SynTypeDefnSimpleRepr.LibraryOnlyILAssembly _ -> ()
+ | SynTypeDefnSimpleRepr.None _ -> ()
+ | SynTypeDefnSimpleRepr.Exception _ -> ()
and walkComponentInfo
(SynComponentInfo(
- attributes = AllAttrs attrs; typeParams = typars; constraints = constraints; longId = longIdent; range = r) as s)
+ attributes = AllAttrs attrs; typeParams = typars; constraints = constraints; longId = _; range = _) as s)
=
walker.WalkComponentInfo s
List.iter walkAttribute attrs
@@ -509,8 +509,8 @@ module Syntax =
walker.WalkTypeDefnRepr s
match s with
- | SynTypeDefnRepr.ObjectModel(_, defns, r) -> List.iter walkMember defns
- | SynTypeDefnRepr.Simple(defn, r) -> walkTypeDefnSimple defn
+ | SynTypeDefnRepr.ObjectModel(_, defns, _) -> List.iter walkMember defns
+ | SynTypeDefnRepr.Simple(defn, _) -> walkTypeDefnSimple defn
| SynTypeDefnRepr.Exception _ -> ()
and walkTypeDefnSigRepr s =
@@ -521,7 +521,7 @@ module Syntax =
| SynTypeDefnSigRepr.Simple(defn, _) -> walkTypeDefnSimple defn
| SynTypeDefnSigRepr.Exception _ -> ()
- and walkTypeDefn (SynTypeDefn(info, repr, members, implicitCtor, r, _) as s) =
+ and walkTypeDefn (SynTypeDefn(info, repr, members, implicitCtor, _, _) as s) =
walker.WalkTypeDefn s
walkComponentInfo info
@@ -534,17 +534,17 @@ module Syntax =
match decl with
| SynModuleDecl.NamespaceFragment fragment -> walkSynModuleOrNamespace fragment
- | SynModuleDecl.NestedModule(info, _, modules, _, r, _) ->
+ | SynModuleDecl.NestedModule(info, _, modules, _, _, _) ->
walkComponentInfo info
List.iter walkSynModuleDecl modules
- | SynModuleDecl.Let(_, bindings, r) -> List.iter walkBinding bindings
- | SynModuleDecl.Expr(expr, r) -> walkExpr expr
- | SynModuleDecl.Types(types, r) -> List.iter walkTypeDefn types
- | SynModuleDecl.Attributes(attributes = AllAttrs attrs; range = r) -> List.iter walkAttribute attrs
- | SynModuleDecl.ModuleAbbrev(ident, longId, r) -> ()
- | SynModuleDecl.Exception(_, r) -> ()
- | SynModuleDecl.Open(longDotId, r) -> ()
- | SynModuleDecl.HashDirective(_, r) -> ()
+ | SynModuleDecl.Let(_, bindings, _) -> List.iter walkBinding bindings
+ | SynModuleDecl.Expr(expr, _) -> walkExpr expr
+ | SynModuleDecl.Types(types, _) -> List.iter walkTypeDefn types
+ | SynModuleDecl.Attributes(attributes = AllAttrs attrs; range = _) -> List.iter walkAttribute attrs
+ | SynModuleDecl.ModuleAbbrev _ -> ()
+ | SynModuleDecl.Exception _ -> ()
+ | SynModuleDecl.Open _ -> ()
+ | SynModuleDecl.HashDirective _ -> ()
match input with
@@ -556,10 +556,7 @@ namespace FsAutoComplete
module UntypedAstUtils =
open FSharp.Compiler.Syntax
- open System.Collections.Generic
- open FSharp.Compiler
open FSharp.Compiler.Text
- open FSharp.Control.Reactive.Observable
type Range with
@@ -612,7 +609,7 @@ module FoldingRange =
override _.WalkInterpolatedStringPart i =
match i with
| SynInterpolatedStringPart.FillExpr(qualifiers = Some ident) -> addIfInside ident.idRange
- | SynInterpolatedStringPart.String(s, r) -> addIfInside r
+ | SynInterpolatedStringPart.String(_, r) -> addIfInside r
| _ -> ()
override _.WalkExpr e = addIfInside e.Range
@@ -646,7 +643,7 @@ module FoldingRange =
member _.Ranges = ranges
let getRangesAtPosition input (r: Position) : Range list =
- let walker = new RangeCollectorWalker(r)
+ let walker = RangeCollectorWalker(r)
walkAst walker input
walker.Ranges |> Seq.toList
@@ -683,7 +680,7 @@ module Completion =
| SynInterpolatedStringPart.FillExpr(e, _) -> Range.rangeContainsPos e.Range pos
match part with
- | SynInterpolatedStringPart.String(s, m) when Range.rangeContainsPos m pos && not inRangeOfPrevious ->
+ | SynInterpolatedStringPart.String(_, m) when Range.rangeContainsPos m pos && not inRangeOfPrevious ->
Some Context.StringLiteral
| SynInterpolatedStringPart.String _ -> None
| SynInterpolatedStringPart.FillExpr(e, _) when
diff --git a/src/FsAutoComplete.Logging/FsLibLog.fs b/src/FsAutoComplete.Logging/FsLibLog.fs
index ffcbb5bd6..e45504d8c 100644
--- a/src/FsAutoComplete.Logging/FsLibLog.fs
+++ b/src/FsAutoComplete.Logging/FsLibLog.fs
@@ -878,7 +878,7 @@ module Providers =
true
| None -> loggerGateway.Value.IsEnabled logger microsoftLevel
- member this.OpenMappedContext (key: string) (value: obj) (destructure: bool) : IDisposable =
+ member this.OpenMappedContext (key: string) (value: obj) (_destructure: bool) : IDisposable =
match microsoftLoggerFactory with
| None ->
{ new IDisposable with
diff --git a/src/FsAutoComplete.Logging/FsOpenTelemetry.fs b/src/FsAutoComplete.Logging/FsOpenTelemetry.fs
index b10b300d7..ad467cc68 100644
--- a/src/FsAutoComplete.Logging/FsOpenTelemetry.fs
+++ b/src/FsAutoComplete.Logging/FsOpenTelemetry.fs
@@ -6,7 +6,6 @@ open System
open System.Diagnostics
open System.Runtime.CompilerServices
open System.Collections.Generic
-open Microsoft.FSharp.Quotations.Patterns
// Thanks https://github.com/fsprojects/FSharp.UMX
@@ -604,9 +603,7 @@ type ActivityExtensions =
/// https://github.com/open-telemetry/opentelemetry-specification/blob/main/specification/trace/semantic_conventions/exceptions.md#semantic-conventions-for-exceptions
/// The span to add the error information to
- /// The exception message.
- /// The type of the exception (its fully-qualified class name, if applicable). The dynamic type of the exception should be preferred over the static type in languages that support it.
- /// A stacktrace as a string in the natural representation for the language runtime. The representation is to be determined and documented by each language SIG.
+ /// The exception message.
/// SHOULD be set to true if the exception event is recorded at a point where it is known that the exception is escaping the scope of the span.
[]
static member inline RecordExceptions(span: Activity, e: exn, ?escaped: bool) =
@@ -659,7 +656,7 @@ type ActivitySourceExtensions =
?startTime: DateTimeOffset,
[] ?memberName: string,
[] ?path: string,
- [] ?line: int
+ [] ?line: int
) =
let name_space =
@@ -699,7 +696,6 @@ type ActivitySourceExtensions =
/// This should be used by methods in classes. Creates and starts a new System.Diagnostics.Activity object if there is any listener to the Activity events, returns null otherwise.
/// Provides APIs to create and start System.Diagnostics.Activity objects.
/// The type where the trace is located.
- /// The namespace where this code is located.
/// The System.Diagnostics.ActivityKind
/// The parent System.Diagnostics.ActivityContext object to initialize the created Activity object with.
/// The optional tags list to initialize the created Activity object with.
@@ -721,7 +717,7 @@ type ActivitySourceExtensions =
?startTime: DateTimeOffset,
[] ?memberName: string,
[] ?path: string,
- [] ?line: int
+ [] ?line: int
) =
let name_space = ty.FullName
let name = $"{name_space}.{memberName.Value}"
@@ -763,7 +759,7 @@ type ActivitySourceExtensions =
?startTime: DateTimeOffset,
[] ?memberName: string,
[] ?path: string,
- [] ?line: int
+ [] ?line: int
) =
let ty = typeof<'typAr>
@@ -801,7 +797,7 @@ type ActivitySourceExtensions =
?startTime: DateTimeOffset,
[] ?memberName: string,
[] ?path: string,
- [] ?line: int
+ [] ?line: int
) =
tracer.StartActivityExt(
diff --git a/src/FsAutoComplete/CodeFixes/AddNewKeywordToDisposableConstructorInvocation.fs b/src/FsAutoComplete/CodeFixes/AddNewKeywordToDisposableConstructorInvocation.fs
index 15ae9f31e..ef8e2e471 100644
--- a/src/FsAutoComplete/CodeFixes/AddNewKeywordToDisposableConstructorInvocation.fs
+++ b/src/FsAutoComplete/CodeFixes/AddNewKeywordToDisposableConstructorInvocation.fs
@@ -10,7 +10,7 @@ open FsAutoComplete.LspHelpers
let title = "Add 'new'"
/// a codefix that suggests using the 'new' keyword on IDisposables
-let fix (getRangeText: GetRangeText) =
+let fix =
Run.ifDiagnosticByCode (Set.ofList [ "760" ]) (fun diagnostic codeActionParams ->
AsyncResult.retn
[ { SourceDiagnostic = Some diagnostic
diff --git a/src/FsAutoComplete/CodeFixes/AddNewKeywordToDisposableConstructorInvocation.fsi b/src/FsAutoComplete/CodeFixes/AddNewKeywordToDisposableConstructorInvocation.fsi
index ca573f2f6..390897ea4 100644
--- a/src/FsAutoComplete/CodeFixes/AddNewKeywordToDisposableConstructorInvocation.fsi
+++ b/src/FsAutoComplete/CodeFixes/AddNewKeywordToDisposableConstructorInvocation.fsi
@@ -1,12 +1,8 @@
module FsAutoComplete.CodeFix.AddNewKeywordToDisposableConstructorInvocation
-open FsToolkit.ErrorHandling
-open FsAutoComplete.CodeFix
open FsAutoComplete.CodeFix.Types
open Ionide.LanguageServerProtocol.Types
-open FsAutoComplete
-open FsAutoComplete.LspHelpers
val title: string
/// a codefix that suggests using the 'new' keyword on IDisposables
-val fix: getRangeText: GetRangeText -> (CodeActionParams -> Async>)
+val fix: (CodeActionParams -> Async>)
diff --git a/src/FsAutoComplete/CodeFixes/AdjustConstant.fs b/src/FsAutoComplete/CodeFixes/AdjustConstant.fs
index dccc76640..626ec2beb 100644
--- a/src/FsAutoComplete/CodeFixes/AdjustConstant.fs
+++ b/src/FsAutoComplete/CodeFixes/AdjustConstant.fs
@@ -772,7 +772,6 @@ module private CommonFixes =
let replaceWithNamedConstantFix
doc
(pos: FcsPos)
- (lineStr: String)
(parseAndCheck: ParseAndCheckResults)
(constant: SynConst)
(constantRange: Range)
@@ -832,7 +831,6 @@ module private CommonFixes =
replaceWithNamedConstantFix
doc
pos
- lineStr
parseAndCheck
constant
constantRange
@@ -842,7 +840,6 @@ module private CommonFixes =
replaceWithNamedConstantFix
doc
pos
- lineStr
parseAndCheck
constant
constantRange
@@ -852,7 +849,6 @@ module private CommonFixes =
replaceWithNamedConstantFix
doc
pos
- lineStr
parseAndCheck
constant
constantRange
@@ -871,7 +867,6 @@ module private CommonFixes =
replaceWithNamedConstantFix
doc
pos
- lineStr
parseAndCheck
constant
constantRange
@@ -881,7 +876,6 @@ module private CommonFixes =
replaceWithNamedConstantFix
doc
pos
- lineStr
parseAndCheck
constant
constantRange
@@ -891,7 +885,6 @@ module private CommonFixes =
replaceWithNamedConstantFix
doc
pos
- lineStr
parseAndCheck
constant
constantRange
@@ -904,7 +897,6 @@ module private CommonFixes =
replaceWithNamedConstantFix
doc
pos
- lineStr
parseAndCheck
constant
constantRange
@@ -914,7 +906,6 @@ module private CommonFixes =
replaceWithNamedConstantFix
doc
pos
- lineStr
parseAndCheck
constant
constantRange
@@ -939,7 +930,7 @@ module private CharFix =
mkFix doc data [||]
- let convertToOtherFormatFixes doc (lineStr: String) (constant: CharConstant) =
+ let convertToOtherFormatFixes doc (constant: CharConstant) =
[ let mkFix' title replacement =
let edits =
[| { Range = constant.ValueRange.ToRangeInside constant.Range
@@ -990,7 +981,7 @@ module private CharFix =
let all doc (lineStr: String) (error: bool) (constant: CharConstant) =
[ if not error then
- yield! convertToOtherFormatFixes doc lineStr constant
+ yield! convertToOtherFormatFixes doc constant
if DEBUG then
debugFix doc lineStr constant ]
@@ -1422,7 +1413,6 @@ module private IntFix =
CommonFixes.replaceWithNamedConstantFix
doc
pos
- lineStr
parseAndCheck
constant.Constant
constant.Range
@@ -1433,7 +1423,6 @@ module private IntFix =
CommonFixes.replaceWithNamedConstantFix
doc
pos
- lineStr
parseAndCheck
constant.Constant
constant.Range
@@ -1599,7 +1588,7 @@ let fix (getParseResultsForFile: GetParseResultsForFile) : CodeFix =
asyncResult {
let filePath = codeActionParams.TextDocument.GetFilePath() |> Utils.normalizePath
let fcsPos = protocolPosToPos codeActionParams.Range.Start
- let! (parseAndCheck, lineStr, sourceText) = getParseResultsForFile filePath fcsPos
+ let! (parseAndCheck, lineStr, _sourceText) = getParseResultsForFile filePath fcsPos
match tryFindConstant parseAndCheck.GetAST fcsPos with
| None -> return []
@@ -1659,7 +1648,7 @@ let fix (getParseResultsForFile: GetParseResultsForFile) : CodeFix =
let constant = CharConstant.parse (lineStr, range, constant, char value)
CharFix.all doc lineStr error constant
| IntConstant constant -> IntFix.all doc fcsPos lineStr parseAndCheck error constant
- | SynConst.UserNum(_, _) ->
+ | SynConst.UserNum _ ->
let constant = IntConstant.parse (lineStr, range, constant)
IntFix.all doc fcsPos lineStr parseAndCheck error constant
| SynConst.Single _
diff --git a/src/FsAutoComplete/CodeFixes/ChangeDerefBangToValue.fs b/src/FsAutoComplete/CodeFixes/ChangeDerefBangToValue.fs
index 2762e1c66..efc654224 100644
--- a/src/FsAutoComplete/CodeFixes/ChangeDerefBangToValue.fs
+++ b/src/FsAutoComplete/CodeFixes/ChangeDerefBangToValue.fs
@@ -30,7 +30,7 @@ let private tryGetRangeOfDeref input derefPos =
let title = "Use `.Value` instead of dereference operator"
-let fix (getParseResultsForFile: GetParseResultsForFile) (getLineText: GetLineText) : CodeFix =
+let fix (getParseResultsForFile: GetParseResultsForFile) : CodeFix =
Run.ifDiagnosticByCode (Set.ofList [ "3370" ]) (fun diagnostic codeActionParams ->
asyncResult {
let fileName = codeActionParams.TextDocument.GetFilePath() |> Utils.normalizePath
diff --git a/src/FsAutoComplete/CodeFixes/ChangeDerefBangToValue.fsi b/src/FsAutoComplete/CodeFixes/ChangeDerefBangToValue.fsi
index a557339e1..3f59cd8e6 100644
--- a/src/FsAutoComplete/CodeFixes/ChangeDerefBangToValue.fsi
+++ b/src/FsAutoComplete/CodeFixes/ChangeDerefBangToValue.fsi
@@ -13,5 +13,4 @@ val title: string
val fix:
getParseResultsForFile: GetParseResultsForFile ->
- getLineText: GetLineText ->
(Ionide.LanguageServerProtocol.Types.CodeActionParams -> Async>)
diff --git a/src/FsAutoComplete/CodeFixes/ChangeRefCellDerefToNot.fs b/src/FsAutoComplete/CodeFixes/ChangeRefCellDerefToNot.fs
index 631eeecf2..304b813d0 100644
--- a/src/FsAutoComplete/CodeFixes/ChangeRefCellDerefToNot.fs
+++ b/src/FsAutoComplete/CodeFixes/ChangeRefCellDerefToNot.fs
@@ -15,7 +15,7 @@ let fix (getParseResultsForFile: GetParseResultsForFile) : CodeFix =
let fileName = codeActionParams.TextDocument.GetFilePath() |> Utils.normalizePath
let fcsPos = protocolPosToPos diagnostic.Range.Start
- let! (tyRes, line, lines) = getParseResultsForFile fileName fcsPos
+ let! tyRes, _line, _lines = getParseResultsForFile fileName fcsPos
match tyRes.GetParseResults.TryRangeOfRefCellDereferenceContainingPos fcsPos with
| Some derefRange ->
diff --git a/src/FsAutoComplete/CodeFixes/ChangeTypeOfNameToNameOf.fs b/src/FsAutoComplete/CodeFixes/ChangeTypeOfNameToNameOf.fs
index 53056f0c0..c70b84b8c 100644
--- a/src/FsAutoComplete/CodeFixes/ChangeTypeOfNameToNameOf.fs
+++ b/src/FsAutoComplete/CodeFixes/ChangeTypeOfNameToNameOf.fs
@@ -45,7 +45,7 @@ let fix (getParseResultsForFile: GetParseResultsForFile) : CodeFix =
let pos = protocolPosToPos codeActionParams.Range.Start
- let! (tyRes, line, sourceText) = getParseResultsForFile fileName pos
+ let! tyRes, _line, sourceText = getParseResultsForFile fileName pos
let! results =
tyRes.GetParseResults.TryRangeOfTypeofWithNameAndTypeExpr(pos)
diff --git a/src/FsAutoComplete/CodeFixes/ConvertCSharpLambdaToFSharpLambda.fs b/src/FsAutoComplete/CodeFixes/ConvertCSharpLambdaToFSharpLambda.fs
index b99c35ffe..cfa3221e0 100644
--- a/src/FsAutoComplete/CodeFixes/ConvertCSharpLambdaToFSharpLambda.fs
+++ b/src/FsAutoComplete/CodeFixes/ConvertCSharpLambdaToFSharpLambda.fs
@@ -19,13 +19,12 @@ let private tryRangeOfParenEnclosingOpEqualsGreaterUsage input pos =
let (|InfixAppOfOpEqualsGreater|_|) =
function
- | SynExpr.App(ExprAtomicFlag.NonAtomic,
- false,
- SynExpr.App(ExprAtomicFlag.NonAtomic, true, Ident "op_EqualsGreater", actualParamListExpr, range),
- actualLambdaBodyExpr,
- _) ->
+ | SynExpr.App(
+ flag = ExprAtomicFlag.NonAtomic
+ isInfix = false
+ funcExpr = SynExpr.App(ExprAtomicFlag.NonAtomic, true, Ident "op_EqualsGreater", actualParamListExpr, range)) ->
let opEnd = range.End
- let opStart = Position.mkPos (range.End.Line) (range.End.Column - 2)
+ let opStart = Position.mkPos range.End.Line (range.End.Column - 2)
let opRange = Range.mkRange range.FileName opStart opEnd
let argsRange = actualParamListExpr.Range
@@ -49,7 +48,7 @@ let private tryRangeOfParenEnclosingOpEqualsGreaterUsage input pos =
| _ -> defaultTraverse binding }
)
-let fix (getParseResultsForFile: GetParseResultsForFile) (getLineText: GetLineText) : CodeFix =
+let fix (getParseResultsForFile: GetParseResultsForFile) (_: GetLineText) : CodeFix =
Run.ifDiagnosticByCode
(Set.ofList
[ "39" // undefined value
@@ -59,7 +58,7 @@ let fix (getParseResultsForFile: GetParseResultsForFile) (getLineText: GetLineTe
let fileName = codeActionParams.TextDocument.GetFilePath() |> Utils.normalizePath
let fcsPos = protocolPosToPos diagnostic.Range.Start
- let! (tyRes, _, lines) = getParseResultsForFile fileName fcsPos
+ let! tyRes, _, _ = getParseResultsForFile fileName fcsPos
match tryRangeOfParenEnclosingOpEqualsGreaterUsage tyRes.GetAST fcsPos with
| Some(argsRange, opRange) ->
diff --git a/src/FsAutoComplete/CodeFixes/ConvertInvalidRecordToAnonRecord.fs b/src/FsAutoComplete/CodeFixes/ConvertInvalidRecordToAnonRecord.fs
index 5f9aee0cb..ee9b95d58 100644
--- a/src/FsAutoComplete/CodeFixes/ConvertInvalidRecordToAnonRecord.fs
+++ b/src/FsAutoComplete/CodeFixes/ConvertInvalidRecordToAnonRecord.fs
@@ -16,7 +16,7 @@ let fix (getParseResultsForFile: GetParseResultsForFile) : CodeFix =
let fileName = codeActionParams.TextDocument.GetFilePath() |> Utils.normalizePath
let fcsPos = protocolPosToPos diagnostic.Range.Start
- let! (tyRes, line, lines) = getParseResultsForFile fileName fcsPos
+ let! tyRes, _line, lines = getParseResultsForFile fileName fcsPos
match tyRes.GetParseResults.TryRangeOfRecordExpressionContainingPos fcsPos with
| Some recordExpressionRange ->
diff --git a/src/FsAutoComplete/CodeFixes/ConvertPositionalDUToNamed.fs b/src/FsAutoComplete/CodeFixes/ConvertPositionalDUToNamed.fs
index 5239f34c1..2341b0b07 100644
--- a/src/FsAutoComplete/CodeFixes/ConvertPositionalDUToNamed.fs
+++ b/src/FsAutoComplete/CodeFixes/ConvertPositionalDUToNamed.fs
@@ -124,15 +124,15 @@ let private toPosSeq (range: FSharp.Compiler.Text.Range, text: IFSACSourceText)
let title = "Convert to named patterns"
-let fix (getParseResultsForFile: GetParseResultsForFile) (getRangeText: GetRangeText) : CodeFix =
+let fix (getParseResultsForFile: GetParseResultsForFile) : CodeFix =
fun codeActionParams ->
asyncResult {
let filePath = codeActionParams.TextDocument.GetFilePath() |> Utils.normalizePath
let fcsPos = protocolPosToPos codeActionParams.Range.Start
- let! (parseAndCheck, lineStr, sourceText) = getParseResultsForFile filePath fcsPos
+ let! parseAndCheck, lineStr, sourceText = getParseResultsForFile filePath fcsPos
- let! (duIdent, duFields, parenRange) =
+ let! duIdent, duFields, parenRange =
parseAndCheck.TryGetPositionalUnionPattern(fcsPos)
|> Result.ofOption (fun _ -> "Not inside a DU pattern")
diff --git a/src/FsAutoComplete/CodeFixes/ConvertPositionalDUToNamed.fsi b/src/FsAutoComplete/CodeFixes/ConvertPositionalDUToNamed.fsi
index 6c1cb4e40..8313fa995 100644
--- a/src/FsAutoComplete/CodeFixes/ConvertPositionalDUToNamed.fsi
+++ b/src/FsAutoComplete/CodeFixes/ConvertPositionalDUToNamed.fsi
@@ -25,7 +25,4 @@ open FsAutoComplete.LspHelpers
val title: string
-val fix:
- getParseResultsForFile: GetParseResultsForFile ->
- getRangeText: GetRangeText ->
- (CodeActionParams -> Async>)
+val fix: getParseResultsForFile: GetParseResultsForFile -> (CodeActionParams -> Async>)
diff --git a/src/FsAutoComplete/CodeFixes/ConvertTripleSlashCommentToXmlTaggedDoc.fs b/src/FsAutoComplete/CodeFixes/ConvertTripleSlashCommentToXmlTaggedDoc.fs
index d6ca2bed4..2bb0f4dee 100644
--- a/src/FsAutoComplete/CodeFixes/ConvertTripleSlashCommentToXmlTaggedDoc.fs
+++ b/src/FsAutoComplete/CodeFixes/ConvertTripleSlashCommentToXmlTaggedDoc.fs
@@ -162,7 +162,7 @@ let private wrapInSummary indent comments =
}
|> String.concat ""
-let fix (getParseResultsForFile: GetParseResultsForFile) (getRangeText: GetRangeText) : CodeFix =
+let fix (getParseResultsForFile: GetParseResultsForFile) : CodeFix =
fun codeActionParams ->
asyncResult {
let filePath = codeActionParams.TextDocument.GetFilePath() |> Utils.normalizePath
diff --git a/src/FsAutoComplete/CodeFixes/ConvertTripleSlashCommentToXmlTaggedDoc.fsi b/src/FsAutoComplete/CodeFixes/ConvertTripleSlashCommentToXmlTaggedDoc.fsi
index 1344fee8d..29e81b3c5 100644
--- a/src/FsAutoComplete/CodeFixes/ConvertTripleSlashCommentToXmlTaggedDoc.fsi
+++ b/src/FsAutoComplete/CodeFixes/ConvertTripleSlashCommentToXmlTaggedDoc.fsi
@@ -12,7 +12,4 @@ open System
val title: string
-val fix:
- getParseResultsForFile: GetParseResultsForFile ->
- getRangeText: GetRangeText ->
- (CodeActionParams -> Async>)
+val fix: getParseResultsForFile: GetParseResultsForFile -> (CodeActionParams -> Async>)
diff --git a/src/FsAutoComplete/CodeFixes/GenerateRecordStub.fs b/src/FsAutoComplete/CodeFixes/GenerateRecordStub.fs
index 81d28915a..8f70aec75 100644
--- a/src/FsAutoComplete/CodeFixes/GenerateRecordStub.fs
+++ b/src/FsAutoComplete/CodeFixes/GenerateRecordStub.fs
@@ -11,7 +11,7 @@ let title = "Generate record stub"
/// a codefix that generates member stubs for a record declaration
let fix
(getParseResultsForFile: GetParseResultsForFile)
- (genRecordStub: _ -> _ -> _ -> _ -> Async>)
+ (genRecordStub: _ -> _ -> _ -> Async>)
(getTextReplacements: unit -> Map)
: CodeFix =
fun codeActionParams ->
@@ -20,9 +20,9 @@ let fix
let pos = protocolPosToPos codeActionParams.Range.Start
- let! (tyRes, line, lines) = getParseResultsForFile fileName pos
+ let! tyRes, _line, lines = getParseResultsForFile fileName pos
- match! genRecordStub tyRes pos lines line with
+ match! genRecordStub tyRes pos lines with
| CoreResponse.Res(text, position) ->
let replacements = getTextReplacements ()
diff --git a/src/FsAutoComplete/CodeFixes/GenerateRecordStub.fsi b/src/FsAutoComplete/CodeFixes/GenerateRecordStub.fsi
index a7395e255..c37529c99 100644
--- a/src/FsAutoComplete/CodeFixes/GenerateRecordStub.fsi
+++ b/src/FsAutoComplete/CodeFixes/GenerateRecordStub.fsi
@@ -11,6 +11,6 @@ val title: string
/// a codefix that generates member stubs for a record declaration
val fix:
getParseResultsForFile: GetParseResultsForFile ->
- genRecordStub: (ParseAndCheckResults -> FcsPos -> IFSACSourceText -> string -> Async>) ->
+ genRecordStub: (ParseAndCheckResults -> FcsPos -> IFSACSourceText -> Async>) ->
getTextReplacements: (unit -> Map) ->
(CodeActionParams -> Async>)
diff --git a/src/FsAutoComplete/CodeFixes/GenerateUnionCases.fs b/src/FsAutoComplete/CodeFixes/GenerateUnionCases.fs
index f4707a5db..60f75d947 100644
--- a/src/FsAutoComplete/CodeFixes/GenerateUnionCases.fs
+++ b/src/FsAutoComplete/CodeFixes/GenerateUnionCases.fs
@@ -14,7 +14,7 @@ let title = "Generate union pattern match cases"
let fix
(getFileLines: GetFileLines)
(getParseResultsForFile: GetParseResultsForFile)
- (generateCases: _ -> _ -> _ -> _ -> Async>)
+ (generateCases: _ -> _ -> _ -> Async>)
(getTextReplacements: unit -> Map)
=
Run.ifDiagnosticByCode (Set.ofList [ "25" ]) (fun diagnostic codeActionParams ->
@@ -37,9 +37,9 @@ let fix
let casePosFCS = protocolPosToPos casePos
- let! (tyRes, line, lines) = getParseResultsForFile fileName casePosFCS
+ let! tyRes, _line, lines = getParseResultsForFile fileName casePosFCS
- match! generateCases tyRes casePosFCS lines line |> Async.map Ok with
+ match! generateCases tyRes casePosFCS lines |> Async.map Ok with
| CoreResponse.Res(insertString: string, insertPosition) ->
let range =
{ Start = fcsPosToLsp insertPosition
diff --git a/src/FsAutoComplete/CodeFixes/GenerateUnionCases.fsi b/src/FsAutoComplete/CodeFixes/GenerateUnionCases.fsi
index d9f19f22e..d1340b670 100644
--- a/src/FsAutoComplete/CodeFixes/GenerateUnionCases.fsi
+++ b/src/FsAutoComplete/CodeFixes/GenerateUnionCases.fsi
@@ -14,6 +14,6 @@ val title: string
val fix:
getFileLines: GetFileLines ->
getParseResultsForFile: GetParseResultsForFile ->
- generateCases: (ParseAndCheckResults -> FcsPos -> IFSACSourceText -> string -> Async>) ->
+ generateCases: (ParseAndCheckResults -> FcsPos -> IFSACSourceText -> Async>) ->
getTextReplacements: (unit -> Map) ->
(CodeActionParams -> Async>)
diff --git a/src/FsAutoComplete/CodeFixes/ImplementInterface.fs b/src/FsAutoComplete/CodeFixes/ImplementInterface.fs
index cf0c33b1d..37832168e 100644
--- a/src/FsAutoComplete/CodeFixes/ImplementInterface.fs
+++ b/src/FsAutoComplete/CodeFixes/ImplementInterface.fs
@@ -146,12 +146,7 @@ let private tryFindInsertionData (interfaceData: InterfaceData) (ast: ParsedInpu
| InterfaceData.ObjExpr(_, bindings) -> bindings |> List.tryLast
match lastExistingMember with
- | Some(SynBinding(
- attributes = attributes
- valData = SynValData(memberFlags = memberFlags)
- headPat = headPat
- expr = expr
- trivia = trivia)) ->
+ | Some(SynBinding(attributes = attributes; valData = SynValData _; headPat = headPat; expr = expr; trivia = trivia)) ->
// align with existing member
// insert after last member
@@ -234,11 +229,7 @@ let titleWithTypeAnnotation = "Implement interface"
let titleWithoutTypeAnnotation = "Implement interface without type annotation"
/// codefix that generates members for an interface implementation
-let fix
- (getParseResultsForFile: GetParseResultsForFile)
- (getProjectOptionsForFile: GetProjectOptionsForFile)
- (config: unit -> Config)
- : CodeFix =
+let fix (getParseResultsForFile: GetParseResultsForFile) (config: unit -> Config) : CodeFix =
Run.ifDiagnosticByCode (Set.ofList [ "366" ]) (fun diagnostic codeActionParams ->
asyncResult {
// diagnostic range:
@@ -250,7 +241,7 @@ let fix
let fileName = codeActionParams.TextDocument.GetFilePath() |> Utils.normalizePath
let startPos = protocolPosToPos codeActionParams.Range.Start
- let! (tyRes, line, lines) = getParseResultsForFile fileName startPos
+ let! tyRes, line, lines = getParseResultsForFile fileName startPos
let! interfaceData =
InterfaceStubGenerator.TryFindInterfaceDeclaration startPos tyRes.GetAST
diff --git a/src/FsAutoComplete/CodeFixes/ImplementInterface.fsi b/src/FsAutoComplete/CodeFixes/ImplementInterface.fsi
index edf350807..de3b2bb18 100644
--- a/src/FsAutoComplete/CodeFixes/ImplementInterface.fsi
+++ b/src/FsAutoComplete/CodeFixes/ImplementInterface.fsi
@@ -22,6 +22,5 @@ val titleWithoutTypeAnnotation: string
/// codefix that generates members for an interface implementation
val fix:
getParseResultsForFile: GetParseResultsForFile ->
- getProjectOptionsForFile: GetProjectOptionsForFile ->
config: (unit -> Config) ->
(CodeActionParams -> Async>)
diff --git a/src/FsAutoComplete/CodeFixes/MakeDeclarationMutable.fs b/src/FsAutoComplete/CodeFixes/MakeDeclarationMutable.fs
index e2ff534ff..ff2e2ac36 100644
--- a/src/FsAutoComplete/CodeFixes/MakeDeclarationMutable.fs
+++ b/src/FsAutoComplete/CodeFixes/MakeDeclarationMutable.fs
@@ -19,11 +19,11 @@ let fix
let fileName = codeActionParams.TextDocument.GetFilePath() |> Utils.normalizePath
let fcsPos = protocolPosToPos diagnostic.Range.Start
- let! (tyRes, line, lines) = getParseResultsForFile fileName fcsPos
+ let! tyRes, line, _lines = getParseResultsForFile fileName fcsPos
let! opts = getProjectOptionsForFile fileName
match Lexer.getSymbol fcsPos.Line fcsPos.Column line SymbolLookupKind.Fuzzy opts.OtherOptions with
- | Some symbol ->
+ | Some _symbol ->
match! tyRes.TryFindDeclaration fcsPos line with
| FindDeclarationResult.Range declRange when declRange.FileName = (UMX.untag fileName) ->
let lspRange = fcsRangeToLsp declRange
diff --git a/src/FsAutoComplete/CodeFixes/RemoveUnnecessaryReturnOrYield.fs b/src/FsAutoComplete/CodeFixes/RemoveUnnecessaryReturnOrYield.fs
index 51ee491bf..bce61d06c 100644
--- a/src/FsAutoComplete/CodeFixes/RemoveUnnecessaryReturnOrYield.fs
+++ b/src/FsAutoComplete/CodeFixes/RemoveUnnecessaryReturnOrYield.fs
@@ -16,7 +16,7 @@ let fix (getParseResultsForFile: GetParseResultsForFile) (getLineText: GetLineTe
let fileName = codeActionParams.TextDocument.GetFilePath() |> Utils.normalizePath
let fcsPos = protocolPosToPos diagnostic.Range.Start
- let! (tyRes, line, lines) = getParseResultsForFile fileName fcsPos
+ let! tyRes, _line, lines = getParseResultsForFile fileName fcsPos
let fcsErrorPos = protocolPosToPos diagnostic.Range.Start
match tyRes.GetParseResults.TryRangeOfExprInYieldOrReturn fcsErrorPos with
diff --git a/src/FsAutoComplete/CodeFixes/RemoveUnusedBinding.fs b/src/FsAutoComplete/CodeFixes/RemoveUnusedBinding.fs
index 050d5d4c8..db6bd4d4a 100644
--- a/src/FsAutoComplete/CodeFixes/RemoveUnusedBinding.fs
+++ b/src/FsAutoComplete/CodeFixes/RemoveUnusedBinding.fs
@@ -11,9 +11,6 @@ open FSharp.Compiler.CodeAnalysis
open FSharp.Compiler.Syntax
open FSharp.Compiler.Text
-let posBetween (range: Range) tester =
- Position.posGeq tester range.Start // positions on this one are flipped to simulate Pos.posLte, because that doesn't exist
- && Position.posGeq range.End tester
type private ReplacementRangeResult =
| FullBinding of bindingRange: Range
@@ -84,7 +81,7 @@ let fix (getParseResults: GetParseResultsForFile) : CodeFix =
let fcsRange =
protocolRangeToRange (codeActionParams.TextDocument.GetFilePath()) diagnostic.Range
- let! tyres, line, lines = getParseResults fileName fcsRange.Start
+ let! tyres, _line, lines = getParseResults fileName fcsRange.Start
let! rangeOfBinding =
tyres.GetParseResults.TryRangeOfBindingWithHeadPatternWithPos(fcsRange)
diff --git a/src/FsAutoComplete/CodeFixes/ResolveNamespace.fs b/src/FsAutoComplete/CodeFixes/ResolveNamespace.fs
index 67d080869..35f96252f 100644
--- a/src/FsAutoComplete/CodeFixes/ResolveNamespace.fs
+++ b/src/FsAutoComplete/CodeFixes/ResolveNamespace.fs
@@ -81,7 +81,7 @@ let fix
Title = $"Use %s{qual}"
Kind = FixKind.Fix }
- let openFix (text: ISourceText) file diagnostic (word: string) (ns, name: string, ctx, multiple) : Fix =
+ let openFix (text: ISourceText) file diagnostic (word: string) (ns, name: string, ctx, _multiple) : Fix =
let insertPoint = adjustInsertionPoint text ctx
let docLine = insertPoint - 1
@@ -129,8 +129,8 @@ let fix
let! tyRes, line, lines = getParseResultsForFile filePath pos
match! getNamespaceSuggestions tyRes pos line with
- | CoreResponse.InfoRes msg
- | CoreResponse.ErrorRes msg -> return []
+ | CoreResponse.InfoRes _msg
+ | CoreResponse.ErrorRes _msg -> return []
| CoreResponse.Res(word, opens, qualifiers) ->
let quals =
qualifiers
diff --git a/src/FsAutoComplete/CodeFixes/UseMutationWhenValueIsMutable.fs b/src/FsAutoComplete/CodeFixes/UseMutationWhenValueIsMutable.fs
index 9f1f0b53f..6e295f3b3 100644
--- a/src/FsAutoComplete/CodeFixes/UseMutationWhenValueIsMutable.fs
+++ b/src/FsAutoComplete/CodeFixes/UseMutationWhenValueIsMutable.fs
@@ -17,7 +17,7 @@ let fix (getParseResultsForFile: GetParseResultsForFile) : CodeFix =
let fileName = codeActionParams.TextDocument.GetFilePath() |> Utils.normalizePath
let fcsPos = protocolPosToPos diagnostic.Range.Start
- let! (tyRes, line, lines) = getParseResultsForFile fileName fcsPos
+ let! tyRes, _line, lines = getParseResultsForFile fileName fcsPos
match walkForwardUntilCondition lines diagnostic.Range.Start System.Char.IsWhiteSpace with
| None -> return []
diff --git a/src/FsAutoComplete/CodeFixes/UseTripleQuotedInterpolation.fs b/src/FsAutoComplete/CodeFixes/UseTripleQuotedInterpolation.fs
index cccc67af3..85ed3428a 100644
--- a/src/FsAutoComplete/CodeFixes/UseTripleQuotedInterpolation.fs
+++ b/src/FsAutoComplete/CodeFixes/UseTripleQuotedInterpolation.fs
@@ -10,7 +10,7 @@ open FsAutoComplete.FCSPatches
let title = "Use triple-quoted string interpolation"
/// a codefix that replaces erroring single-quoted interpolations with triple-quoted interpolations
-let fix (getParseResultsForFile: GetParseResultsForFile) (getRangeText: GetRangeText) : CodeFix =
+let fix (getParseResultsForFile: GetParseResultsForFile) : CodeFix =
Run.ifDiagnosticByCode (Set.ofList [ "3373" ]) (fun diagnostic codeActionParams ->
asyncResult {
let pos = protocolPosToPos diagnostic.Range.Start
diff --git a/src/FsAutoComplete/CodeFixes/UseTripleQuotedInterpolation.fsi b/src/FsAutoComplete/CodeFixes/UseTripleQuotedInterpolation.fsi
index 7bcbc5276..cac8bc0cb 100644
--- a/src/FsAutoComplete/CodeFixes/UseTripleQuotedInterpolation.fsi
+++ b/src/FsAutoComplete/CodeFixes/UseTripleQuotedInterpolation.fsi
@@ -10,7 +10,4 @@ open FsAutoComplete.FCSPatches
val title: string
/// a codefix that replaces erroring single-quoted interpolations with triple-quoted interpolations
-val fix:
- getParseResultsForFile: GetParseResultsForFile ->
- getRangeText: GetRangeText ->
- (CodeActionParams -> Async>)
+val fix: getParseResultsForFile: GetParseResultsForFile -> (CodeActionParams -> Async>)
diff --git a/src/FsAutoComplete/CodeFixes/WrapExpressionInParentheses.fs b/src/FsAutoComplete/CodeFixes/WrapExpressionInParentheses.fs
index 4f1debe8f..13d3cb497 100644
--- a/src/FsAutoComplete/CodeFixes/WrapExpressionInParentheses.fs
+++ b/src/FsAutoComplete/CodeFixes/WrapExpressionInParentheses.fs
@@ -8,7 +8,7 @@ open FsAutoComplete
let title = "Wrap expression in parentheses"
/// a codefix that parenthesizes a member expression that needs it
-let fix (getRangeText: GetRangeText) : CodeFix =
+let fix: CodeFix =
Run.ifDiagnosticByCode (Set.ofList [ "597" ]) (fun diagnostic codeActionParams ->
AsyncResult.retn
[ { Title = title
diff --git a/src/FsAutoComplete/CodeFixes/WrapExpressionInParentheses.fsi b/src/FsAutoComplete/CodeFixes/WrapExpressionInParentheses.fsi
index 8b93959cd..4416d5eaf 100644
--- a/src/FsAutoComplete/CodeFixes/WrapExpressionInParentheses.fsi
+++ b/src/FsAutoComplete/CodeFixes/WrapExpressionInParentheses.fsi
@@ -7,4 +7,4 @@ open FsAutoComplete
val title: string
/// a codefix that parenthesizes a member expression that needs it
-val fix: getRangeText: GetRangeText -> (CodeActionParams -> Async>)
+val fix: (CodeActionParams -> Async>)
diff --git a/src/FsAutoComplete/CommandResponse.fs b/src/FsAutoComplete/CommandResponse.fs
index cb63a28a3..6d54bfeee 100644
--- a/src/FsAutoComplete/CommandResponse.fs
+++ b/src/FsAutoComplete/CommandResponse.fs
@@ -91,7 +91,7 @@ module internal ClassificationUtils =
| SemanticClassificationType.Value
| SemanticClassificationType.LocalValue -> "variable"
| SemanticClassificationType.Plaintext -> "text"
- | n -> "unknown"
+ | _n -> "unknown"
module CommandResponse =
open FSharp.Compiler.Text
@@ -460,7 +460,7 @@ module CommandResponse =
match x.Kind with
| Ionide.ProjInfo.InspectSln.SolutionItemKind.Unknown
| Ionide.ProjInfo.InspectSln.SolutionItemKind.Unsupported -> None
- | Ionide.ProjInfo.InspectSln.SolutionItemKind.MsbuildFormat msbuildProj ->
+ | Ionide.ProjInfo.InspectSln.SolutionItemKind.MsbuildFormat _ ->
Some(
WorkspacePeekFoundSolutionItemKind.MsbuildFormat
{ WorkspacePeekFoundSolutionItemKindMsbuildFormat.Configurations = [] }
diff --git a/src/FsAutoComplete/JsonSerializer.fs b/src/FsAutoComplete/JsonSerializer.fs
index ecba41b6c..110369bb3 100644
--- a/src/FsAutoComplete/JsonSerializer.fs
+++ b/src/FsAutoComplete/JsonSerializer.fs
@@ -23,7 +23,7 @@ module private JsonSerializerConverters =
serializer.Serialize(writer, value)
- override x.ReadJson(reader, t, existingValue, serializer) =
+ override x.ReadJson(reader, t, _existingValue, serializer) =
let innerType = t.GetGenericArguments().[0]
let innerType =
diff --git a/src/FsAutoComplete/LspHelpers.fs b/src/FsAutoComplete/LspHelpers.fs
index ac9aafb54..7967d56c6 100644
--- a/src/FsAutoComplete/LspHelpers.fs
+++ b/src/FsAutoComplete/LspHelpers.fs
@@ -305,7 +305,7 @@ module Workspace =
match x.Kind with
| Ionide.ProjInfo.InspectSln.SolutionItemKind.Unknown
| Ionide.ProjInfo.InspectSln.SolutionItemKind.Unsupported -> None
- | Ionide.ProjInfo.InspectSln.SolutionItemKind.MsbuildFormat msbuildProj ->
+ | Ionide.ProjInfo.InspectSln.SolutionItemKind.MsbuildFormat _msbuildProj ->
Some(
WorkspacePeekFoundSolutionItemKind.MsbuildFormat
{ WorkspacePeekFoundSolutionItemKindMsbuildFormat.Configurations = [] }
@@ -359,7 +359,7 @@ module SignatureData =
let args =
parms
- |> List.map (fun group -> group |> List.map (fun (n, t) -> formatType t) |> String.concat " * ")
+ |> List.map (fun group -> group |> List.map (snd >> formatType) |> String.concat " * ")
|> String.concat " -> "
if String.IsNullOrEmpty args then
@@ -531,7 +531,7 @@ module ClassificationUtils =
| SemanticClassificationType.Value
| SemanticClassificationType.LocalValue -> SemanticTokenTypes.Variable, []
| SemanticClassificationType.Plaintext -> SemanticTokenTypes.Text, []
- | unknown -> SemanticTokenTypes.Text, []
+ | _unknown -> SemanticTokenTypes.Text, []
type PlainNotification = { Content: string }
diff --git a/src/FsAutoComplete/LspServers/AdaptiveFSharpLspServer.fs b/src/FsAutoComplete/LspServers/AdaptiveFSharpLspServer.fs
index 574e2cfd7..130161c96 100644
--- a/src/FsAutoComplete/LspServers/AdaptiveFSharpLspServer.fs
+++ b/src/FsAutoComplete/LspServers/AdaptiveFSharpLspServer.fs
@@ -706,7 +706,7 @@ type AdaptiveFSharpLspServer
{ ci with
Detail = Some symbolName
Documentation = Some d }
- | HelpText.Full(name, tip, additionalEdit) ->
+ | HelpText.Full(_name, tip, additionalEdit) ->
let (si, comment) = TipFormatter.formatCompletionItemTip tip
let edits, label =
@@ -753,7 +753,7 @@ type AdaptiveFSharpLspServer
match state.GetAutoCompleteByDeclName sym with
| None -> //Isn't in sync filled cache, we don't have result
CoreResponse.ErrorRes(sprintf "No help text available for symbol '%s'" sym)
- | Some(decl, pos, fn, _, _) -> //Is in sync filled cache, try to get results from async filled caches or calculate if it's not there
+ | Some(decl, _pos, _fn, _, _) -> //Is in sync filled cache, try to get results from async filled caches or calculate if it's not there
let tip = decl.Description
@@ -995,7 +995,7 @@ type AdaptiveFSharpLspServer
// validate name and surround with backticks if necessary
let! newName =
- Commands.adjustRenameSymbolNewName pos lineStr volatileFile.Source tyRes p.NewName
+ Commands.adjustRenameSymbolNewName pos lineStr tyRes p.NewName
|> AsyncResult.mapError (fun msg -> JsonRpc.Error.Create(JsonRpc.ErrorCodes.invalidParams, msg))
// safety check: rename valid?
@@ -1166,9 +1166,9 @@ type AdaptiveFSharpLspServer
tyRes.TryGetSymbolUseAndUsages pos lineStr
|> Result.bimap CoreResponse.Res CoreResponse.InfoRes
with
- | CoreResponse.InfoRes msg -> return None
+ | CoreResponse.InfoRes _msg -> return None
| CoreResponse.ErrorRes msg -> return! LspResult.internalError msg
- | CoreResponse.Res(symbol, uses) ->
+ | CoreResponse.Res(_symbol, uses) ->
return
uses
|> Array.map (fun s ->
@@ -1268,7 +1268,7 @@ type AdaptiveFSharpLspServer
return
decls
|> Array.collect (fun top ->
- getSymbolInformations p.TextDocument.Uri state.GlyphToSymbolKind top (fun s -> true))
+ getSymbolInformations p.TextDocument.Uri state.GlyphToSymbolKind top (fun _s -> true))
|> U2.First
|> Some
| None -> return! LspResult.internalError $"No declarations for {fn}"
@@ -1394,7 +1394,7 @@ type AdaptiveFSharpLspServer
Commands.formatSelection tryGetFileCheckerOptionsWithLines formatSelectionAsync fileName range
- let handlerFormattedRangeDoc (sourceText: IFSACSourceText, formatted: string, range: FormatSelectionRange) =
+ let handlerFormattedRangeDoc (_sourceText: IFSACSourceText, formatted: string, range: FormatSelectionRange) =
let range =
{ Start =
{ Line = range.StartLine - 1
@@ -1992,7 +1992,7 @@ type AdaptiveFSharpLspServer
let! tyRes = state.GetOpenFileTypeCheckResults filePath |> AsyncResult.ofStringErr
- let fcsRange = protocolRangeToRange (UMX.untag filePath) p.Range
+ let _fcsRange = protocolRangeToRange (UMX.untag filePath) p.Range
let! pipelineHints = Commands.inlineValues volatileFile.Source tyRes
@@ -3087,7 +3087,7 @@ type AdaptiveFSharpLspServer
}
member this.CallHierarchyOutgoingCalls
- (arg1: CallHierarchyOutgoingCallsParams)
+ (_arg1: CallHierarchyOutgoingCallsParams)
: AsyncLspResult =
AsyncLspResult.notImplemented
@@ -3109,7 +3109,7 @@ module AdaptiveFSharpLspServer =
None
| _ -> None
- let strategy = StreamJsonRpcTracingStrategy(Tracing.fsacActivitySource)
+ let _strategy = StreamJsonRpcTracingStrategy(Tracing.fsacActivitySource)
let (|Flatten|_|) (e: exn) =
match e with
diff --git a/src/FsAutoComplete/LspServers/AdaptiveServerState.fs b/src/FsAutoComplete/LspServers/AdaptiveServerState.fs
index 324e42b27..f5d829512 100644
--- a/src/FsAutoComplete/LspServers/AdaptiveServerState.fs
+++ b/src/FsAutoComplete/LspServers/AdaptiveServerState.fs
@@ -428,7 +428,7 @@ type AdaptiveState(lspClient: FSharpLspClient, sourceTextFactory: ISourceTextFac
CommandResponse.projectLoading JsonSerializer.writeJson projectFileName
| ProjectResponse.WorkspaceLoad(finished) ->
CommandResponse.workspaceLoad JsonSerializer.writeJson finished
- | ProjectResponse.ProjectChanged(projectFileName) -> failwith "Not Implemented"
+ | ProjectResponse.ProjectChanged(_projectFileName) -> failwith "Not Implemented"
logger.info (Log.setMessage "Workspace Notify {ws}" >> Log.addContextDestructured "ws" ws)
do! ({ Content = ws }: PlainNotification) |> lspClient.NotifyWorkspace
@@ -604,7 +604,7 @@ type AdaptiveState(lspClient: FSharpLspClient, sourceTextFactory: ISourceTextFac
()
}
|> fun work -> Async.StartImmediate(work, ct)
- with :? OperationCanceledException as e ->
+ with :? OperationCanceledException ->
()
@@ -689,7 +689,7 @@ type AdaptiveState(lspClient: FSharpLspClient, sourceTextFactory: ISourceTextFac
|> ASet.mapAtoAMap (UMX.untag >> AdaptiveFile.GetLastWriteTimeUtc)
let cb =
- projChanges.AddCallback(fun old delta ->
+ projChanges.AddCallback(fun _old delta ->
logger.info (
Log.setMessage "Loading projects because of {delta}"
>> Log.addContextDestructured "delta" delta
@@ -985,13 +985,10 @@ type AdaptiveState(lspClient: FSharpLspClient, sourceTextFactory: ISourceTextFac
// ignore if already cancelled
()
-
- let cachedFileContents = cmap, asyncaval> ()
-
let resetCancellationToken filePath =
let adder _ = new CancellationTokenSource()
- let updater key value =
+ let updater _key value =
cancelToken filePath value
new CancellationTokenSource()
@@ -1143,7 +1140,7 @@ type AdaptiveState(lspClient: FSharpLspClient, sourceTextFactory: ISourceTextFac
let allFSharpFilesAndProjectOptions =
let wins =
openFilesToChangesAndProjectOptions
- |> AMap.map (fun k v -> v |> AsyncAVal.mapSync (fun (file, projects) _ -> Some file, projects))
+ |> AMap.map (fun _k v -> v |> AsyncAVal.mapSync (fun (file, projects) _ -> Some file, projects))
let loses =
sourceFileToProjectOptions
@@ -1157,11 +1154,11 @@ type AdaptiveState(lspClient: FSharpLspClient, sourceTextFactory: ISourceTextFac
let allFilesToFSharpProjectOptions =
allFSharpFilesAndProjectOptions
- |> AMapAsync.mapAsyncAVal (fun filePath (file, options) ctok -> AsyncAVal.constant options)
+ |> AMapAsync.mapAsyncAVal (fun _filePath (_file, options) _ctok -> AsyncAVal.constant options)
let allFilesParsed =
allFSharpFilesAndProjectOptions
- |> AMapAsync.mapAsyncAVal (fun filePath (file, options: LoadedProject list) ctok ->
+ |> AMapAsync.mapAsyncAVal (fun _filePath (file, options: LoadedProject list) _ctok ->
asyncAVal {
let! (checker: FSharpCompilerServiceChecker) = checker
@@ -1234,7 +1231,7 @@ type AdaptiveState(lspClient: FSharpLspClient, sourceTextFactory: ISourceTextFac
let autoCompleteNamespaces =
autoCompleteItems
- |> AMap.choose (fun name (d, pos, fn, getLine, ast) ->
+ |> AMap.choose (fun _name (d, pos, _fn, getLine, ast) ->
Commands.calculateNamespaceInsert (fun () -> Some ast) d pos getLine)
@@ -1439,7 +1436,7 @@ type AdaptiveState(lspClient: FSharpLspClient, sourceTextFactory: ISourceTextFac
do!
forceGetOpenFileTypeCheckResults filePath
|> Async.Ignore>
- with e ->
+ with _e ->
()
}
)
@@ -1450,7 +1447,7 @@ type AdaptiveState(lspClient: FSharpLspClient, sourceTextFactory: ISourceTextFac
let allFilesToDeclarations =
allFilesParsed
- |> AMap.map (fun k v -> v |> AsyncAVal.mapOption (fun p _ -> p.GetNavigationItems().Declarations))
+ |> AMap.map (fun _k v -> v |> AsyncAVal.mapOption (fun p _ -> p.GetNavigationItems().Declarations))
let getAllDeclarations () =
async {
@@ -1652,8 +1649,8 @@ type AdaptiveState(lspClient: FSharpLspClient, sourceTextFactory: ISourceTextFac
let tryFindUnionDefinitionFromPos = tryFindUnionDefinitionFromPos codeGenServer
- let getUnionPatternMatchCases tyRes pos sourceText line =
- Commands.getUnionPatternMatchCases tryFindUnionDefinitionFromPos tyRes pos sourceText line
+ let getUnionPatternMatchCases tyRes pos sourceText =
+ Commands.getUnionPatternMatchCases tryFindUnionDefinitionFromPos tyRes pos sourceText
let unionCaseStubReplacements (config) () = Map.ofList [ "$1", config.UnionCaseStubGenerationBody ]
@@ -1668,8 +1665,8 @@ type AdaptiveState(lspClient: FSharpLspClient, sourceTextFactory: ISourceTextFac
let tryFindRecordDefinitionFromPos =
RecordStubGenerator.tryFindRecordDefinitionFromPos codeGenServer
- let getRecordStub tyRes pos sourceText line =
- Commands.getRecordStub (tryFindRecordDefinitionFromPos) tyRes pos sourceText line
+ let getRecordStub tyRes pos sourceText =
+ Commands.getRecordStub (tryFindRecordDefinitionFromPos) tyRes pos sourceText
let getLineText (sourceText: IFSACSourceText) (range: Ionide.LanguageServerProtocol.Types.Range) =
sourceText.GetText(protocolRangeToRange (UMX.untag sourceText.FileName) range)
@@ -1715,7 +1712,7 @@ type AdaptiveState(lspClient: FSharpLspClient, sourceTextFactory: ISourceTextFac
ReplaceWithSuggestion.fix
RemoveRedundantQualifier.fix
Run.ifEnabled (fun _ -> config.UnusedDeclarationsAnalyzer) (RenameUnusedValue.fix tryGetParseResultsForFile)
- AddNewKeywordToDisposableConstructorInvocation.fix getRangeText
+ AddNewKeywordToDisposableConstructorInvocation.fix
Run.ifEnabled
(fun _ -> config.UnionCaseStubGeneration)
(GenerateUnionCases.fix
@@ -1727,10 +1724,7 @@ type AdaptiveState(lspClient: FSharpLspClient, sourceTextFactory: ISourceTextFac
ExternalSystemDiagnostics.analyzers
Run.ifEnabled
(fun _ -> config.InterfaceStubGeneration)
- (ImplementInterface.fix
- tryGetParseResultsForFile
- forceGetFSharpProjectOptions
- (implementInterfaceConfig config))
+ (ImplementInterface.fix tryGetParseResultsForFile (implementInterfaceConfig config))
Run.ifEnabled
(fun _ -> config.RecordStubGeneration)
(GenerateRecordStub.fix tryGetParseResultsForFile getRecordStub (recordStubReplacements config))
@@ -1744,7 +1738,7 @@ type AdaptiveState(lspClient: FSharpLspClient, sourceTextFactory: ISourceTextFac
ChangePrefixNegationToInfixSubtraction.fix forceFindSourceText
ConvertDoubleEqualsToSingleEquals.fix getRangeText
ChangeEqualsInFieldTypeToColon.fix
- WrapExpressionInParentheses.fix getRangeText
+ WrapExpressionInParentheses.fix
ChangeRefCellDerefToNot.fix tryGetParseResultsForFile
ChangeDowncastToUpcast.fix getRangeText
MakeDeclarationMutable.fix tryGetParseResultsForFile forceGetFSharpProjectOptions
@@ -1756,21 +1750,21 @@ type AdaptiveState(lspClient: FSharpLspClient, sourceTextFactory: ISourceTextFac
MakeOuterBindingRecursive.fix tryGetParseResultsForFile getLineText
AddMissingRecKeyword.fix forceFindSourceText getLineText
ConvertBangEqualsToInequality.fix getRangeText
- ChangeDerefBangToValue.fix tryGetParseResultsForFile getLineText
+ ChangeDerefBangToValue.fix tryGetParseResultsForFile
RemoveUnusedBinding.fix tryGetParseResultsForFile
AddTypeToIndeterminateValue.fix tryGetParseResultsForFile forceGetFSharpProjectOptions
ChangeTypeOfNameToNameOf.fix tryGetParseResultsForFile
AddMissingInstanceMember.fix
AddMissingXmlDocumentation.fix tryGetParseResultsForFile
AddExplicitTypeAnnotation.fix tryGetParseResultsForFile
- ConvertPositionalDUToNamed.fix tryGetParseResultsForFile getRangeText
- ConvertTripleSlashCommentToXmlTaggedDoc.fix tryGetParseResultsForFile getRangeText
+ ConvertPositionalDUToNamed.fix tryGetParseResultsForFile
+ ConvertTripleSlashCommentToXmlTaggedDoc.fix tryGetParseResultsForFile
GenerateXmlDocumentation.fix tryGetParseResultsForFile
RemoveRedundantAttributeSuffix.fix tryGetParseResultsForFile
Run.ifEnabled
(fun _ -> config.AddPrivateAccessModifier)
(AddPrivateAccessModifier.fix tryGetParseResultsForFile symbolUseWorkspace)
- UseTripleQuotedInterpolation.fix tryGetParseResultsForFile getRangeText
+ UseTripleQuotedInterpolation.fix tryGetParseResultsForFile
RenameParamToMatchSignature.fix tryGetParseResultsForFile
RemovePatternArgument.fix tryGetParseResultsForFile
ToInterpolatedString.fix tryGetParseResultsForFile getLanguageVersion
diff --git a/src/FsAutoComplete/LspServers/Common.fs b/src/FsAutoComplete/LspServers/Common.fs
index bf8d75d79..318eeda76 100644
--- a/src/FsAutoComplete/LspServers/Common.fs
+++ b/src/FsAutoComplete/LspServers/Common.fs
@@ -102,7 +102,7 @@ type DiagnosticCollection(sendDiagnostics: DocumentUri -> Diagnostic[] -> Async<
>> Log.addContext "message" exn.Message
))
- mailbox.Error.Add(fun exn -> restartAgent uri)
+ mailbox.Error.Add(fun _exn -> restartAgent uri)
mailbox
and getOrAddAgent fileUri =
diff --git a/src/FsAutoComplete/LspServers/FSharpLspClient.fs b/src/FsAutoComplete/LspServers/FSharpLspClient.fs
index 0489a6a61..cbd1e2fcb 100644
--- a/src/FsAutoComplete/LspServers/FSharpLspClient.fs
+++ b/src/FsAutoComplete/LspServers/FSharpLspClient.fs
@@ -132,7 +132,7 @@ type ServerProgressReport(lspClient: FSharpLspClient, ?token: ProgressToken) =
match result with
| Ok() -> canReportProgress <- true
- | Error e -> canReportProgress <- false
+ | Error _e -> canReportProgress <- false
if canReportProgress then
do!
@@ -170,7 +170,7 @@ type ServerProgressReport(lspClient: FSharpLspClient, ?token: ProgressToken) =
}
interface IAsyncDisposable with
- member x.DisposeAsync() = task { do! x.End () (CancellationToken.None) } |> ValueTask
+ member x.DisposeAsync() = task { do! x.End () CancellationToken.None } |> ValueTask
interface IDisposable with
member x.Dispose() = (x :> IAsyncDisposable).DisposeAsync() |> ignore
@@ -285,7 +285,7 @@ type ProgressListener(lspClient: FSharpLspClient, traceNamespace: string array)
if activity.DisplayName |> isOneOf interestingActivities then
match inflightEvents.TryRemove(activity.Id) with
- | true, (old, progressReport) -> do! progressReport.End()
+ | true, (_old, progressReport) -> do! progressReport.End()
| _ -> ()
| _ -> ()
diff --git a/src/FsAutoComplete/Parser.fs b/src/FsAutoComplete/Parser.fs
index 66ce94002..c7b0736b5 100644
--- a/src/FsAutoComplete/Parser.fs
+++ b/src/FsAutoComplete/Parser.fs
@@ -234,7 +234,7 @@ module Parser =
let hasMinLevel (minLevel: LogEventLevel) (e: LogEvent) = e.Level >= minLevel
// will use later when a mapping-style config of { "category": "minLevel" } is established
- let excludeByLevelWhenCategory category level event = isCategory category event || not (hasMinLevel level event)
+ let _excludeByLevelWhenCategory category level event = isCategory category event || not (hasMinLevel level event)
let args = ctx.ParseResult
diff --git a/test/FsAutoComplete.DependencyManager.Dummy/Library.fs b/test/FsAutoComplete.DependencyManager.Dummy/Library.fs
index fddd110fd..af9d4152b 100644
--- a/test/FsAutoComplete.DependencyManager.Dummy/Library.fs
+++ b/test/FsAutoComplete.DependencyManager.Dummy/Library.fs
@@ -4,52 +4,68 @@ open System
/// A marker attribute to tell FCS that this assembly contains a Dependency Manager, or
/// that a class with the attribute is a DependencyManager
-[]
+[]
type DependencyManagerAttribute() =
- inherit Attribute()
+ inherit Attribute()
[]
do ()
/// returned structure from the ResolveDependencies method call.
-type ResolveDependenciesResult (success: bool, stdOut: string array, stdError: string array, resolutions: string seq, sourceFiles: string seq, roots: string seq) =
+type ResolveDependenciesResult
+ (
+ success: bool,
+ stdOut: string array,
+ stdError: string array,
+ resolutions: string seq,
+ sourceFiles: string seq,
+ roots: string seq
+ ) =
- /// Succeeded?
- member __.Success = success
+ /// Succeeded?
+ member __.Success = success
- /// The resolution output log
- member __.StdOut = stdOut
+ /// The resolution output log
+ member __.StdOut = stdOut
- /// The resolution error log (* process stderror *)
- member __.StdError = stdError
+ /// The resolution error log (* process stderror *)
+ member __.StdError = stdError
- /// The resolution paths (will be treated as #r options)
- member __.Resolutions = resolutions
+ /// The resolution paths (will be treated as #r options)
+ member __.Resolutions = resolutions
- /// The source code file paths (will be treated as #load options)
- member __.SourceFiles = sourceFiles
+ /// The source code file paths (will be treated as #load options)
+ member __.SourceFiles = sourceFiles
- /// The roots to package directories (will be treated like #I options)
- member __.Roots = roots
+ /// The roots to package directories (will be treated like #I options)
+ member __.Roots = roots
type ScriptExtension = string
type HashRLines = string seq
type TFM = string
-[]
/// the type _must_ take an optional output directory
-type DummyDependencyManager(outputDir: string option) =
+[]
+type DummyDependencyManager(_outputDir: string option) =
- /// Name of the dependency manager
- member val Name = "Dummy Dependency Manager" with get
+ /// Name of the dependency manager
+ member val Name = "Dummy Dependency Manager" with get
- /// Key that identifies the types of dependencies that this DependencyManager operates on
- member val Key = "dummy" with get
+ /// Key that identifies the types of dependencies that this DependencyManager operates on
+ member val Key = "dummy" with get
- /// Resolve the dependencies, for the given set of arguments, go find the .dll references, scripts and additional include values.
- member _.ResolveDependencies(_scriptExt: ScriptExtension, _includeLines: HashRLines, _tfm: TFM): obj =
- // generally, here we'd parse the includeLines to determine what to do,
- // package those results into a `ResolveDependenciesResult`,
- // and return it boxed as obj.
- // but here we will return a dummy
- ResolveDependenciesResult(true, [|"Skipped processing of any hash-r references"|], [||], Seq.empty, Seq.empty, Seq.empty) :> _
+ /// Resolve the dependencies, for the given set of arguments, go find the .dll references, scripts and additional include values.
+ member _.ResolveDependencies(_scriptExt: ScriptExtension, _includeLines: HashRLines, _tfm: TFM) : obj =
+ // generally, here we'd parse the includeLines to determine what to do,
+ // package those results into a `ResolveDependenciesResult`,
+ // and return it boxed as obj.
+ // but here we will return a dummy
+ ResolveDependenciesResult(
+ true,
+ [| "Skipped processing of any hash-r references" |],
+ [||],
+ Seq.empty,
+ Seq.empty,
+ Seq.empty
+ )
+ :> _
diff --git a/test/FsAutoComplete.Tests.Lsp/CodeFixTests/AdjustConstantTests.fs b/test/FsAutoComplete.Tests.Lsp/CodeFixTests/AdjustConstantTests.fs
index 3df6155ed..fd9ce4a8a 100644
--- a/test/FsAutoComplete.Tests.Lsp/CodeFixTests/AdjustConstantTests.fs
+++ b/test/FsAutoComplete.Tests.Lsp/CodeFixTests/AdjustConstantTests.fs
@@ -27,32 +27,29 @@ module private ConvertIntToOtherBase =
| Base.Octal -> Title.Int.Convert.toOctal
| Base.Binary -> Title.Int.Convert.toBinary
|> CodeFix.withTitle
+
/// empty `expected`: no corresponding fix
- let private checkBase
- doc
- (source: String, cursor: Range)
- base'
- expected
- =
- let name =
+ let private checkBase doc (source: String, cursor: Range) base' expected =
+ let name =
if String.IsNullOrWhiteSpace expected then
$"cannot convert to {base'}"
else
$"can convert to {base'}"
- testCaseAsync name (async {
- let! (doc, diags) = doc
- let expected =
- if String.IsNullOrWhiteSpace expected then
- ExpectedResult.NotApplicable
- else
- ExpectedResult.After expected
- do! checkFixAt
- (doc, diags)
- (source, cursor)
- Diagnostics.acceptAll
- (selectIntCodeFix base')
- expected
- })
+
+ testCaseAsync
+ name
+ (async {
+ let! (doc, diags) = doc
+
+ let expected =
+ if String.IsNullOrWhiteSpace expected then
+ ExpectedResult.NotApplicable
+ else
+ ExpectedResult.After expected
+
+ do! checkFixAt (doc, diags) (source, cursor) Diagnostics.acceptAll (selectIntCodeFix base') expected
+ })
+
/// empty `expectedXXX`: there should be no corresponding Fix
let check
server
@@ -64,16 +61,17 @@ module private ConvertIntToOtherBase =
(expectedBinary: String)
=
let (cursor, source) = Cursor.assertExtractRange beforeWithCursor
- documentTestList name server (Server.createUntitledDocument source) (fun doc -> [
- checkBase doc (source, cursor) Base.Decimal expectedDecimal
- checkBase doc (source, cursor) Base.Hexadecimal expectedHexadecimal
- checkBase doc (source, cursor) Base.Octal expectedOctal
- checkBase doc (source, cursor) Base.Binary expectedBinary
- ])
+
+ documentTestList name server (Server.createUntitledDocument source) (fun doc ->
+ [ checkBase doc (source, cursor) Base.Decimal expectedDecimal
+ checkBase doc (source, cursor) Base.Hexadecimal expectedHexadecimal
+ checkBase doc (source, cursor) Base.Octal expectedOctal
+ checkBase doc (source, cursor) Base.Binary expectedBinary ])
+
/// Checks all combinations of base': Can convert from any base to all others but not to self
- ///
+ ///
/// `template`: without cursor, but with `{number}` marker: number gets inserted here and cursor placed at end
- ///
+ ///
/// empty `valueXXX`: there should be no corresponding Fix
let private checkAll
server
@@ -85,50 +83,59 @@ module private ConvertIntToOtherBase =
(binaryNumber: String)
=
let applyTemplate cursor number =
- let number =
- if cursor then
- number + "$0"
- else
- number
+ let number = if cursor then number + "$0" else number
template.Replace("{number}", number)
- testList name [
- let data = [(Base.Decimal, decimalNumber); (Base.Hexadecimal, hexadecimalNumber); (Base.Octal, octalNumber); (Base.Binary, binaryNumber)]
- let valueOf (base') =
- data
- |> List.find (fun (b,_) -> b = base')
- |> snd
- for (base', value) in data do
- if String.IsNullOrEmpty value then
- ()
- else
- let mkExpected (b) =
- if base' = b || String.IsNullOrEmpty (valueOf b) then
- ""
- else
- applyTemplate false (valueOf b)
- check server $"can convert from {base'}"
- (applyTemplate true value)
- (mkExpected Base.Decimal)
- (mkExpected Base.Hexadecimal)
- (mkExpected Base.Octal)
- (mkExpected Base.Binary)
- ]
+ testList
+ name
+ [ let data =
+ [ (Base.Decimal, decimalNumber)
+ (Base.Hexadecimal, hexadecimalNumber)
+ (Base.Octal, octalNumber)
+ (Base.Binary, binaryNumber) ]
+
+ let valueOf (base') = data |> List.find (fun (b, _) -> b = base') |> snd
+
+ for (base', value) in data do
+ if String.IsNullOrEmpty value then
+ ()
+ else
+ let mkExpected (b) =
+ if base' = b || String.IsNullOrEmpty(valueOf b) then
+ ""
+ else
+ applyTemplate false (valueOf b)
+
+ check
+ server
+ $"can convert from {base'}"
+ (applyTemplate true value)
+ (mkExpected Base.Decimal)
+ (mkExpected Base.Hexadecimal)
+ (mkExpected Base.Octal)
+ (mkExpected Base.Binary) ]
type private Journey =
| JustDestination of string
| JustSource of string
| InOut of string
| Neither
+
module private Journey =
let source =
function
- | JustSource value | InOut value -> Some value
- | JustDestination _ | Neither -> None
+ | JustSource value
+ | InOut value -> Some value
+ | JustDestination _
+ | Neither -> None
+
let destination =
function
- | JustDestination value | InOut value -> Some value
- | JustSource _ | Neither -> None
+ | JustDestination value
+ | InOut value -> Some value
+ | JustSource _
+ | Neither -> None
+
let private checkAllJourneys
server
name
@@ -142,394 +149,414 @@ module private ConvertIntToOtherBase =
let number = if cursor then number + "$0" else number
template.Replace("{number}", number)
- testList name [
- let data = [(Base.Decimal, decimalNumber); (Base.Hexadecimal, hexadecimalNumber); (Base.Octal, octalNumber); (Base.Binary, binaryNumber)]
-
- for (base', j) in data do
- match j |> Journey.source with
- | None -> ()
- | Some value ->
-
- let mkExpected b =
- if base' = b then
- ""
- else
- data
- |> List.find (fst >> (=) b)
- |> snd
- |> Journey.destination
- |> Option.map (applyTemplate false)
- |> Option.defaultValue ""
-
- check server $"can convert from {base'}"
- (applyTemplate true value)
- (mkExpected Base.Decimal)
- (mkExpected Base.Hexadecimal)
- (mkExpected Base.Octal)
- (mkExpected Base.Binary)
- ]
+ testList
+ name
+ [ let data =
+ [ (Base.Decimal, decimalNumber)
+ (Base.Hexadecimal, hexadecimalNumber)
+ (Base.Octal, octalNumber)
+ (Base.Binary, binaryNumber) ]
+
+ for (base', j) in data do
+ match j |> Journey.source with
+ | None -> ()
+ | Some value ->
+
+ let mkExpected b =
+ if base' = b then
+ ""
+ else
+ data
+ |> List.find (fst >> (=) b)
+ |> snd
+ |> Journey.destination
+ |> Option.map (applyTemplate false)
+ |> Option.defaultValue ""
+
+ check
+ server
+ $"can convert from {base'}"
+ (applyTemplate true value)
+ (mkExpected Base.Decimal)
+ (mkExpected Base.Hexadecimal)
+ (mkExpected Base.Octal)
+ (mkExpected Base.Binary) ]
let tests state =
- serverTestList "Convert int-number to other bases" state defaultConfigDto None (fun server -> [
- checkAll server "can convert simple number"
- "let n = {number}"
- "123"
- "0x7B"
- "0o173"
- "0b1111011"
- checkAll server "can convert simple negative number"
- "let n = {number}"
- "-123"
- "-0x7B"
- "-0o173"
- "-0b1111011"
- checkAll server "can convert 0"
- "let n = {number}"
- "0"
- "0x0"
- "0o0"
- "0b0"
- checkAll server "can convert 1"
- "let n = {number}"
- "1"
- "0x1"
- "0o1"
- "0b1"
- checkAll server "can convert -1"
- "let n = {number}"
- "-1"
- "-0x1"
- "-0o1"
- "-0b1"
-
- testList "extrema" [
- // Note regarding negative `MinValue`:
- // Only decimal has `-` sign -- all other should not.
- // While `-0b1000_0000y` is valid -- it has basically two minus signs: one `-` and one minus bit.
- // The Quick Fix removes that `-` sign when converting from decimal to other base.
- // However: it does NOT remove the `-` sign when it already exists for a non-decimal base:
- // `-0b1000_0000y` becomes `-0x80y`, while `0b1000_0000y` becomes `0x80y`
- testList "sbyte" [
- checkAll server "can convert MaxValue"
- "let n = {number} = System.SByte.MaxValue"
- "127y"
- "0x7Fy"
- "0o177y"
- "0b1111111y"
- checkAll server "can convert MinValue (no `-`)"
- "let n = {number} = System.SByte.MinValue"
- "-128y"
- "0x80y"
- "0o200y"
- "0b10000000y"
- checkAllJourneys server "can convert MinValue (keep `-`)"
- "let n = {number} = System.SByte.MinValue"
- (JustDestination "-128y")
- (InOut "-0x80y")
- (InOut "-0o200y")
- (InOut "-0b10000000y")
- ]
- testList "byte" [
- checkAll server "can convert MaxValue"
- "let n = {number} = System.Byte.MaxValue"
- "255uy"
- "0xFFuy"
- "0o377uy"
- "0b11111111uy"
- checkAll server "can convert MinValue"
- "let n = {number} = System.Byte.MinValue"
- "0uy"
- "0x0uy"
- "0o0uy"
- "0b0uy"
- ]
-
- testList "uint64" [
- checkAll server "can convert MaxValue"
- "let n = {number} = System.UInt64.MaxValue"
- "18446744073709551615UL"
- "0xFFFFFFFFFFFFFFFFUL"
- "0o1777777777777777777777UL"
- "0b1111111111111111111111111111111111111111111111111111111111111111UL"
- checkAll server "can convert MinValue"
- "let n = {number} = System.UInt64.MinValue"
- "0UL"
- "0x0UL"
- "0o0UL"
- "0b0UL"
- ]
- testList "int64" [
- // let value = Int64.MinValue in sprintf "\"%i\"\n\"0x%X\"\n\"0o%o\"\n\"0b%B\"" value value value value;;
- checkAll server "can convert MaxValue"
- "let n = {number} = System.Int64.MaxValue"
- "9223372036854775807UL"
- "0x7FFFFFFFFFFFFFFFUL"
- "0o777777777777777777777UL"
- "0b111111111111111111111111111111111111111111111111111111111111111UL"
- checkAll server "can convert MinValue (no `-`)"
- "let n = {number} = System.Int64.MinValue"
- "-9223372036854775808L"
- "0x8000000000000000L"
- "0o1000000000000000000000L"
- "0b1000000000000000000000000000000000000000000000000000000000000000L"
- checkAllJourneys server "can convert MinValue (keep `-`)"
- "let n = {number} = System.Int64.MinValue"
- (JustDestination "-9223372036854775808L")
- (InOut "-0x8000000000000000L")
- (InOut "-0o1000000000000000000000L")
- (InOut "-0b1000000000000000000000000000000000000000000000000000000000000000L")
- ]
-
- testList "int (without suffix)" [
- checkAll server "can convert Int64.MaxValue"
- "let n = {number} = Int32.MaxValue"
- "2147483647"
- "0x7FFFFFFF"
- "0o17777777777"
- "0b1111111111111111111111111111111"
- checkAll server "can convert System.Int32.MinValue"
- "let n = {number} = System.Int32.MinValue"
- "-2147483648"
- "0x80000000"
- "0o20000000000"
- "0b10000000000000000000000000000000"
- checkAllJourneys server "can convert MinValue (keep `-`)"
- "let n = {number} = System.Int32.MinValue"
- (JustDestination "-2147483648")
- (InOut "-0x80000000")
- (InOut "-0o20000000000")
- (InOut "-0b10000000000000000000000000000000")
- ]
- ]
-
- testList "types" [
- let suffixes = [
- ("sbyte", ["y"])
- ("byte", ["uy"])
- ("int16", ["s"])
- ("uint16", ["us"])
- ("int32", [""; "l"])
- ("uint32", ["u"; "ul"])
- ("nativeint", ["n"])
- ("unativeint", ["un"])
- ("int64", ["L"])
- ("uint64", ["UL"])
- ]
-
- for (name, suffixes) in suffixes do
- testList $"can convert {name}" [
- for suffix in suffixes do
- testList $"with suffix {suffix}" [
- checkAll server $"with value 123"
- $"let n = {{number}}{suffix}"
- "123"
- "0x7B"
- "0o173"
- "0b1111011"
-
- if not (name.StartsWith("u", StringComparison.Ordinal)) && name <> "byte" then
- checkAll server $"with value -123"
- $"let n = {{number}}{suffix}"
- "123"
- "0x7B"
- "0o173"
- "0b1111011"
- ]
- ]
-
- testCaseAsync "does not trigger for bigint" <|
- CodeFix.checkNotApplicable server
- "let n = 9999999999999999999999999999$0I"
- Diagnostics.acceptAll
- (selectIntCodeFix Base.Hexadecimal)
- ]
-
- testList "sign shenanigans" [
- testList "keep unnecessary sign" [
- checkAll server "keep + in +123"
- "let n = {number}"
- "+123"
- "+0x7B"
- "+0o173"
- "+0b1111011"
- checkAll server "keep + in +0"
- "let n = {number}"
- "+0"
- "+0x0"
- "+0o0"
- "+0b0"
- checkAll server "keep - in -0"
- "let n = {number}"
- "-0"
- "-0x0"
- "-0o0"
- "-0b0"
- checkAllJourneys server "keep + in +(-123)"
- "let n = {number}"
- (JustDestination "-123")
- (InOut "+0xFFFFFF85")
- (InOut "+0o37777777605")
- (InOut "+0b11111111111111111111111110000101")
- ]
-
- testList "explicit sign and actual sign do not match" [
- testList "keep explicit `-` in positive constant" [
- // Hex/Oct/Bin have sign bit, but can additional have explicit `-` sign
- checkAllJourneys server "keep - in -(-123)"
- "let n = {number}"
- (JustDestination "123")
- (InOut "-0xFFFFFF85")
- (InOut "-0o37777777605")
- (InOut "-0b11111111111111111111111110000101")
- ]
- testList "keep explicit `+` in negative constant" [
- checkAllJourneys server "keep + in +(-123)"
- "let n = {number}"
- (JustDestination "-123")
- (InOut "+0xFFFFFF85")
- (InOut "+0o37777777605")
- (InOut "+0b11111111111111111111111110000101")
- ]
- ]
- ]
-
- testList "locations" [
- check server "can convert in math expression"
- "let n = max (123 + 456$0 / 13 * 17 - 9) (456 - 123)"
- ""
- "let n = max (123 + 0x1C8 / 13 * 17 - 9) (456 - 123)"
- "let n = max (123 + 0o710 / 13 * 17 - 9) (456 - 123)"
- "let n = max (123 + 0b111001000 / 13 * 17 - 9) (456 - 123)"
- check server "can convert inside member"
- """
+ serverTestList "Convert int-number to other bases" state defaultConfigDto None (fun server ->
+ [ checkAll server "can convert simple number" "let n = {number}" "123" "0x7B" "0o173" "0b1111011"
+ checkAll server "can convert simple negative number" "let n = {number}" "-123" "-0x7B" "-0o173" "-0b1111011"
+ checkAll server "can convert 0" "let n = {number}" "0" "0x0" "0o0" "0b0"
+ checkAll server "can convert 1" "let n = {number}" "1" "0x1" "0o1" "0b1"
+ checkAll server "can convert -1" "let n = {number}" "-1" "-0x1" "-0o1" "-0b1"
+
+ testList
+ "extrema"
+ [
+ // Note regarding negative `MinValue`:
+ // Only decimal has `-` sign -- all other should not.
+ // While `-0b1000_0000y` is valid -- it has basically two minus signs: one `-` and one minus bit.
+ // The Quick Fix removes that `-` sign when converting from decimal to other base.
+ // However: it does NOT remove the `-` sign when it already exists for a non-decimal base:
+ // `-0b1000_0000y` becomes `-0x80y`, while `0b1000_0000y` becomes `0x80y`
+ testList
+ "sbyte"
+ [ checkAll
+ server
+ "can convert MaxValue"
+ "let n = {number} = System.SByte.MaxValue"
+ "127y"
+ "0x7Fy"
+ "0o177y"
+ "0b1111111y"
+ checkAll
+ server
+ "can convert MinValue (no `-`)"
+ "let n = {number} = System.SByte.MinValue"
+ "-128y"
+ "0x80y"
+ "0o200y"
+ "0b10000000y"
+ checkAllJourneys
+ server
+ "can convert MinValue (keep `-`)"
+ "let n = {number} = System.SByte.MinValue"
+ (JustDestination "-128y")
+ (InOut "-0x80y")
+ (InOut "-0o200y")
+ (InOut "-0b10000000y") ]
+ testList
+ "byte"
+ [ checkAll
+ server
+ "can convert MaxValue"
+ "let n = {number} = System.Byte.MaxValue"
+ "255uy"
+ "0xFFuy"
+ "0o377uy"
+ "0b11111111uy"
+ checkAll
+ server
+ "can convert MinValue"
+ "let n = {number} = System.Byte.MinValue"
+ "0uy"
+ "0x0uy"
+ "0o0uy"
+ "0b0uy" ]
+
+ testList
+ "uint64"
+ [ checkAll
+ server
+ "can convert MaxValue"
+ "let n = {number} = System.UInt64.MaxValue"
+ "18446744073709551615UL"
+ "0xFFFFFFFFFFFFFFFFUL"
+ "0o1777777777777777777777UL"
+ "0b1111111111111111111111111111111111111111111111111111111111111111UL"
+ checkAll
+ server
+ "can convert MinValue"
+ "let n = {number} = System.UInt64.MinValue"
+ "0UL"
+ "0x0UL"
+ "0o0UL"
+ "0b0UL" ]
+ testList
+ "int64"
+ [
+ // let value = Int64.MinValue in sprintf "\"%i\"\n\"0x%X\"\n\"0o%o\"\n\"0b%B\"" value value value value;;
+ checkAll
+ server
+ "can convert MaxValue"
+ "let n = {number} = System.Int64.MaxValue"
+ "9223372036854775807UL"
+ "0x7FFFFFFFFFFFFFFFUL"
+ "0o777777777777777777777UL"
+ "0b111111111111111111111111111111111111111111111111111111111111111UL"
+ checkAll
+ server
+ "can convert MinValue (no `-`)"
+ "let n = {number} = System.Int64.MinValue"
+ "-9223372036854775808L"
+ "0x8000000000000000L"
+ "0o1000000000000000000000L"
+ "0b1000000000000000000000000000000000000000000000000000000000000000L"
+ checkAllJourneys
+ server
+ "can convert MinValue (keep `-`)"
+ "let n = {number} = System.Int64.MinValue"
+ (JustDestination "-9223372036854775808L")
+ (InOut "-0x8000000000000000L")
+ (InOut "-0o1000000000000000000000L")
+ (InOut "-0b1000000000000000000000000000000000000000000000000000000000000000L") ]
+
+ testList
+ "int (without suffix)"
+ [ checkAll
+ server
+ "can convert Int64.MaxValue"
+ "let n = {number} = Int32.MaxValue"
+ "2147483647"
+ "0x7FFFFFFF"
+ "0o17777777777"
+ "0b1111111111111111111111111111111"
+ checkAll
+ server
+ "can convert System.Int32.MinValue"
+ "let n = {number} = System.Int32.MinValue"
+ "-2147483648"
+ "0x80000000"
+ "0o20000000000"
+ "0b10000000000000000000000000000000"
+ checkAllJourneys
+ server
+ "can convert MinValue (keep `-`)"
+ "let n = {number} = System.Int32.MinValue"
+ (JustDestination "-2147483648")
+ (InOut "-0x80000000")
+ (InOut "-0o20000000000")
+ (InOut "-0b10000000000000000000000000000000") ] ]
+
+ testList
+ "types"
+ [ let suffixes =
+ [ ("sbyte", [ "y" ])
+ ("byte", [ "uy" ])
+ ("int16", [ "s" ])
+ ("uint16", [ "us" ])
+ ("int32", [ ""; "l" ])
+ ("uint32", [ "u"; "ul" ])
+ ("nativeint", [ "n" ])
+ ("unativeint", [ "un" ])
+ ("int64", [ "L" ])
+ ("uint64", [ "UL" ]) ]
+
+ for (name, suffixes) in suffixes do
+ testList
+ $"can convert {name}"
+ [ for suffix in suffixes do
+ testList
+ $"with suffix {suffix}"
+ [ checkAll
+ server
+ $"with value 123"
+ $"let n = {{number}}{suffix}"
+ "123"
+ "0x7B"
+ "0o173"
+ "0b1111011"
+
+ if not (name.StartsWith("u", StringComparison.Ordinal)) && name <> "byte" then
+ checkAll
+ server
+ $"with value -123"
+ $"let n = {{number}}{suffix}"
+ "123"
+ "0x7B"
+ "0o173"
+ "0b1111011" ] ]
+
+ testCaseAsync "does not trigger for bigint"
+ <| CodeFix.checkNotApplicable
+ server
+ "let n = 9999999999999999999999999999$0I"
+ Diagnostics.acceptAll
+ (selectIntCodeFix Base.Hexadecimal) ]
+
+ testList
+ "sign shenanigans"
+ [ testList
+ "keep unnecessary sign"
+ [ checkAll server "keep + in +123" "let n = {number}" "+123" "+0x7B" "+0o173" "+0b1111011"
+ checkAll server "keep + in +0" "let n = {number}" "+0" "+0x0" "+0o0" "+0b0"
+ checkAll server "keep - in -0" "let n = {number}" "-0" "-0x0" "-0o0" "-0b0"
+ checkAllJourneys
+ server
+ "keep + in +(-123)"
+ "let n = {number}"
+ (JustDestination "-123")
+ (InOut "+0xFFFFFF85")
+ (InOut "+0o37777777605")
+ (InOut "+0b11111111111111111111111110000101") ]
+
+ testList
+ "explicit sign and actual sign do not match"
+ [ testList
+ "keep explicit `-` in positive constant"
+ [
+ // Hex/Oct/Bin have sign bit, but can additional have explicit `-` sign
+ checkAllJourneys
+ server
+ "keep - in -(-123)"
+ "let n = {number}"
+ (JustDestination "123")
+ (InOut "-0xFFFFFF85")
+ (InOut "-0o37777777605")
+ (InOut "-0b11111111111111111111111110000101") ]
+ testList
+ "keep explicit `+` in negative constant"
+ [ checkAllJourneys
+ server
+ "keep + in +(-123)"
+ "let n = {number}"
+ (JustDestination "-123")
+ (InOut "+0xFFFFFF85")
+ (InOut "+0o37777777605")
+ (InOut "+0b11111111111111111111111110000101") ] ] ]
+
+ testList
+ "locations"
+ [ check
+ server
+ "can convert in math expression"
+ "let n = max (123 + 456$0 / 13 * 17 - 9) (456 - 123)"
+ ""
+ "let n = max (123 + 0x1C8 / 13 * 17 - 9) (456 - 123)"
+ "let n = max (123 + 0o710 / 13 * 17 - 9) (456 - 123)"
+ "let n = max (123 + 0b111001000 / 13 * 17 - 9) (456 - 123)"
+ check
+ server
+ "can convert inside member"
+ """
type T() =
member _.DoStuff(arg: int) =
arg + 3 * 456$0 / 3
"""
- ""
- """
+ ""
+ """
type T() =
member _.DoStuff(arg: int) =
arg + 3 * 0x1C8 / 3
"""
- """
+ """
type T() =
member _.DoStuff(arg: int) =
arg + 3 * 0o710 / 3
"""
- """
+ """
type T() =
member _.DoStuff(arg: int) =
arg + 3 * 0b111001000 / 3
"""
- testList "can convert in enum" [
- check server "just value"
- """
+ testList
+ "can convert in enum"
+ [ check
+ server
+ "just value"
+ """
type MyEnum =
| Alpha = 123
| Beta = 456$0
| Gamma = 789
"""
- ""
- """
+ ""
+ """
type MyEnum =
| Alpha = 123
| Beta = 0x1C8
| Gamma = 789
"""
- """
+ """
type MyEnum =
| Alpha = 123
| Beta = 0o710
| Gamma = 789
"""
- """
+ """
type MyEnum =
| Alpha = 123
| Beta = 0b111001000
| Gamma = 789
"""
- check server "in parens"
- """
+ check
+ server
+ "in parens"
+ """
type MyEnum =
| Alpha = 123
| Beta = (456$0)
| Gamma = 789
"""
- ""
- """
+ ""
+ """
type MyEnum =
| Alpha = 123
| Beta = (0x1C8)
| Gamma = 789
"""
- """
+ """
type MyEnum =
| Alpha = 123
| Beta = (0o710)
| Gamma = 789
"""
- """
+ """
type MyEnum =
| Alpha = 123
| Beta = (0b111001000)
| Gamma = 789
"""
- check server "in app (lhs)"
- """
+ check
+ server
+ "in app (lhs)"
+ """
type MyEnum =
| Alpha = 123
| Beta = (456$0 >>> 2)
| Gamma = 789
"""
- ""
- """
+ ""
+ """
type MyEnum =
| Alpha = 123
| Beta = (0x1C8 >>> 2)
| Gamma = 789
"""
- """
+ """
type MyEnum =
| Alpha = 123
| Beta = (0o710 >>> 2)
| Gamma = 789
"""
- """
+ """
type MyEnum =
| Alpha = 123
| Beta = (0b111001000 >>> 2)
| Gamma = 789
"""
- check server "in app (rhs)"
- """
+ check
+ server
+ "in app (rhs)"
+ """
type MyEnum =
| Alpha = 123
| Beta = (1 <<< 456$0)
| Gamma = 789
"""
- ""
- """
+ ""
+ """
type MyEnum =
| Alpha = 123
| Beta = (1 <<< 0x1C8)
| Gamma = 789
"""
- """
+ """
type MyEnum =
| Alpha = 123
| Beta = (1 <<< 0o710)
| Gamma = 789
"""
- """
+ """
type MyEnum =
| Alpha = 123
| Beta = (1 <<< 0b111001000)
| Gamma = 789
- """
- ]
- check server "can convert in pattern"
- """
+ """ ]
+ check
+ server
+ "can convert in pattern"
+ """
let f arg =
match arg with
| 123 -> 1
@@ -537,8 +564,8 @@ module private ConvertIntToOtherBase =
| 789 -> 3
| _ -> -1
"""
- ""
- """
+ ""
+ """
let f arg =
match arg with
| 123 -> 1
@@ -546,7 +573,7 @@ module private ConvertIntToOtherBase =
| 789 -> 3
| _ -> -1
"""
- """
+ """
let f arg =
match arg with
| 123 -> 1
@@ -554,7 +581,7 @@ module private ConvertIntToOtherBase =
| 789 -> 3
| _ -> -1
"""
- """
+ """
let f arg =
match arg with
| 123 -> 1
@@ -562,258 +589,317 @@ module private ConvertIntToOtherBase =
| 789 -> 3
| _ -> -1
"""
- check server "can convert with measure"
- """
- [] type km
+ check
+ server
+ "can convert with measure"
+ """
+ [] type km
let n = 456$0
"""
- ""
- """
- [] type km
+ ""
+ """
+ [] type km
let n = 0x1C8
"""
- """
- [] type km
+ """
+ [] type km
let n = 0o710
"""
- """
- [] type km
+ """
+ [] type km
let n = 0b111001000
- """
- ]
-
- checkAllJourneys server "does not trigger for invalid int"
- // Value for invalid `SynConst` is always `0` -> cannot convert
- "let n = {number}"
- (JustSource "1099511627775")
- (JustSource "0xFFFFFFFFFF")
- (JustSource "0o17777777777777")
- (JustSource "0b1111111111111111111111111111111111111111")
-
- testCaseAsync "does not trigger on comment after constant" <|
- CodeFix.checkNotApplicable server
+ """ ]
+
+ checkAllJourneys
+ server
+ "does not trigger for invalid int"
+ // Value for invalid `SynConst` is always `0` -> cannot convert
+ "let n = {number}"
+ (JustSource "1099511627775")
+ (JustSource "0xFFFFFFFFFF")
+ (JustSource "0o17777777777777")
+ (JustSource "0b1111111111111111111111111111111111111111")
+
+ testCaseAsync "does not trigger on comment after constant"
+ <| CodeFix.checkNotApplicable
+ server
"let n = 123 // some$0 comment"
Diagnostics.acceptAll
(selectIntCodeFix Base.Hexadecimal)
- testList "different upper-lower-cases in bases" [
- testList "hexadecimal" [
- testCaseAsync "0x" <|
- CodeFix.checkApplicable server
- "let n = 0x123$0"
- Diagnostics.acceptAll
- (selectIntCodeFix Base.Decimal)
- testCaseAsync "0X" <|
- CodeFix.checkApplicable server
- "let n = 0X123$0"
- Diagnostics.acceptAll
- (selectIntCodeFix Base.Decimal)
- ]
- testList "octal" [
- testCaseAsync "0o" <|
- CodeFix.checkApplicable server
- "let n = 0o443$0"
- Diagnostics.acceptAll
- (selectIntCodeFix Base.Decimal)
- testCaseAsync "0O" <|
- CodeFix.checkApplicable server
- "let n = 0O443$0"
- Diagnostics.acceptAll
- (selectIntCodeFix Base.Decimal)
- ]
- testList "binary" [
- testCaseAsync "0b" <|
- CodeFix.checkApplicable server
- "let n = 0b100100011$0"
- Diagnostics.acceptAll
- (selectIntCodeFix Base.Decimal)
- testCaseAsync "0B" <|
- CodeFix.checkApplicable server
- "let n = 0B100100011$0"
- Diagnostics.acceptAll
- (selectIntCodeFix Base.Decimal)
- ]
- ]
- ])
+ testList
+ "different upper-lower-cases in bases"
+ [ testList
+ "hexadecimal"
+ [ testCaseAsync "0x"
+ <| CodeFix.checkApplicable
+ server
+ "let n = 0x123$0"
+ Diagnostics.acceptAll
+ (selectIntCodeFix Base.Decimal)
+ testCaseAsync "0X"
+ <| CodeFix.checkApplicable
+ server
+ "let n = 0X123$0"
+ Diagnostics.acceptAll
+ (selectIntCodeFix Base.Decimal) ]
+ testList
+ "octal"
+ [ testCaseAsync "0o"
+ <| CodeFix.checkApplicable
+ server
+ "let n = 0o443$0"
+ Diagnostics.acceptAll
+ (selectIntCodeFix Base.Decimal)
+ testCaseAsync "0O"
+ <| CodeFix.checkApplicable
+ server
+ "let n = 0O443$0"
+ Diagnostics.acceptAll
+ (selectIntCodeFix Base.Decimal) ]
+ testList
+ "binary"
+ [ testCaseAsync "0b"
+ <| CodeFix.checkApplicable
+ server
+ "let n = 0b100100011$0"
+ Diagnostics.acceptAll
+ (selectIntCodeFix Base.Decimal)
+ testCaseAsync "0B"
+ <| CodeFix.checkApplicable
+ server
+ "let n = 0B100100011$0"
+ Diagnostics.acceptAll
+ (selectIntCodeFix Base.Decimal) ] ] ])
module Float =
let tests state =
- serverTestList "Convert float-number in Hex/Oct/Bin to other bases" state defaultConfigDto None (fun server -> [
- // Note: No Decimal: cannot be represented as Hex/Oct/Bin
+ serverTestList "Convert float-number in Hex/Oct/Bin to other bases" state defaultConfigDto None (fun server ->
+ [
+ // Note: No Decimal: cannot be represented as Hex/Oct/Bin
+
+ let checkAll server name template (hexadecimalNumber: String) (octalNumber: String) (binaryNumber: String) =
+ checkAllJourneys
+ server
+ name
+ template
+ (Neither)
+ (InOut hexadecimalNumber)
+ (InOut octalNumber)
+ (InOut binaryNumber)
+
+ testList
+ "can convert pi"
+ [
+ // let value = Math.PI in let bits = BitConverter.DoubleToUInt64Bits(value) in [ $"0x%X{bits}LF"; $"0o%o{bits}LF"; $"0b%B{bits}LF" ];;
+ checkAll
+ server
+ "float"
+ "let n = {number}"
+ "0x400921FB54442D18LF"
+ "0o400111037552421026430LF"
+ "0b100000000001001001000011111101101010100010001000010110100011000LF"
+ // let value = MathF.PI in let bits = BitConverter.SingleToUInt32Bits(value) in [ $"0x%X{bits}lf"; $"0o%o{bits}lf"; $"0b%B{bits}lf" ];;
+ checkAll
+ server
+ "float32"
+ "let n = {number}"
+ "0x40490FDBlf"
+ "0o10022207733lf"
+ "0b1000000010010010000111111011011lf" ]
+
+ testList
+ "can convert 0"
+ [ checkAll server "float" "let n = {number}" "0x0LF" "0o0LF" "0b0LF"
+ checkAll server "float32" "let n = {number}" "0x0lf" "0o0lf" "0b0lf" ]
+
+ testList
+ "can convert -pi"
+ [ checkAll
+ server
+ "float"
+ "let n = {number}"
+ "0xC00921FB54442D18LF"
+ "0o1400111037552421026430LF"
+ "0b1100000000001001001000011111101101010100010001000010110100011000LF"
+ checkAll
+ server
+ "float32"
+ "let n = {number}"
+ "0xC0490FDBlf"
+ "0o30022207733lf"
+ "0b11000000010010010000111111011011lf"
+
+ testList
+ "keep existing `-`"
+ [ checkAll
+ server
+ "float"
+ "let n = {number}"
+ "-0x400921FB54442D18LF"
+ "-0o400111037552421026430LF"
+ "-0b100000000001001001000011111101101010100010001000010110100011000LF"
+ checkAll
+ server
+ "float32"
+ "let n = {number}"
+ "-0x40490FDBlf"
+ "-0o10022207733lf"
+ "-0b1000000010010010000111111011011lf" ] ]
+
+ testList
+ "can convert MaxValue"
+ [ checkAll
+ server
+ "float"
+ "let n = {number}"
+ "0x7FEFFFFFFFFFFFFFLF"
+ "0o777577777777777777777LF"
+ "0b111111111101111111111111111111111111111111111111111111111111111LF"
+ checkAll
+ server
+ "float32"
+ "let n = {number}"
+ "0x7F7FFFFFlf"
+ "0o17737777777lf"
+ "0b1111111011111111111111111111111lf" ]
+
+ testList
+ "can convert MinValue"
+ [ checkAll
+ server
+ "float"
+ "let n = {number}"
+ "0xFFEFFFFFFFFFFFFFLF"
+ "0o1777577777777777777777LF"
+ "0b1111111111101111111111111111111111111111111111111111111111111111LF"
+ checkAll
+ server
+ "float32"
+ "let n = {number}"
+ "0xFF7FFFFFlf"
+ "0o37737777777lf"
+ "0b11111111011111111111111111111111lf"
+
+ testList
+ "keep existing `-`"
+ [
+ // Note: unlike int numbers: float is symmetric: `MinValue = - MaxValue` -> just negative bit changed
+ checkAll
+ server
+ "float"
+ "let n = {number}"
+ "-0x7FEFFFFFFFFFFFFFLF"
+ "-0o777577777777777777777LF"
+ "-0b111111111101111111111111111111111111111111111111111111111111111LF"
+ checkAll
+ server
+ "float32"
+ "let n = {number}"
+ "-0x7F7FFFFFlf"
+ "-0o17737777777lf"
+ "-0b1111111011111111111111111111111lf" ] ]
+
+ testList
+ "can convert nan"
+ [
+ // `nan`, `nanf`
+ checkAll
+ server
+ "float - nan"
+ "let n = {number}"
+ "0xFFF8000000000000LF"
+ "0o1777700000000000000000LF"
+ "0b1111111111111000000000000000000000000000000000000000000000000000LF"
+ checkAll
+ server
+ "float32 - nanf"
+ "let n = {number}"
+ "0xFFC00000lf"
+ "0o37760000000lf"
+ "0b11111111110000000000000000000000lf"
+
+ // `nan` that are different from default F# `nan` (-> tests above)
+ checkAll
+ server
+ "float - different nan"
+ "let n = {number}"
+ "0xFFF800C257000000LF"
+ "0o1777700014112700000000LF"
+ "0b1111111111111000000000001100001001010111000000000000000000000000LF"
+ checkAll
+ server
+ "float32 -- different nan"
+ "let n = {number}"
+ "0xFFC00000lf"
+ "0o37760000000lf"
+ "0b11111111110000000000000000000000lf"
- let checkAll
- server
- name template
- (hexadecimalNumber: String)
- (octalNumber: String)
- (binaryNumber: String)
- =
- checkAllJourneys server name template
- (Neither)
- (InOut hexadecimalNumber)
- (InOut octalNumber)
- (InOut binaryNumber)
-
- testList "can convert pi" [
- // let value = Math.PI in let bits = BitConverter.DoubleToUInt64Bits(value) in [ $"0x%X{bits}LF"; $"0o%o{bits}LF"; $"0b%B{bits}LF" ];;
- checkAll server "float"
- "let n = {number}"
- "0x400921FB54442D18LF"
- "0o400111037552421026430LF"
- "0b100000000001001001000011111101101010100010001000010110100011000LF"
- // let value = MathF.PI in let bits = BitConverter.SingleToUInt32Bits(value) in [ $"0x%X{bits}lf"; $"0o%o{bits}lf"; $"0b%B{bits}lf" ];;
- checkAll server "float32"
- "let n = {number}"
- "0x40490FDBlf"
- "0o10022207733lf"
- "0b1000000010010010000111111011011lf"
- ]
- testList "can convert 0" [
- checkAll server "float"
- "let n = {number}"
- "0x0LF"
- "0o0LF"
- "0b0LF"
- checkAll server "float32"
- "let n = {number}"
- "0x0lf"
- "0o0lf"
- "0b0lf"
- ]
- testList "can convert -pi" [
- checkAll server "float"
- "let n = {number}"
- "0xC00921FB54442D18LF"
- "0o1400111037552421026430LF"
- "0b1100000000001001001000011111101101010100010001000010110100011000LF"
- checkAll server "float32"
- "let n = {number}"
- "0xC0490FDBlf"
- "0o30022207733lf"
- "0b11000000010010010000111111011011lf"
-
- testList "keep existing `-`" [
- checkAll server "float"
- "let n = {number}"
- "-0x400921FB54442D18LF"
- "-0o400111037552421026430LF"
- "-0b100000000001001001000011111101101010100010001000010110100011000LF"
- checkAll server "float32"
- "let n = {number}"
- "-0x40490FDBlf"
- "-0o10022207733lf"
- "-0b1000000010010010000111111011011lf"
- ]
- ]
-
- testList "can convert MaxValue" [
- checkAll server "float"
- "let n = {number}"
- "0x7FEFFFFFFFFFFFFFLF"
- "0o777577777777777777777LF"
- "0b111111111101111111111111111111111111111111111111111111111111111LF"
- checkAll server "float32"
- "let n = {number}"
- "0x7F7FFFFFlf"
- "0o17737777777lf"
- "0b1111111011111111111111111111111lf"
- ]
- testList "can convert MinValue" [
- checkAll server "float"
- "let n = {number}"
- "0xFFEFFFFFFFFFFFFFLF"
- "0o1777577777777777777777LF"
- "0b1111111111101111111111111111111111111111111111111111111111111111LF"
- checkAll server "float32"
- "let n = {number}"
- "0xFF7FFFFFlf"
- "0o37737777777lf"
- "0b11111111011111111111111111111111lf"
-
- testList "keep existing `-`" [
- // Note: unlike int numbers: float is symmetric: `MinValue = - MaxValue` -> just negative bit changed
- checkAll server "float"
- "let n = {number}"
- "-0x7FEFFFFFFFFFFFFFLF"
- "-0o777577777777777777777LF"
- "-0b111111111101111111111111111111111111111111111111111111111111111LF"
- checkAll server "float32"
- "let n = {number}"
- "-0x7F7FFFFFlf"
- "-0o17737777777lf"
- "-0b1111111011111111111111111111111lf"
- ]
- ]
-
- testList "can convert nan" [
- // `nan`, `nanf`
- checkAll server "float - nan"
- "let n = {number}"
- "0xFFF8000000000000LF"
- "0o1777700000000000000000LF"
- "0b1111111111111000000000000000000000000000000000000000000000000000LF"
- checkAll server "float32 - nanf"
- "let n = {number}"
- "0xFFC00000lf"
- "0o37760000000lf"
- "0b11111111110000000000000000000000lf"
-
- // `nan` that are different from default F# `nan` (-> tests above)
- checkAll server "float - different nan"
- "let n = {number}"
- "0xFFF800C257000000LF"
- "0o1777700014112700000000LF"
- "0b1111111111111000000000001100001001010111000000000000000000000000LF"
- checkAll server "float32 -- different nan"
- "let n = {number}"
- "0xFFC00000lf"
- "0o37760000000lf"
- "0b11111111110000000000000000000000lf"
-
- ]
- testList "can convert infinity" [
- testList "+" [
- checkAll server "float"
- "let n = {number}"
- "0x7FF0000000000000LF"
- "0o777600000000000000000LF"
- "0b111111111110000000000000000000000000000000000000000000000000000LF"
- checkAll server "float32"
- "let n = {number}"
- "0x7F800000lf"
- "0o17740000000lf"
- "0b1111111100000000000000000000000lf"
- ]
- testList "-" [
- checkAll server "float"
- "let n = {number}"
- "0xFFF0000000000000LF"
- "0o1777600000000000000000LF"
- "0b1111111111110000000000000000000000000000000000000000000000000000LF"
- checkAll server "float32"
- "let n = {number}"
- "0xFF800000lf"
- "0o37740000000lf"
- "0b11111111100000000000000000000000lf"
- ]
- ]
- ])
+ ]
+
+ testList
+ "can convert infinity"
+ [ testList
+ "+"
+ [ checkAll
+ server
+ "float"
+ "let n = {number}"
+ "0x7FF0000000000000LF"
+ "0o777600000000000000000LF"
+ "0b111111111110000000000000000000000000000000000000000000000000000LF"
+ checkAll
+ server
+ "float32"
+ "let n = {number}"
+ "0x7F800000lf"
+ "0o17740000000lf"
+ "0b1111111100000000000000000000000lf" ]
+ testList
+ "-"
+ [ checkAll
+ server
+ "float"
+ "let n = {number}"
+ "0xFFF0000000000000LF"
+ "0o1777600000000000000000LF"
+ "0b1111111111110000000000000000000000000000000000000000000000000000LF"
+ checkAll
+ server
+ "float32"
+ "let n = {number}"
+ "0xFF800000lf"
+ "0o37740000000lf"
+ "0b11111111100000000000000000000000lf" ] ] ])
module private ConvertCharToOtherForm =
let private tryExtractChar (title: String) =
let (start, fin) = "Convert to `", "`"
- if title.StartsWith(start, StringComparison.Ordinal) && title.EndsWith(fin, StringComparison.Ordinal) then
- let c = title.Substring(start.Length, title.Length - start.Length - fin.Length).ToString()
+
+ if
+ title.StartsWith(start, StringComparison.Ordinal)
+ && title.EndsWith(fin, StringComparison.Ordinal)
+ then
let c =
- if c.Length > 3 && c.StartsWith("'", StringComparison.Ordinal) && c.EndsWith("'B", StringComparison.Ordinal) then
+ title
+ .Substring(start.Length, title.Length - start.Length - fin.Length)
+ .ToString()
+
+ let c =
+ if
+ c.Length > 3
+ && c.StartsWith("'", StringComparison.Ordinal)
+ && c.EndsWith("'B", StringComparison.Ordinal)
+ then
// byte char (only when converting from int to char representation. Otherwise no `B` suffix in title)
c.Substring(1, c.Length - 2)
else
c
- c
- |> Some
+
+ c |> Some
else
None
+
let private extractFormat (char: String) =
if char.StartsWith("\\u", StringComparison.Ordinal) then
CharFormat.Utf16Hexadecimal
@@ -825,43 +911,38 @@ module private ConvertCharToOtherForm =
CharFormat.Decimal
else
CharFormat.Char
- let private tryExtractCharAndFormat (title: String) =
- tryExtractChar title
- |> Option.map (fun c -> c, extractFormat c)
-
+
+ let private tryExtractCharAndFormat (title: String) = tryExtractChar title |> Option.map (fun c -> c, extractFormat c)
+
let selectCharCodeFix (format: CharFormat) =
let f (a: CodeAction) =
a.Title
- |> tryExtractCharAndFormat
+ |> tryExtractCharAndFormat
|> Option.map (snd >> (=) format)
|> Option.defaultValue false
+
CodeFix.matching f
- let private checkFormat
- doc
- (source: String, cursor: Range)
- (format: CharFormat)
- expected
- =
- let name =
+ let private checkFormat doc (source: String, cursor: Range) (format: CharFormat) expected =
+ let name =
if String.IsNullOrWhiteSpace expected then
$"cannot convert to {format}"
else
$"can convert to {format}"
- testCaseAsync name (async {
- let! (doc, diags) = doc
- let expected =
- if String.IsNullOrWhiteSpace expected then
- ExpectedResult.NotApplicable
- else
- ExpectedResult.After expected
- do! checkFixAt
- (doc, diags)
- (source, cursor)
- Diagnostics.acceptAll
- (selectCharCodeFix (format))
- expected
- })
+
+ testCaseAsync
+ name
+ (async {
+ let! (doc, diags) = doc
+
+ let expected =
+ if String.IsNullOrWhiteSpace expected then
+ ExpectedResult.NotApplicable
+ else
+ ExpectedResult.After expected
+
+ do! checkFixAt (doc, diags) (source, cursor) Diagnostics.acceptAll (selectCharCodeFix (format)) expected
+ })
let check
server
@@ -874,13 +955,14 @@ module private ConvertCharToOtherForm =
(expectedUtf32Hexadecimal: String)
=
let (cursor, source) = Cursor.assertExtractRange beforeWithCursor
- documentTestList name server (Server.createUntitledDocument source) (fun doc -> [
- checkFormat doc (source, cursor) (CharFormat.Char) expectedChar
- checkFormat doc (source, cursor) (CharFormat.Decimal) expectedDecimal
- checkFormat doc (source, cursor) (CharFormat.Hexadecimal) expectedHexadecimal
- checkFormat doc (source, cursor) (CharFormat.Utf16Hexadecimal) expectedUtf16Hexadecimal
- checkFormat doc (source, cursor) (CharFormat.Utf32Hexadecimal) expectedUtf32Hexadecimal
- ])
+
+ documentTestList name server (Server.createUntitledDocument source) (fun doc ->
+ [ checkFormat doc (source, cursor) (CharFormat.Char) expectedChar
+ checkFormat doc (source, cursor) (CharFormat.Decimal) expectedDecimal
+ checkFormat doc (source, cursor) (CharFormat.Hexadecimal) expectedHexadecimal
+ checkFormat doc (source, cursor) (CharFormat.Utf16Hexadecimal) expectedUtf16Hexadecimal
+ checkFormat doc (source, cursor) (CharFormat.Utf32Hexadecimal) expectedUtf32Hexadecimal ])
+
/// in `template`: use `{char}` as placeholder
let private checkAll
server
@@ -893,699 +975,752 @@ module private ConvertCharToOtherForm =
(utf32HexadecimalValue: String)
=
let applyTemplate cursor number =
- let number =
- if cursor then
- number + "$0"
- else
- number
+ let number = if cursor then number + "$0" else number
template.Replace("{char}", number)
- testList name [
- let data = [
- CharFormat.Char, charValue
- CharFormat.Decimal, decimalValue
- CharFormat.Hexadecimal, hexadecimalValue
- CharFormat.Utf16Hexadecimal, utf16HexadecimalValue
- CharFormat.Utf32Hexadecimal, utf32HexadecimalValue
- ]
- let valueOf (format) =
- data
- |> List.find (fun (b,_) -> b = format)
- |> snd
- for (format, value) in data do
- if String.IsNullOrEmpty value then
- ()
- else
- let mkExpected (f) =
- if format = f || String.IsNullOrEmpty (valueOf f) then
- ""
- else
- applyTemplate false (valueOf f)
- check server $"can convert from {format}"
- (applyTemplate true value)
- (mkExpected CharFormat.Char)
- (mkExpected CharFormat.Decimal)
- (mkExpected CharFormat.Hexadecimal)
- (mkExpected CharFormat.Utf16Hexadecimal)
- (mkExpected CharFormat.Utf32Hexadecimal)
- ]
-
+ testList
+ name
+ [ let data =
+ [ CharFormat.Char, charValue
+ CharFormat.Decimal, decimalValue
+ CharFormat.Hexadecimal, hexadecimalValue
+ CharFormat.Utf16Hexadecimal, utf16HexadecimalValue
+ CharFormat.Utf32Hexadecimal, utf32HexadecimalValue ]
+
+ let valueOf (format) = data |> List.find (fun (b, _) -> b = format) |> snd
+
+ for (format, value) in data do
+ if String.IsNullOrEmpty value then
+ ()
+ else
+ let mkExpected (f) =
+ if format = f || String.IsNullOrEmpty(valueOf f) then
+ ""
+ else
+ applyTemplate false (valueOf f)
+
+ check
+ server
+ $"can convert from {format}"
+ (applyTemplate true value)
+ (mkExpected CharFormat.Char)
+ (mkExpected CharFormat.Decimal)
+ (mkExpected CharFormat.Hexadecimal)
+ (mkExpected CharFormat.Utf16Hexadecimal)
+ (mkExpected CharFormat.Utf32Hexadecimal) ]
+
let tests state =
- serverTestList "Convert char" state defaultConfigDto None (fun server -> [
- checkAll server "can convert ç"
- "let c = '{char}'"
- "ç"
- "\\231"
- "\\xE7"
- "\\u00E7"
- "\\U000000E7"
- checkAll server "can convert \\n"
- "let c = '{char}'"
- "\\n"
- "\\010"
- "\\x0A"
- "\\u000A"
- "\\U0000000A"
- checkAll server "can convert \\000 except to char"
- "let c = '{char}'"
- ""
- "\\000"
- "\\x00"
- "\\u0000"
- "\\U00000000"
-
- checkAll server "can convert \\u2248 only to formats that are big enough"
- "let c = '{char}'"
- "≈"
- ""
- ""
- "\\u2248"
- "\\U00002248"
-
- checkAll server "can convert single quotation mark"
- "let c = '{char}'"
- "\\\'"
- "\\039"
- "\\x27"
- "\\u0027"
- "\\U00000027"
-
- checkAll server "can convert unescaped double quotation mark"
- "let c = '{char}'"
- "\""
- "\\034"
- "\\x22"
- "\\u0022"
- "\\U00000022"
- // Note: Just check from `'"` to number forms.
- // Other directions produce unescaped quotation mark
- // -> Handled in test above
- check server "can convert escaped double quotation mark"
- "let c = '\"$0'"
- ""
- "let c = '\\034'"
- "let c = '\\x22'"
- "let c = '\\u0022'"
- "let c = '\\U00000022'"
-
- testList "byte" [
- let checkAll
+ serverTestList "Convert char" state defaultConfigDto None (fun server ->
+ [ checkAll server "can convert ç" "let c = '{char}'" "ç" "\\231" "\\xE7" "\\u00E7" "\\U000000E7"
+ checkAll server "can convert \\n" "let c = '{char}'" "\\n" "\\010" "\\x0A" "\\u000A" "\\U0000000A"
+ checkAll
server
- name
- (template: String)
- (charValue: String)
- (decimalValue: String)
- (hexadecimalValue: String)
- (utf16HexadecimalValue: String)
- (utf32HexadecimalValue: String)
- =
- // Note: `\x` & `\U` are currently not supported for byte char
- //TODO: change once supported was added
- checkAll server name template
- charValue
- decimalValue
- ""
- utf16HexadecimalValue
- ""
-
- checkAll server "can convert f"
- "let c = '{char}'B"
- "f"
- "\\102"
- "\\x66"
- "\\u0066"
- "\\U00000066"
- checkAll server "can convert \\n"
- "let c = '{char}'B"
- "\\n"
- "\\010"
- "\\x0A"
- "\\u000A"
- "\\U0000000A"
- checkAll server "can convert \\000 except to char"
- "let c = '{char}'B"
+ "can convert \\000 except to char"
+ "let c = '{char}'"
""
"\\000"
"\\x00"
"\\u0000"
"\\U00000000"
- check server "does not trigger for char outside of byte range"
- "let c = 'ç$0'B"
- "" "" "" "" ""
- ]
- ])
+
+ checkAll
+ server
+ "can convert \\u2248 only to formats that are big enough"
+ "let c = '{char}'"
+ "≈"
+ ""
+ ""
+ "\\u2248"
+ "\\U00002248"
+
+ checkAll
+ server
+ "can convert single quotation mark"
+ "let c = '{char}'"
+ "\\\'"
+ "\\039"
+ "\\x27"
+ "\\u0027"
+ "\\U00000027"
+
+ checkAll
+ server
+ "can convert unescaped double quotation mark"
+ "let c = '{char}'"
+ "\""
+ "\\034"
+ "\\x22"
+ "\\u0022"
+ "\\U00000022"
+ // Note: Just check from `'"` to number forms.
+ // Other directions produce unescaped quotation mark
+ // -> Handled in test above
+ check
+ server
+ "can convert escaped double quotation mark"
+ "let c = '\"$0'"
+ ""
+ "let c = '\\034'"
+ "let c = '\\x22'"
+ "let c = '\\u0022'"
+ "let c = '\\U00000022'"
+
+ testList
+ "byte"
+ [ let checkAll
+ server
+ name
+ (template: String)
+ (charValue: String)
+ (decimalValue: String)
+ (_hexadecimalValue: String)
+ (utf16HexadecimalValue: String)
+ (_utf32HexadecimalValue: String)
+ =
+ // Note: `\x` & `\U` are currently not supported for byte char
+ //TODO: change once supported was added
+ checkAll server name template charValue decimalValue "" utf16HexadecimalValue ""
+
+ checkAll server "can convert f" "let c = '{char}'B" "f" "\\102" "\\x66" "\\u0066" "\\U00000066"
+ checkAll server "can convert \\n" "let c = '{char}'B" "\\n" "\\010" "\\x0A" "\\u000A" "\\U0000000A"
+
+ checkAll
+ server
+ "can convert \\000 except to char"
+ "let c = '{char}'B"
+ ""
+ "\\000"
+ "\\x00"
+ "\\u0000"
+ "\\U00000000"
+
+ check server "does not trigger for char outside of byte range" "let c = 'ç$0'B" "" "" "" "" "" ] ])
module private ConvertByteBetweenIntAndChar =
let tests state =
- serverTestList "Convert Byte between Int And Char" state defaultConfigDto None (fun server -> [
- let template = sprintf "let c = %s"
- let charTemplate (c: string) = template $"'%s{c}'B"
- ConvertCharToOtherForm.check server "can convert from int to char"
- (template "102$0uy")
- (charTemplate "f")
- (charTemplate "\\102")
- ""// (charTemplate "\\x66")
- (charTemplate "\\u0066")
- ""// (charTemplate "\\U00000066")
-
- let template = sprintf "let c = %s"
- let intTemplate (c: string) = template $"%s{c}uy"
- ConvertIntToOtherBase.check server "can convert from char to int"
- (template "'f$0'B")
- (intTemplate "102")
- (intTemplate "0x66")
- (intTemplate "0o146")
- (intTemplate "0b1100110")
-
- testCaseAsync "cannot convert from int > 127 to char" <|
- CodeFix.checkNotApplicable server
+ serverTestList "Convert Byte between Int And Char" state defaultConfigDto None (fun server ->
+ [ let template = sprintf "let c = %s"
+ let charTemplate (c: string) = template $"'%s{c}'B"
+
+ ConvertCharToOtherForm.check
+ server
+ "can convert from int to char"
+ (template "102$0uy")
+ (charTemplate "f")
+ (charTemplate "\\102")
+ "" // (charTemplate "\\x66")
+ (charTemplate "\\u0066")
+ "" // (charTemplate "\\U00000066")
+
+ let template = sprintf "let c = %s"
+ let intTemplate (c: string) = template $"%s{c}uy"
+
+ ConvertIntToOtherBase.check
+ server
+ "can convert from char to int"
+ (template "'f$0'B")
+ (intTemplate "102")
+ (intTemplate "0x66")
+ (intTemplate "0o146")
+ (intTemplate "0b1100110")
+
+ testCaseAsync "cannot convert from int > 127 to char"
+ <| CodeFix.checkNotApplicable
+ server
"let c = 250$0uy"
Diagnostics.acceptAll
(ConvertCharToOtherForm.selectCharCodeFix CharFormat.Char)
- testCaseAsync "cannot convert from char > 127 to int" <|
- CodeFix.checkNotApplicable server
+
+ testCaseAsync "cannot convert from char > 127 to int"
+ <| CodeFix.checkNotApplicable
+ server
"let c = 'ú$0'B;"
Diagnostics.acceptAll
- (ConvertIntToOtherBase.selectIntCodeFix Base.Decimal)
- ])
+ (ConvertIntToOtherBase.selectIntCodeFix Base.Decimal) ])
module private AddDigitGroupSeparator =
let private intTests state =
- serverTestList "To int numbers" state defaultConfigDto None (fun server -> [
- testCaseAsync "can add separator to long decimal int" <|
- CodeFix.check server
+ serverTestList "To int numbers" state defaultConfigDto None (fun server ->
+ [ testCaseAsync "can add separator to long decimal int"
+ <| CodeFix.check
+ server
"let value = 1234567890$0"
Diagnostics.acceptAll
(CodeFix.withTitle Title.Int.Separate.decimal3)
"let value = 1_234_567_890"
- testCaseAsync "cannot add separator short decimal int" <|
- CodeFix.checkNotApplicable server
+ testCaseAsync "cannot add separator short decimal int"
+ <| CodeFix.checkNotApplicable
+ server
"let value = 123$0"
Diagnostics.acceptAll
(CodeFix.withTitle Title.Int.Separate.decimal3)
- testCaseAsync "cannot add separator to decimal int with existing separator" <|
- CodeFix.checkNotApplicable server
+ testCaseAsync "cannot add separator to decimal int with existing separator"
+ <| CodeFix.checkNotApplicable
+ server
"let value = 123456789_0$0"
Diagnostics.acceptAll
(CodeFix.withTitle Title.Int.Separate.decimal3)
- testCaseAsync "can add separator to long negative decimal int" <|
- CodeFix.check server
+ testCaseAsync "can add separator to long negative decimal int"
+ <| CodeFix.check
+ server
"let value = -1234567890$0"
Diagnostics.acceptAll
(CodeFix.withTitle Title.Int.Separate.decimal3)
"let value = -1_234_567_890"
- testCaseAsync "can add separator to decimal int with leading zeros" <|
- CodeFix.check server
+ testCaseAsync "can add separator to decimal int with leading zeros"
+ <| CodeFix.check
+ server
"let value = 0000000090$0"
Diagnostics.acceptAll
(CodeFix.withTitle Title.Int.Separate.decimal3)
"let value = 0_000_000_090"
- testCaseAsync "can add separator to too-long decimal int" <|
- CodeFix.check server
+ testCaseAsync "can add separator to too-long decimal int"
+ <| CodeFix.check
+ server
"let value = 12345678901234567890$0"
Diagnostics.acceptAll
(CodeFix.withTitle Title.Int.Separate.decimal3)
"let value = 12_345_678_901_234_567_890"
- testCaseAsync "can add separator to long decimal int64" <|
- CodeFix.check server
+ testCaseAsync "can add separator to long decimal int64"
+ <| CodeFix.check
+ server
"let value = 12345678901234567L$0"
Diagnostics.acceptAll
(CodeFix.withTitle Title.Int.Separate.decimal3)
"let value = 12_345_678_901_234_567L"
- testList "can add separator to hexadecimal int" [
- testCaseAsync "words" <|
- CodeFix.check server
- "let value = 0x1234578$0"
- Diagnostics.acceptAll
- (CodeFix.withTitle Title.Int.Separate.hexadecimal4)
- "let value = 0x123_4578"
- testCaseAsync "bytes" <|
- CodeFix.check server
- "let value = 0x1234578$0"
- Diagnostics.acceptAll
- (CodeFix.withTitle Title.Int.Separate.hexadecimal2)
- "let value = 0x1_23_45_78"
- ]
- testCaseAsync "can add separator to octal int" <|
- CodeFix.check server
+ testList
+ "can add separator to hexadecimal int"
+ [ testCaseAsync "words"
+ <| CodeFix.check
+ server
+ "let value = 0x1234578$0"
+ Diagnostics.acceptAll
+ (CodeFix.withTitle Title.Int.Separate.hexadecimal4)
+ "let value = 0x123_4578"
+ testCaseAsync "bytes"
+ <| CodeFix.check
+ server
+ "let value = 0x1234578$0"
+ Diagnostics.acceptAll
+ (CodeFix.withTitle Title.Int.Separate.hexadecimal2)
+ "let value = 0x1_23_45_78" ]
+ testCaseAsync "can add separator to octal int"
+ <| CodeFix.check
+ server
"let value = 0o1234567$0"
Diagnostics.acceptAll
(CodeFix.withTitle Title.Int.Separate.octal3)
"let value = 0o1_234_567"
- testList "can add separator to binary int" [
- testCaseAsync "nibbles" <|
- CodeFix.check server
- "let value = 0b1010101010101010101$0"
- Diagnostics.acceptAll
- (CodeFix.withTitle Title.Int.Separate.binary4)
- "let value = 0b101_0101_0101_0101_0101"
- testCaseAsync "bytes" <|
- CodeFix.check server
- "let value = 0b1010101010101010101$0"
- Diagnostics.acceptAll
- (CodeFix.withTitle Title.Int.Separate.binary8)
- "let value = 0b101_01010101_01010101"
- ]
- testCaseAsync "can add separator to bigint" <|
- CodeFix.check server
+ testList
+ "can add separator to binary int"
+ [ testCaseAsync "nibbles"
+ <| CodeFix.check
+ server
+ "let value = 0b1010101010101010101$0"
+ Diagnostics.acceptAll
+ (CodeFix.withTitle Title.Int.Separate.binary4)
+ "let value = 0b101_0101_0101_0101_0101"
+ testCaseAsync "bytes"
+ <| CodeFix.check
+ server
+ "let value = 0b1010101010101010101$0"
+ Diagnostics.acceptAll
+ (CodeFix.withTitle Title.Int.Separate.binary8)
+ "let value = 0b101_01010101_01010101" ]
+ testCaseAsync "can add separator to bigint"
+ <| CodeFix.check
+ server
"let value = 9999999999999999999999999999$0I"
Diagnostics.acceptAll
(CodeFix.withTitle Title.Int.Separate.decimal3)
"let value = 9_999_999_999_999_999_999_999_999_999I"
- testCaseAsync "does not trigger for short number" <|
- CodeFix.checkNotApplicable server
+ testCaseAsync "does not trigger for short number"
+ <| CodeFix.checkNotApplicable
+ server
"let value = 123$0"
Diagnostics.acceptAll
- (CodeFix.withTitle Title.Int.Separate.decimal3)
- ])
+ (CodeFix.withTitle Title.Int.Separate.decimal3) ])
let private floatTests state =
- serverTestList "To float numbers" state defaultConfigDto None (fun server -> [
- testCaseAsync "can add separator to X.X float" <|
- CodeFix.check server
+ serverTestList "To float numbers" state defaultConfigDto None (fun server ->
+ [ testCaseAsync "can add separator to X.X float"
+ <| CodeFix.check
+ server
"let value = 1234567.01234567$0"
Diagnostics.acceptAll
(CodeFix.withTitle Title.Float.Separate.all3)
"let value = 1_234_567.012_345_67"
- testCaseAsync "can add separator to X.XeX float" <|
- CodeFix.check server
+ testCaseAsync "can add separator to X.XeX float"
+ <| CodeFix.check
+ server
"let value = 1234567.01234567e12345678$0"
Diagnostics.acceptAll
(CodeFix.withTitle Title.Float.Separate.all3)
"let value = 1_234_567.012_345_67e12_345_678"
- testCaseAsync "can add separator to X. float" <|
- CodeFix.check server
+ testCaseAsync "can add separator to X. float"
+ <| CodeFix.check
+ server
"let value = 1234567.$0"
Diagnostics.acceptAll
(CodeFix.withTitle Title.Float.Separate.all3)
"let value = 1_234_567."
- testCaseAsync "can add separator to XeX float" <|
- CodeFix.check server
+ testCaseAsync "can add separator to XeX float"
+ <| CodeFix.check
+ server
"let value = 1234567e12345678$0"
Diagnostics.acceptAll
(CodeFix.withTitle Title.Float.Separate.all3)
"let value = 1_234_567e12_345_678"
- testCaseAsync "can add separator to float32" <|
- CodeFix.check server
+ testCaseAsync "can add separator to float32"
+ <| CodeFix.check
+ server
"let value = 1234567.01234567f$0"
Diagnostics.acceptAll
(CodeFix.withTitle Title.Float.Separate.all3)
"let value = 1_234_567.012_345_67f"
- testCaseAsync "can add separator to decimal" <|
- CodeFix.check server
+ testCaseAsync "can add separator to decimal"
+ <| CodeFix.check
+ server
"let value = 1234567.01234567m$0"
Diagnostics.acceptAll
(CodeFix.withTitle Title.Float.Separate.all3)
"let value = 1_234_567.012_345_67m"
- testCaseAsync "keep sign" <|
- CodeFix.check server
+ testCaseAsync "keep sign"
+ <| CodeFix.check
+ server
"let value = -1234567.01234567e12345678$0"
Diagnostics.acceptAll
(CodeFix.withTitle Title.Float.Separate.all3)
"let value = -1_234_567.012_345_67e12_345_678"
- testCaseAsync "keep sign for exponent" <|
- CodeFix.check server
+ testCaseAsync "keep sign for exponent"
+ <| CodeFix.check
+ server
"let value = 1234567.01234567e+12345678$0"
Diagnostics.acceptAll
(CodeFix.withTitle Title.Float.Separate.all3)
"let value = 1_234_567.012_345_67e+12_345_678"
- testCaseAsync "cannot add separator when existing separator" <|
- CodeFix.checkNotApplicable server
+ testCaseAsync "cannot add separator when existing separator"
+ <| CodeFix.checkNotApplicable
+ server
"let value = 1234567.0123_4567$0"
Diagnostics.acceptAll
(CodeFix.withTitle Title.Float.Separate.all3)
- testCaseAsync "does not trigger for short number" <|
- CodeFix.checkNotApplicable server
+ testCaseAsync "does not trigger for short number"
+ <| CodeFix.checkNotApplicable
+ server
"let value = 123.012e123$0"
Diagnostics.acceptAll
(CodeFix.withTitle Title.Float.Separate.all3)
- testCaseAsync "can add separator to just decimal part when other parts are too short" <|
- CodeFix.check server
+ testCaseAsync "can add separator to just decimal part when other parts are too short"
+ <| CodeFix.check
+ server
"let value = 123.01234567e+123$0"
Diagnostics.acceptAll
(CodeFix.withTitle Title.Float.Separate.all3)
"let value = 123.012_345_67e+123"
- testCaseAsync "can add separator to just int part when other parts are too short" <|
- CodeFix.check server
+ testCaseAsync "can add separator to just int part when other parts are too short"
+ <| CodeFix.check
+ server
"let value = 1234567.012e+123$0"
Diagnostics.acceptAll
(CodeFix.withTitle Title.Float.Separate.all3)
"let value = 1_234_567.012e+123"
- testCaseAsync "can add separator to just exponent part when other parts are too short" <|
- CodeFix.check server
+ testCaseAsync "can add separator to just exponent part when other parts are too short"
+ <| CodeFix.check
+ server
"let value = 123.012e+1234567$0"
Diagnostics.acceptAll
(CodeFix.withTitle Title.Float.Separate.all3)
"let value = 123.012e+1_234_567"
- testCaseAsync "can add separator to decimal & exponent parts when int part is too short" <|
- CodeFix.check server
+ testCaseAsync "can add separator to decimal & exponent parts when int part is too short"
+ <| CodeFix.check
+ server
"let value = 123.012345678e+1234567$0"
Diagnostics.acceptAll
(CodeFix.withTitle Title.Float.Separate.all3)
- "let value = 123.012_345_678e+1_234_567"
- ])
+ "let value = 123.012_345_678e+1_234_567" ])
- let tests state =
- testList "Add Digit Group Separator" [
- intTests state
- floatTests state
- ]
+ let tests state = testList "Add Digit Group Separator" [ intTests state; floatTests state ]
module private ReplaceWithName =
/// Note: `System` is `open`
let checkReplaceWith server tyName value fieldName =
let replacement = $"{tyName}.{fieldName}"
- CodeFix.check server
+
+ CodeFix.check
+ server
$"open System\nlet value = {value}$0"
Diagnostics.acceptAll
(CodeFix.withTitle (Title.replaceWith replacement))
$"open System\nlet value = {replacement}"
+
let checkCannotReplaceWith server tyName value fieldName =
let replacement = $"{tyName}.{fieldName}"
- CodeFix.checkNotApplicable server
+
+ CodeFix.checkNotApplicable
+ server
$"open System\nlet value = {value}$0"
Diagnostics.acceptAll
(CodeFix.withTitle (Title.replaceWith replacement))
let private intTests state =
- serverTestList "Replace Int" state defaultConfigDto None (fun server -> [
- let checkReplaceWith = checkReplaceWith server
- let checkCannotReplaceWith = checkCannotReplaceWith server
-
- /// Formats with suffix
- let inline format value = sprintf "%A" value
-
- testList "can replace SByte" [
- testCaseAsync "with MaxValue" <|
- checkReplaceWith (nameof System.SByte) (format SByte.MaxValue) (nameof System.SByte.MaxValue)
- testCaseAsync "with MinValue" <|
- checkReplaceWith (nameof System.SByte) (format SByte.MinValue) (nameof(System.SByte.MinValue))
- ]
- testList "can replace Byte" [
- testCaseAsync "with MaxValue" <|
- checkReplaceWith (nameof System.Byte) (format Byte.MaxValue) (nameof System.Byte.MaxValue)
- testCaseAsync "not with MinValue" <|
- checkCannotReplaceWith (nameof System.Byte) (format Byte.MinValue) (nameof(System.Byte.MinValue))
- ]
- testList "can replace Int16" [
- testCaseAsync "with MaxValue" <|
- checkReplaceWith (nameof System.Int16) (format Int16.MaxValue) (nameof System.Int16.MaxValue)
- testCaseAsync "with MinValue" <|
- checkReplaceWith (nameof System.Int16) (format Int16.MinValue) (nameof(System.Int16.MinValue))
- ]
- testList "can replace UInt16" [
- testCaseAsync "with MaxValue" <|
- checkReplaceWith (nameof System.UInt16) (format UInt16.MaxValue) (nameof System.UInt16.MaxValue)
- testCaseAsync "not with MinValue" <|
- checkCannotReplaceWith (nameof System.UInt16) (format UInt16.MinValue) (nameof(System.UInt16.MinValue))
- ]
- testList "can replace Int32" [
- testCaseAsync "with MaxValue" <|
- checkReplaceWith (nameof System.Int32) (format Int32.MaxValue) (nameof System.Int32.MaxValue)
- testCaseAsync "with MinValue" <|
- checkReplaceWith (nameof System.Int32) (format Int32.MinValue) (nameof(System.Int32.MinValue))
- ]
- testList "can replace UInt32" [
- testCaseAsync "with MaxValue" <|
- checkReplaceWith (nameof System.UInt32) (format UInt32.MaxValue) (nameof System.UInt32.MaxValue)
- testCaseAsync "not with MinValue" <|
- checkCannotReplaceWith (nameof System.UInt32) (format UInt32.MinValue) (nameof(System.UInt32.MinValue))
- ]
- testList "can replace NativeInt" [
- testCaseAsync "with MaxValue" <|
- checkReplaceWith (nameof System.IntPtr) (format IntPtr.MaxValue) (nameof System.IntPtr.MaxValue)
- testCaseAsync "with MinValue" <|
- checkReplaceWith (nameof System.IntPtr) (format IntPtr.MinValue) (nameof(System.IntPtr.MinValue))
- ]
- testList "can replace UNativeInt" [
- testCaseAsync "with MaxValue" <|
- checkReplaceWith (nameof System.UIntPtr) (format UIntPtr.MaxValue) (nameof System.UIntPtr.MaxValue)
- testCaseAsync "not with MinValue" <|
- checkCannotReplaceWith (nameof System.UIntPtr) (format UIntPtr.MinValue) (nameof(System.UIntPtr.MinValue))
- ]
- testList "can replace Int64" [
- testCaseAsync "with MaxValue" <|
- checkReplaceWith (nameof System.Int64) (format Int64.MaxValue) (nameof System.Int64.MaxValue)
- testCaseAsync "with MinValue" <|
- checkReplaceWith (nameof System.Int64) (format Int64.MinValue) (nameof(System.Int64.MinValue))
- ]
- testList "can replace UInt64" [
- testCaseAsync "with MaxValue" <|
- checkReplaceWith (nameof System.UInt64) (format UInt64.MaxValue) (nameof System.UInt64.MaxValue)
- testCaseAsync "not with MinValue" <|
- checkCannotReplaceWith (nameof System.UInt64) (format UInt64.MinValue) (nameof(System.UInt64.MinValue))
- ]
-
- testCaseAsync "Emit leading System if System not open" <|
- CodeFix.check server
+ serverTestList "Replace Int" state defaultConfigDto None (fun server ->
+ [ let checkReplaceWith = checkReplaceWith server
+ let checkCannotReplaceWith = checkCannotReplaceWith server
+
+ /// Formats with suffix
+ let inline format value = sprintf "%A" value
+
+ testList
+ "can replace SByte"
+ [ testCaseAsync "with MaxValue"
+ <| checkReplaceWith (nameof System.SByte) (format SByte.MaxValue) (nameof System.SByte.MaxValue)
+ testCaseAsync "with MinValue"
+ <| checkReplaceWith (nameof System.SByte) (format SByte.MinValue) (nameof (System.SByte.MinValue)) ]
+
+ testList
+ "can replace Byte"
+ [ testCaseAsync "with MaxValue"
+ <| checkReplaceWith (nameof System.Byte) (format Byte.MaxValue) (nameof System.Byte.MaxValue)
+ testCaseAsync "not with MinValue"
+ <| checkCannotReplaceWith (nameof System.Byte) (format Byte.MinValue) (nameof (System.Byte.MinValue)) ]
+
+ testList
+ "can replace Int16"
+ [ testCaseAsync "with MaxValue"
+ <| checkReplaceWith (nameof System.Int16) (format Int16.MaxValue) (nameof System.Int16.MaxValue)
+ testCaseAsync "with MinValue"
+ <| checkReplaceWith (nameof System.Int16) (format Int16.MinValue) (nameof (System.Int16.MinValue)) ]
+
+ testList
+ "can replace UInt16"
+ [ testCaseAsync "with MaxValue"
+ <| checkReplaceWith (nameof System.UInt16) (format UInt16.MaxValue) (nameof System.UInt16.MaxValue)
+ testCaseAsync "not with MinValue"
+ <| checkCannotReplaceWith (nameof System.UInt16) (format UInt16.MinValue) (nameof (System.UInt16.MinValue)) ]
+
+ testList
+ "can replace Int32"
+ [ testCaseAsync "with MaxValue"
+ <| checkReplaceWith (nameof System.Int32) (format Int32.MaxValue) (nameof System.Int32.MaxValue)
+ testCaseAsync "with MinValue"
+ <| checkReplaceWith (nameof System.Int32) (format Int32.MinValue) (nameof (System.Int32.MinValue)) ]
+
+ testList
+ "can replace UInt32"
+ [ testCaseAsync "with MaxValue"
+ <| checkReplaceWith (nameof System.UInt32) (format UInt32.MaxValue) (nameof System.UInt32.MaxValue)
+ testCaseAsync "not with MinValue"
+ <| checkCannotReplaceWith (nameof System.UInt32) (format UInt32.MinValue) (nameof (System.UInt32.MinValue)) ]
+
+ testList
+ "can replace NativeInt"
+ [ testCaseAsync "with MaxValue"
+ <| checkReplaceWith (nameof System.IntPtr) (format IntPtr.MaxValue) (nameof System.IntPtr.MaxValue)
+ testCaseAsync "with MinValue"
+ <| checkReplaceWith (nameof System.IntPtr) (format IntPtr.MinValue) (nameof (System.IntPtr.MinValue)) ]
+
+ testList
+ "can replace UNativeInt"
+ [ testCaseAsync "with MaxValue"
+ <| checkReplaceWith (nameof System.UIntPtr) (format UIntPtr.MaxValue) (nameof System.UIntPtr.MaxValue)
+ testCaseAsync "not with MinValue"
+ <| checkCannotReplaceWith
+ (nameof System.UIntPtr)
+ (format UIntPtr.MinValue)
+ (nameof (System.UIntPtr.MinValue)) ]
+
+ testList
+ "can replace Int64"
+ [ testCaseAsync "with MaxValue"
+ <| checkReplaceWith (nameof System.Int64) (format Int64.MaxValue) (nameof System.Int64.MaxValue)
+ testCaseAsync "with MinValue"
+ <| checkReplaceWith (nameof System.Int64) (format Int64.MinValue) (nameof (System.Int64.MinValue)) ]
+
+ testList
+ "can replace UInt64"
+ [ testCaseAsync "with MaxValue"
+ <| checkReplaceWith (nameof System.UInt64) (format UInt64.MaxValue) (nameof System.UInt64.MaxValue)
+ testCaseAsync "not with MinValue"
+ <| checkCannotReplaceWith (nameof System.UInt64) (format UInt64.MinValue) (nameof (System.UInt64.MinValue)) ]
+
+ testCaseAsync "Emit leading System if System not open"
+ <| CodeFix.check
+ server
$"let value = {format Int32.MaxValue}$0"
Diagnostics.acceptAll
(CodeFix.withTitle (Title.replaceWith "Int32.MaxValue"))
- $"let value = System.Int32.MaxValue"
- ])
+ $"let value = System.Int32.MaxValue" ])
let private floatTests state =
- serverTestList "Replace Float" state defaultConfigDto None (fun server -> [
- // Beware of rounding in number printing!
- // For example:
- // ```fsharp
- // > Double.MaxValue;;
- // val it: float = 1.797693135e+308
- // > 1.797693135e+308;;
- // val it: float = infinity
-
- // > Double.MaxValue.ToString();;
- // val it: string = "1.7976931348623157E+308"
- // ```
-
- let checkReplaceWith = checkReplaceWith server
- let checkCannotReplaceWith = checkCannotReplaceWith server
- let checkReplaceWith' value name =
- CodeFix.check server
- $"let value = {value}$0"
- Diagnostics.acceptAll
- (CodeFix.withTitle (Title.replaceWith name))
- $"let value = {name}"
-
- testList "can replace float" [
- testCaseAsync "with MaxValue" <|
- checkReplaceWith (nameof System.Double) "1.7976931348623157E+308" (nameof System.Double.MaxValue)
- testCaseAsync "with MinValue" <|
- checkReplaceWith (nameof System.Double) "-1.7976931348623157E+308" (nameof System.Double.MinValue)
- testCaseAsync "with Epsilon" <|
- checkReplaceWith (nameof System.Double) "5E-324" (nameof System.Double.Epsilon)
- testCaseAsync "with infinity" <|
- checkReplaceWith' "123456789e123456789" "infinity"
- testCaseAsync "with infinity (int)" <|
- checkReplaceWith' "0x7FF0000000000000LF" "infinity"
- testCaseAsync "with -infinity" <|
- checkReplaceWith' "-123456789e123456789" "-infinity"
- testCaseAsync "with -infinity (int)" <|
- checkReplaceWith' "0o1777600000000000000000LF" "-infinity"
- testCaseAsync "with nan (int)" <|
- checkReplaceWith' "0b1111111111111000000100010001010010010010001000100010001000100100LF" "nan"
- ]
- testList "can replace float32" [
- testCaseAsync "with MaxValue" <|
- checkReplaceWith (nameof System.Single) "3.4028235E+38f" (nameof System.Single.MaxValue)
- testCaseAsync "with MinValue" <|
- checkReplaceWith (nameof System.Single) "-3.4028235E+38f" (nameof System.Single.MinValue)
- testCaseAsync "with Epsilon" <|
- checkReplaceWith (nameof System.Single) "1.401298464e-45f" (nameof System.Single.Epsilon)
- testCaseAsync "with infinity" <|
- checkReplaceWith' "123456789e123456789f" "infinityf"
- testCaseAsync "with infinity (int)" <|
- checkReplaceWith' "0x7F800000lf" "infinityf"
- testCaseAsync "with -infinity" <|
- checkReplaceWith' "-123456789e123456789f" "-infinityf"
- testCaseAsync "with -infinity (int)" <|
- checkReplaceWith' "0o37740000000lf" "-infinityf"
- testCaseAsync "with nan (int)" <|
- checkReplaceWith' "0b1111111101001000100100100100100lf" "nanf"
- ]
-
- testCaseAsync "Emit leading System if System not open" <|
- CodeFix.check server
+ serverTestList "Replace Float" state defaultConfigDto None (fun server ->
+ [
+ // Beware of rounding in number printing!
+ // For example:
+ // ```fsharp
+ // > Double.MaxValue;;
+ // val it: float = 1.797693135e+308
+ // > 1.797693135e+308;;
+ // val it: float = infinity
+
+ // > Double.MaxValue.ToString();;
+ // val it: string = "1.7976931348623157E+308"
+ // ```
+
+ let checkReplaceWith = checkReplaceWith server
+ let _checkCannotReplaceWith = checkCannotReplaceWith server
+
+ let checkReplaceWith' value name =
+ CodeFix.check
+ server
+ $"let value = {value}$0"
+ Diagnostics.acceptAll
+ (CodeFix.withTitle (Title.replaceWith name))
+ $"let value = {name}"
+
+ testList
+ "can replace float"
+ [ testCaseAsync "with MaxValue"
+ <| checkReplaceWith (nameof System.Double) "1.7976931348623157E+308" (nameof System.Double.MaxValue)
+ testCaseAsync "with MinValue"
+ <| checkReplaceWith (nameof System.Double) "-1.7976931348623157E+308" (nameof System.Double.MinValue)
+ testCaseAsync "with Epsilon"
+ <| checkReplaceWith (nameof System.Double) "5E-324" (nameof System.Double.Epsilon)
+ testCaseAsync "with infinity"
+ <| checkReplaceWith' "123456789e123456789" "infinity"
+ testCaseAsync "with infinity (int)"
+ <| checkReplaceWith' "0x7FF0000000000000LF" "infinity"
+ testCaseAsync "with -infinity"
+ <| checkReplaceWith' "-123456789e123456789" "-infinity"
+ testCaseAsync "with -infinity (int)"
+ <| checkReplaceWith' "0o1777600000000000000000LF" "-infinity"
+ testCaseAsync "with nan (int)"
+ <| checkReplaceWith' "0b1111111111111000000100010001010010010010001000100010001000100100LF" "nan" ]
+
+ testList
+ "can replace float32"
+ [ testCaseAsync "with MaxValue"
+ <| checkReplaceWith (nameof System.Single) "3.4028235E+38f" (nameof System.Single.MaxValue)
+ testCaseAsync "with MinValue"
+ <| checkReplaceWith (nameof System.Single) "-3.4028235E+38f" (nameof System.Single.MinValue)
+ testCaseAsync "with Epsilon"
+ <| checkReplaceWith (nameof System.Single) "1.401298464e-45f" (nameof System.Single.Epsilon)
+ testCaseAsync "with infinity"
+ <| checkReplaceWith' "123456789e123456789f" "infinityf"
+ testCaseAsync "with infinity (int)"
+ <| checkReplaceWith' "0x7F800000lf" "infinityf"
+ testCaseAsync "with -infinity"
+ <| checkReplaceWith' "-123456789e123456789f" "-infinityf"
+ testCaseAsync "with -infinity (int)"
+ <| checkReplaceWith' "0o37740000000lf" "-infinityf"
+ testCaseAsync "with nan (int)"
+ <| checkReplaceWith' "0b1111111101001000100100100100100lf" "nanf" ]
+
+ testCaseAsync "Emit leading System if System not open"
+ <| CodeFix.check
+ server
$"let value = 1.7976931348623157E+308$0"
Diagnostics.acceptAll
(CodeFix.withTitle (Title.replaceWith "Double.MaxValue"))
$"let value = System.Double.MaxValue"
- testList "can replace decimal" [
- testCaseAsync "with MaxValue" <|
- checkReplaceWith (nameof System.Decimal) "79228162514264337593543950335m" (nameof System.Decimal.MaxValue)
- testCaseAsync "with MinValue" <|
- checkReplaceWith (nameof System.Decimal) "-79228162514264337593543950335m" (nameof System.Decimal.MinValue)
- ]
+ testList
+ "can replace decimal"
+ [ testCaseAsync "with MaxValue"
+ <| checkReplaceWith
+ (nameof System.Decimal)
+ "79228162514264337593543950335m"
+ (nameof System.Decimal.MaxValue)
+ testCaseAsync "with MinValue"
+ <| checkReplaceWith
+ (nameof System.Decimal)
+ "-79228162514264337593543950335m"
+ (nameof System.Decimal.MinValue) ]
- ])
- let tests state =
- testList "Replace With Name" [
- intTests state
- floatTests state
- ]
+ ])
+
+ let tests state = testList "Replace With Name" [ intTests state; floatTests state ]
module SignHelpers =
let tests state =
- serverTestList "Sign Helpers" state defaultConfigDto None (fun server -> [
- testList "extract `-`" [
- testCaseAsync "from bin int" <|
- CodeFix.check server
- "let value = 0b10000101y$0"
- Diagnostics.acceptAll
- (CodeFix.withTitle Title.Int.Convert.SpecialCase.extractMinusFromNegativeConstant)
- "let value = -0b1111011y"
- testCaseAsync "from hex int" <|
- CodeFix.check server
- "let value = 0x85y$0"
- Diagnostics.acceptAll
- (CodeFix.withTitle Title.Int.Convert.SpecialCase.extractMinusFromNegativeConstant)
- "let value = -0x7By"
- testCaseAsync "from oct int" <|
- CodeFix.check server
- "let value = 0o205y$0"
- Diagnostics.acceptAll
- (CodeFix.withTitle Title.Int.Convert.SpecialCase.extractMinusFromNegativeConstant)
- "let value = -0o173y"
- testCaseAsync "does not trigger for decimal int" <|
- CodeFix.checkNotApplicable server
- "let value = -123y$0"
- Diagnostics.acceptAll
- (CodeFix.withTitle Title.Int.Convert.SpecialCase.extractMinusFromNegativeConstant)
- ]
- testList "integrate `-`" [
- testCaseAsync "into bin int" <|
- CodeFix.check server
- "let value = -0b1111011y$0"
- Diagnostics.acceptAll
- (CodeFix.withTitle Title.Int.Convert.SpecialCase.integrateExplicitMinus)
- "let value = 0b10000101y"
- testCaseAsync "into hex int" <|
- CodeFix.check server
- "let value = -0x7By$0"
- Diagnostics.acceptAll
- (CodeFix.withTitle Title.Int.Convert.SpecialCase.integrateExplicitMinus)
- "let value = 0x85y"
- testCaseAsync "into oct int" <|
- CodeFix.check server
- "let value = -0o173y$0"
- Diagnostics.acceptAll
- (CodeFix.withTitle Title.Int.Convert.SpecialCase.integrateExplicitMinus)
- "let value = 0o205y"
- testCaseAsync "does not trigger for decimal int" <|
- CodeFix.checkNotApplicable server
- "let value = -123y$0"
- Diagnostics.acceptAll
- (CodeFix.withTitle Title.Int.Convert.SpecialCase.integrateExplicitMinus)
- ]
-
- testList "MinValue" [
- testCaseAsync "can remove explicit `-`" <|
- CodeFix.check server
- "let value = -0b10000000y$0"
- Diagnostics.acceptAll
- (CodeFix.withTitle Title.Int.Convert.SpecialCase.removeExplicitMinusWithMinValue)
- "let value = 0b10000000y"
- testCaseAsync "does not trigger for decimal int" <|
- CodeFix.checkNotApplicable server
- "let value = -127y$0"
- Diagnostics.acceptAll
- (CodeFix.withTitle Title.Int.Convert.SpecialCase.removeExplicitMinusWithMinValue)
- ]
-
- testList "use implicit `+`" [
- testCaseAsync "can change to positive" <|
- CodeFix.check server
- "let value = -0b1111_1101y$0"
- Diagnostics.acceptAll
- (CodeFix.withTitle Title.Int.Convert.SpecialCase.useImplicitPlusInPositiveConstantWithMinusSign)
- "let value = 0b11y"
- ]
-
- testList "ensure valid sign" [
- // QuickFixes might add sign which might lead to invalid code:
- // ```fsharp
- // // -91y
- // let value = 5y+0b1010_0101y
-
- // // => Convert to decimal
+ serverTestList "Sign Helpers" state defaultConfigDto None (fun server ->
+ [ testList
+ "extract `-`"
+ [ testCaseAsync "from bin int"
+ <| CodeFix.check
+ server
+ "let value = 0b10000101y$0"
+ Diagnostics.acceptAll
+ (CodeFix.withTitle Title.Int.Convert.SpecialCase.extractMinusFromNegativeConstant)
+ "let value = -0b1111011y"
+ testCaseAsync "from hex int"
+ <| CodeFix.check
+ server
+ "let value = 0x85y$0"
+ Diagnostics.acceptAll
+ (CodeFix.withTitle Title.Int.Convert.SpecialCase.extractMinusFromNegativeConstant)
+ "let value = -0x7By"
+ testCaseAsync "from oct int"
+ <| CodeFix.check
+ server
+ "let value = 0o205y$0"
+ Diagnostics.acceptAll
+ (CodeFix.withTitle Title.Int.Convert.SpecialCase.extractMinusFromNegativeConstant)
+ "let value = -0o173y"
+ testCaseAsync "does not trigger for decimal int"
+ <| CodeFix.checkNotApplicable
+ server
+ "let value = -123y$0"
+ Diagnostics.acceptAll
+ (CodeFix.withTitle Title.Int.Convert.SpecialCase.extractMinusFromNegativeConstant) ]
+ testList
+ "integrate `-`"
+ [ testCaseAsync "into bin int"
+ <| CodeFix.check
+ server
+ "let value = -0b1111011y$0"
+ Diagnostics.acceptAll
+ (CodeFix.withTitle Title.Int.Convert.SpecialCase.integrateExplicitMinus)
+ "let value = 0b10000101y"
+ testCaseAsync "into hex int"
+ <| CodeFix.check
+ server
+ "let value = -0x7By$0"
+ Diagnostics.acceptAll
+ (CodeFix.withTitle Title.Int.Convert.SpecialCase.integrateExplicitMinus)
+ "let value = 0x85y"
+ testCaseAsync "into oct int"
+ <| CodeFix.check
+ server
+ "let value = -0o173y$0"
+ Diagnostics.acceptAll
+ (CodeFix.withTitle Title.Int.Convert.SpecialCase.integrateExplicitMinus)
+ "let value = 0o205y"
+ testCaseAsync "does not trigger for decimal int"
+ <| CodeFix.checkNotApplicable
+ server
+ "let value = -123y$0"
+ Diagnostics.acceptAll
+ (CodeFix.withTitle Title.Int.Convert.SpecialCase.integrateExplicitMinus) ]
+
+ testList
+ "MinValue"
+ [ testCaseAsync "can remove explicit `-`"
+ <| CodeFix.check
+ server
+ "let value = -0b10000000y$0"
+ Diagnostics.acceptAll
+ (CodeFix.withTitle Title.Int.Convert.SpecialCase.removeExplicitMinusWithMinValue)
+ "let value = 0b10000000y"
+ testCaseAsync "does not trigger for decimal int"
+ <| CodeFix.checkNotApplicable
+ server
+ "let value = -127y$0"
+ Diagnostics.acceptAll
+ (CodeFix.withTitle Title.Int.Convert.SpecialCase.removeExplicitMinusWithMinValue) ]
+
+ testList
+ "use implicit `+`"
+ [ testCaseAsync "can change to positive"
+ <| CodeFix.check
+ server
+ "let value = -0b1111_1101y$0"
+ Diagnostics.acceptAll
+ (CodeFix.withTitle Title.Int.Convert.SpecialCase.useImplicitPlusInPositiveConstantWithMinusSign)
+ "let value = 0b11y" ]
+
+ testList
+ "ensure valid sign"
+ [
+ // QuickFixes might add sign which might lead to invalid code:
+ // ```fsharp
+ // // -91y
+ // let value = 5y+0b1010_0101y
+
+ // // => Convert to decimal
+
+ // let value = 5y+-91y
+ // // ^^
+ // // The type 'sbyte' does not support the operator '+-'
+ // ```
+ //
+ // -> insert space before sign if necessary
+
+ testCaseAsync "add space when new `-` sign immediately after `+`"
+ <| CodeFix.check
+ server
+ "let value = 5y+0b1010_0101y$0"
+ Diagnostics.acceptAll
+ (CodeFix.withTitle Title.Int.Convert.toDecimal)
+ "let value = 5y+ -91y"
+ testCaseAsync "don't add space when `-` with space before"
+ <| CodeFix.check
+ server
+ "let value = 5y+ 0b1010_0101y$0"
+ Diagnostics.acceptAll
+ (CodeFix.withTitle Title.Int.Convert.toDecimal)
+ "let value = 5y+ -91y"
+ testCaseAsync "don't add space when new `-` sign immediately after `(`"
+ <| CodeFix.check
+ server
+ "let value = 5y+(0b1010_0101y$0)"
+ Diagnostics.acceptAll
+ (CodeFix.withTitle Title.Int.Convert.toDecimal)
+ "let value = 5y+(-91y)"
+ testCaseAsync "add space when new `-` sign immediately after `<|`"
+ <| CodeFix.check
+ server
+ "let value = max 5y <|0b1010_0101y$0"
+ Diagnostics.acceptAll
+ (CodeFix.withTitle Title.Int.Convert.toDecimal)
+ "let value = max 5y <| -91y"
+ testCaseAsync "don't add space when no new `-` sign"
+ <| CodeFix.check
+ server
+ "let value = 5y+0b1011011y$0"
+ Diagnostics.acceptAll
+ (CodeFix.withTitle Title.Int.Convert.toDecimal)
+ "let value = 5y+91y"
- // let value = 5y+-91y
- // // ^^
- // // The type 'sbyte' does not support the operator '+-'
- // ```
- //
- // -> insert space before sign if necessary
+ testCaseAsync "add space when convert to other base"
+ <| CodeFix.check
+ server
+ "let value = 5y+0b1010_0101y$0"
+ Diagnostics.acceptAll
+ (CodeFix.withTitle Title.Int.Convert.toDecimal)
+ "let value = 5y+ -91y"
+ testCaseAsync "add space when extract `-`"
+ <| CodeFix.check
+ server
+ "let value = 5y+0b10000101y$0"
+ Diagnostics.acceptAll
+ (CodeFix.withTitle Title.Int.Convert.SpecialCase.extractMinusFromNegativeConstant)
+ "let value = 5y+ -0b1111011y"
- testCaseAsync "add space when new `-` sign immediately after `+`" <|
- CodeFix.check server
- "let value = 5y+0b1010_0101y$0"
- Diagnostics.acceptAll
- (CodeFix.withTitle Title.Int.Convert.toDecimal)
- "let value = 5y+ -91y"
- testCaseAsync "don't add space when `-` with space before" <|
- CodeFix.check server
- "let value = 5y+ 0b1010_0101y$0"
- Diagnostics.acceptAll
- (CodeFix.withTitle Title.Int.Convert.toDecimal)
- "let value = 5y+ -91y"
- testCaseAsync "don't add space when new `-` sign immediately after `(`" <|
- CodeFix.check server
- "let value = 5y+(0b1010_0101y$0)"
- Diagnostics.acceptAll
- (CodeFix.withTitle Title.Int.Convert.toDecimal)
- "let value = 5y+(-91y)"
- testCaseAsync "add space when new `-` sign immediately after `<|`" <|
- CodeFix.check server
- "let value = max 5y <|0b1010_0101y$0"
- Diagnostics.acceptAll
- (CodeFix.withTitle Title.Int.Convert.toDecimal)
- "let value = max 5y <| -91y"
- testCaseAsync "don't add space when no new `-` sign" <|
- CodeFix.check server
- "let value = 5y+0b1011011y$0"
- Diagnostics.acceptAll
- (CodeFix.withTitle Title.Int.Convert.toDecimal)
- "let value = 5y+91y"
+ testCaseAsync "add space when convert to `-infinity`"
+ <| CodeFix.check
+ server
+ "let value = 5.0+0o1777600000000000000000LF$0"
+ Diagnostics.acceptAll
+ (CodeFix.withTitle (Title.replaceWith "-infinity"))
+ "let value = 5.0+ -infinity" ] ])
- testCaseAsync "add space when convert to other base" <|
- CodeFix.check server
- "let value = 5y+0b1010_0101y$0"
- Diagnostics.acceptAll
- (CodeFix.withTitle Title.Int.Convert.toDecimal)
- "let value = 5y+ -91y"
- testCaseAsync "add space when extract `-`" <|
- CodeFix.check server
- "let value = 5y+0b10000101y$0"
- Diagnostics.acceptAll
- (CodeFix.withTitle Title.Int.Convert.SpecialCase.extractMinusFromNegativeConstant)
- "let value = 5y+ -0b1111011y"
+let tests state =
+ testList
+ (nameof AdjustConstant)
+ [ ConvertIntToOtherBase.tests state
+ ConvertIntToOtherBase.Float.tests state
+ ConvertCharToOtherForm.tests state
+ ConvertByteBetweenIntAndChar.tests state
- testCaseAsync "add space when convert to `-infinity`" <|
- CodeFix.check server
- "let value = 5.0+0o1777600000000000000000LF$0"
- Diagnostics.acceptAll
- (CodeFix.withTitle (Title.replaceWith "-infinity"))
- "let value = 5.0+ -infinity"
- ]
- ])
+ ReplaceWithName.tests state
+ SignHelpers.tests state
-let tests state =
- testList (nameof AdjustConstant) [
- ConvertIntToOtherBase.tests state
- ConvertIntToOtherBase.Float.tests state
- ConvertCharToOtherForm.tests state
- ConvertByteBetweenIntAndChar.tests state
-
- ReplaceWithName.tests state
- SignHelpers.tests state
-
- AddDigitGroupSeparator.tests state
- ]
+ AddDigitGroupSeparator.tests state ]
diff --git a/test/FsAutoComplete.Tests.Lsp/CodeFixTests/RenameParamToMatchSignatureTests.fs b/test/FsAutoComplete.Tests.Lsp/CodeFixTests/RenameParamToMatchSignatureTests.fs
index daa72bc34..89e4c4e75 100644
--- a/test/FsAutoComplete.Tests.Lsp/CodeFixTests/RenameParamToMatchSignatureTests.fs
+++ b/test/FsAutoComplete.Tests.Lsp/CodeFixTests/RenameParamToMatchSignatureTests.fs
@@ -18,38 +18,36 @@ let tests state =
// requires `fsi` and corresponding `fs` file (and a project!)
// -> cannot use untitled doc
// -> use existing files, but load with text specified in tests
- let path = Path.Combine(__SOURCE_DIRECTORY__, @"../TestCases/CodeFixTests/RenameParamToMatchSignature/")
+ let path =
+ Path.Combine(__SOURCE_DIRECTORY__, @"../TestCases/CodeFixTests/RenameParamToMatchSignature/")
+
let (fsiFile, fsFile) = ("Code.fsi", "Code.fs")
- serverTestList (nameof RenameParamToMatchSignature) state defaultConfigDto (Some path) (fun server -> [
- let checkWithFsi
- fsiSource
- fsSourceWithCursor
- selectCodeFix
- fsSourceExpected
- = async {
- let fsiSource = fsiSource |> Text.trimTripleQuotation
- let (cursor, fsSource) =
- fsSourceWithCursor
- |> Text.trimTripleQuotation
- |> Cursor.assertExtractRange
- let! (fsiDoc, diags) = server |> Server.openDocumentWithText fsiFile fsiSource
- use fsiDoc = fsiDoc
- Expect.isEmpty diags "There should be no diagnostics in fsi doc"
- let! (fsDoc, diags) = server |> Server.openDocumentWithText fsFile fsSource
- use fsDoc = fsDoc
-
- do!
- checkFixAt
- (fsDoc, diags)
- (fsSource, cursor)
- (Diagnostics.expectCode "3218")
- selectCodeFix
- (After (fsSourceExpected |> Text.trimTripleQuotation))
- }
-
- testCaseAsync "can rename parameter in F# function" <|
- checkWithFsi
+ serverTestList (nameof RenameParamToMatchSignature) state defaultConfigDto (Some path) (fun server ->
+ [ let checkWithFsi fsiSource fsSourceWithCursor selectCodeFix fsSourceExpected =
+ async {
+ let fsiSource = fsiSource |> Text.trimTripleQuotation
+
+ let (cursor, fsSource) =
+ fsSourceWithCursor |> Text.trimTripleQuotation |> Cursor.assertExtractRange
+
+ let! (fsiDoc, diags) = server |> Server.openDocumentWithText fsiFile fsiSource
+ use _fsiDoc = fsiDoc
+ Expect.isEmpty diags "There should be no diagnostics in fsi doc"
+ let! (fsDoc, diags) = server |> Server.openDocumentWithText fsFile fsSource
+ use fsDoc = fsDoc
+
+ do!
+ checkFixAt
+ (fsDoc, diags)
+ (fsSource, cursor)
+ (Diagnostics.expectCode "3218")
+ selectCodeFix
+ (After(fsSourceExpected |> Text.trimTripleQuotation))
+ }
+
+ testCaseAsync "can rename parameter in F# function"
+ <| checkWithFsi
"""
module Code
@@ -66,8 +64,9 @@ let tests state =
let f value1 = value1 + 1
"""
- testCaseAsync "can rename parameter with backticks in signature in F# function" <|
- checkWithFsi
+
+ testCaseAsync "can rename parameter with backticks in signature in F# function"
+ <| checkWithFsi
"""
module Code
@@ -84,8 +83,9 @@ let tests state =
let f ``my value2`` = ``my value2`` + 1
"""
- testCaseAsync "can rename parameter with backticks in implementation in F# function" <|
- checkWithFsi
+
+ testCaseAsync "can rename parameter with backticks in implementation in F# function"
+ <| checkWithFsi
"""
module Code
@@ -102,8 +102,9 @@ let tests state =
let f value3 = value3 + 1
"""
- testCaseAsync "can rename all usage in F# function" <|
- checkWithFsi
+
+ testCaseAsync "can rename all usage in F# function"
+ <| checkWithFsi
"""
module Code
@@ -128,8 +129,9 @@ let tests state =
let v = a + b
v + x * y
"""
- testCaseAsync "can rename parameter with type in F# function" <|
- checkWithFsi
+
+ testCaseAsync "can rename parameter with type in F# function"
+ <| checkWithFsi
"""
module Code
@@ -146,8 +148,9 @@ let tests state =
let f (value5: int) = value5 + 1
"""
- testCaseAsync "can rename parameter in constructor" <|
- checkWithFsi
+
+ testCaseAsync "can rename parameter in constructor"
+ <| checkWithFsi
"""
module Code
@@ -167,8 +170,9 @@ let tests state =
type T(value6: int) =
let _ = value6 + 3
"""
- testCaseAsync "can rename parameter in member" <|
- checkWithFsi
+
+ testCaseAsync "can rename parameter in member"
+ <| checkWithFsi
"""
module Code
@@ -189,8 +193,9 @@ let tests state =
type T() =
member _.F(value7) = value7 + 1
"""
- testCaseAsync "can rename parameter with ' in signature in F# function" <|
- checkWithFsi
+
+ testCaseAsync "can rename parameter with ' in signature in F# function"
+ <| checkWithFsi
"""
module Code
@@ -207,8 +212,9 @@ let tests state =
let f value8' = value8' + 1
"""
- testCaseAsync "can rename parameter with ' in implementation in F# function" <|
- checkWithFsi
+
+ testCaseAsync "can rename parameter with ' in implementation in F# function"
+ <| checkWithFsi
"""
module Code
@@ -225,8 +231,9 @@ let tests state =
let f value9 = value9 + 1
"""
- testCaseAsync "can rename parameter with ' (not in last place) in signature in F# function" <|
- checkWithFsi
+
+ testCaseAsync "can rename parameter with ' (not in last place) in signature in F# function"
+ <| checkWithFsi
"""
module Code
@@ -243,8 +250,9 @@ let tests state =
let f v10'2 = v10'2 + 1
"""
- testCaseAsync "can rename parameter with ' (not in last place) in implementation in F# function" <|
- checkWithFsi
+
+ testCaseAsync "can rename parameter with ' (not in last place) in implementation in F# function"
+ <| checkWithFsi
"""
module Code
@@ -261,8 +269,9 @@ let tests state =
let f value11 = value11 + 1
"""
- testCaseAsync "can rename parameter with multiple ' in signature in F# function" <|
- checkWithFsi
+
+ testCaseAsync "can rename parameter with multiple ' in signature in F# function"
+ <| checkWithFsi
"""
module Code
@@ -279,8 +288,9 @@ let tests state =
let f value12'v'2 = value12'v'2 + 1
"""
- testCaseAsync "can rename parameter with multiple ' in implementation in F# function" <|
- checkWithFsi
+
+ testCaseAsync "can rename parameter with multiple ' in implementation in F# function"
+ <| checkWithFsi
"""
module Code
@@ -297,8 +307,9 @@ let tests state =
let f value13 = value13 + 1
"""
- itestCaseAsync "can handle `' and implementation '` in impl name" <|
- checkWithFsi
+
+ itestCaseAsync "can handle `' and implementation '` in impl name"
+ <| checkWithFsi
"""
module Code
@@ -315,9 +326,9 @@ let tests state =
let f value14 = value14 + 1
"""
- //ENHANCEMENT: correctly detect below. Currently: detects sig name `sig`
- itestCaseAsync "can handle `' and implementation '` in sig name" <|
- checkWithFsi
+ //ENHANCEMENT: correctly detect below. Currently: detects sig name `sig`
+ itestCaseAsync "can handle `' and implementation '` in sig name"
+ <| checkWithFsi
"""
module Code
@@ -333,5 +344,4 @@ let tests state =
module Code
let f ``sig' and implementation 'impl' do not match`` = ``sig' and implementation 'impl' do not match`` + 1
- """
- ])
+ """ ])
diff --git a/test/FsAutoComplete.Tests.Lsp/CodeFixTests/Tests.fs b/test/FsAutoComplete.Tests.Lsp/CodeFixTests/Tests.fs
index 724a5012f..f18f62585 100644
--- a/test/FsAutoComplete.Tests.Lsp/CodeFixTests/Tests.fs
+++ b/test/FsAutoComplete.Tests.Lsp/CodeFixTests/Tests.fs
@@ -2054,7 +2054,7 @@ let private generateUnionCasesTests state =
UnionCaseStubGenerationBody = Some "failwith \"---\"" }
serverTestList (nameof GenerateUnionCases) state config None (fun server ->
- [ let selectCodeFix = CodeFix.withTitle GenerateUnionCases.title
+ [ let _selectCodeFix = CodeFix.withTitle GenerateUnionCases.title
testCaseAsync "can generate match cases for a simple DU"
<| CodeFix.check
@@ -2675,8 +2675,7 @@ let private renameUnusedValue state =
let private replaceWithSuggestionTests state =
serverTestList (nameof ReplaceWithSuggestion) state defaultConfigDto None (fun server ->
- [ let selectCodeFix replacement =
- CodeFix.withTitle (ReplaceWithSuggestion.title replacement)
+ [ let selectCodeFix replacement = CodeFix.withTitle (ReplaceWithSuggestion.title replacement)
let validateDiags (diags: Diagnostic[]) =
Diagnostics.expectCode "39" diags
@@ -3305,50 +3304,51 @@ let private removePatternArgumentTests state =
let (None) = None
""" ])
-let tests textFactory state = testList "CodeFix-tests" [
- HelpersTests.tests textFactory
- AddExplicitTypeAnnotationTests.tests state
- AdjustConstantTests.tests state
- ToInterpolatedStringTests.tests state
- ToInterpolatedStringTests.unavailableTests state
- addMissingEqualsToTypeDefinitionTests state
- addMissingFunKeywordTests state
- addMissingInstanceMemberTests state
- addMissingRecKeywordTests state
- addMissingXmlDocumentationTests state
- addNewKeywordToDisposableConstructorInvocationTests state
- addTypeToIndeterminateValueTests state
- changeDerefBangToValueTests state
- changeDowncastToUpcastTests state
- changeEqualsInFieldTypeToColonTests state
- changePrefixNegationToInfixSubtractionTests state
- changeRefCellDerefToNotTests state
- changeTypeOfNameToNameOfTests state
- convertBangEqualsToInequalityTests state
- convertCSharpLambdaToFSharpLambdaTests state
- convertDoubleEqualsToSingleEqualsTests state
- convertInvalidRecordToAnonRecordTests state
- convertPositionalDUToNamedTests state
- convertTripleSlashCommentToXmlTaggedDocTests state
- addPrivateAccessModifierTests state
- GenerateAbstractClassStubTests.tests state
- generateRecordStubTests state
- generateUnionCasesTests state
- generateXmlDocumentationTests state
- ImplementInterfaceTests.tests state
- makeDeclarationMutableTests state
- makeOuterBindingRecursiveTests state
- removeRedundantQualifierTests state
- removeUnnecessaryReturnOrYieldTests state
- removeUnusedBindingTests state
- removeUnusedOpensTests state
- RenameParamToMatchSignatureTests.tests state
- renameUnusedValue state
- replaceWithSuggestionTests state
- resolveNamespaceTests state
- useMutationWhenValueIsMutableTests state
- useTripleQuotedInterpolationTests state
- wrapExpressionInParenthesesTests state
- removeRedundantAttributeSuffixTests state
- removePatternArgumentTests state
-]
+let tests textFactory state =
+ testList
+ "CodeFix-tests"
+ [ HelpersTests.tests textFactory
+ AddExplicitTypeAnnotationTests.tests state
+ AdjustConstantTests.tests state
+ ToInterpolatedStringTests.tests state
+ ToInterpolatedStringTests.unavailableTests state
+ addMissingEqualsToTypeDefinitionTests state
+ addMissingFunKeywordTests state
+ addMissingInstanceMemberTests state
+ addMissingRecKeywordTests state
+ addMissingXmlDocumentationTests state
+ addNewKeywordToDisposableConstructorInvocationTests state
+ addTypeToIndeterminateValueTests state
+ changeDerefBangToValueTests state
+ changeDowncastToUpcastTests state
+ changeEqualsInFieldTypeToColonTests state
+ changePrefixNegationToInfixSubtractionTests state
+ changeRefCellDerefToNotTests state
+ changeTypeOfNameToNameOfTests state
+ convertBangEqualsToInequalityTests state
+ convertCSharpLambdaToFSharpLambdaTests state
+ convertDoubleEqualsToSingleEqualsTests state
+ convertInvalidRecordToAnonRecordTests state
+ convertPositionalDUToNamedTests state
+ convertTripleSlashCommentToXmlTaggedDocTests state
+ addPrivateAccessModifierTests state
+ GenerateAbstractClassStubTests.tests state
+ generateRecordStubTests state
+ generateUnionCasesTests state
+ generateXmlDocumentationTests state
+ ImplementInterfaceTests.tests state
+ makeDeclarationMutableTests state
+ makeOuterBindingRecursiveTests state
+ removeRedundantQualifierTests state
+ removeUnnecessaryReturnOrYieldTests state
+ removeUnusedBindingTests state
+ removeUnusedOpensTests state
+ RenameParamToMatchSignatureTests.tests state
+ renameUnusedValue state
+ replaceWithSuggestionTests state
+ resolveNamespaceTests state
+ useMutationWhenValueIsMutableTests state
+ useTripleQuotedInterpolationTests state
+ wrapExpressionInParenthesesTests state
+ removeRedundantAttributeSuffixTests state
+ removePatternArgumentTests state ]
diff --git a/test/FsAutoComplete.Tests.Lsp/CodeFixTests/Utils.fs b/test/FsAutoComplete.Tests.Lsp/CodeFixTests/Utils.fs
index b72087ee3..7ab75a18e 100644
--- a/test/FsAutoComplete.Tests.Lsp/CodeFixTests/Utils.fs
+++ b/test/FsAutoComplete.Tests.Lsp/CodeFixTests/Utils.fs
@@ -6,13 +6,16 @@ open FsAutoComplete.Logging
module Diagnostics =
let expectCode code (diags: Diagnostic[]) =
- Expecto.Flip.Expect.exists
+ Expecto.Flip.Expect.exists
$"There should be a Diagnostic with code %s{code}"
(fun (d: Diagnostic) -> d.Code = Some code)
diags
+
let acceptAll = ignore
- let private logger = FsAutoComplete.Logging.LogProvider.getLoggerByName "CodeFixes.Diagnostics"
+ let private logger =
+ FsAutoComplete.Logging.LogProvider.getLoggerByName "CodeFixes.Diagnostics"
+
/// Usage: `(Diagnostics.log >> Diagnostics.expectCode "XXX")`
/// Logs as `info`
let log (diags: Diagnostic[]) =
@@ -21,10 +24,13 @@ module Diagnostics =
>> Log.addContext "count" diags.Length
>> Log.addContextDestructured "diags" diags
)
+
diags
module CodeFix =
- let private logger = FsAutoComplete.Logging.LogProvider.getLoggerByName "CodeFixes.CodeFix"
+ let private logger =
+ FsAutoComplete.Logging.LogProvider.getLoggerByName "CodeFixes.CodeFix"
+
/// Usage: `(CodeFix.log >> CodeFix.withTitle "XXX")`
/// Logs as `info`
let log (codeActions: CodeAction[]) =
@@ -33,14 +39,15 @@ module CodeFix =
>> Log.addContext "count" codeActions.Length
>> Log.addContextDestructured "codeActions" codeActions
)
+
codeActions
/// `ignore testCaseAsync`
-///
+///
/// Like `testCaseAsync`, but test gets completely ignored.
/// Unlike `ptestCaseAsync` (pending), this here doesn't even show up in Expecto summary.
-///
+///
/// -> Used to mark issues & shortcomings in CodeFixes, but without any (immediate) intention to fix
-/// (vs. `pending` -> marked for fixing)
+/// (vs. `pending` -> marked for fixing)
/// -> ~ uncommenting tests without actual uncommenting
-let itestCaseAsync name test = ()
+let itestCaseAsync _name _test = ()
diff --git a/test/FsAutoComplete.Tests.Lsp/CodeLensTests.fs b/test/FsAutoComplete.Tests.Lsp/CodeLensTests.fs
index f5977e55b..655eff2aa 100644
--- a/test/FsAutoComplete.Tests.Lsp/CodeLensTests.fs
+++ b/test/FsAutoComplete.Tests.Lsp/CodeLensTests.fs
@@ -14,26 +14,21 @@ open Newtonsoft.Json.Linq
open Helpers.Expecto.ShadowedTimeouts
module private CodeLens =
- let assertNoDiagnostics (ds: Diagnostic []) =
+ let assertNoDiagnostics (ds: Diagnostic[]) =
match ds with
| [||] -> Ok()
| ds -> Error $"Expected no diagnostics, but got %A{ds}"
let check server text checkLenses =
asyncResult {
- let textRange, text =
- text
- |> Text.trimTripleQuotation
- |> Cursor.assertExtractRange
+ let textRange, text = text |> Text.trimTripleQuotation |> Cursor.assertExtractRange
let! (doc, diags) = Server.createUntitledDocument text server
do! assertNoDiagnostics diags
let p: CodeLensParams = { TextDocument = doc.TextDocumentIdentifier }
- let! lenses =
- doc.Server.Server.TextDocumentCodeLens p
- |> AsyncResult.mapError string
+ let! lenses = doc.Server.Server.TextDocumentCodeLens p |> AsyncResult.mapError string
let! resolved =
Option.toList lenses
@@ -53,72 +48,80 @@ module private CodeLens =
let tests state =
serverTestList (nameof CodeLens) state defaultConfigDto None (fun server ->
- [ testCaseAsync
- "can show codelens for type annotation" <|
- CodeLens.check server
- """
+ [ testCaseAsync "can show codelens for type annotation"
+ <| CodeLens.check server """
module X =
$0let func x = x + 1$0
- """ (fun (doc, lenses) ->
- Expect.hasLength lenses 2 "should have a type lens and a reference lens"
- let typeLens = lenses[0]
- Expect.equal typeLens.Command.Value.Title "int -> int" "first lens should be a type hint of int to int"
- Expect.isNone typeLens.Command.Value.Arguments "No data required for type lenses"
- Expect.equal typeLens.Command.Value.Command "" "No command for type lenses"
- )
-
- testCaseAsync
- "can show codelens for 0 reference count" <|
- CodeLens.check server
- """
+ """ (fun (_doc, lenses) ->
+ Expect.hasLength lenses 2 "should have a type lens and a reference lens"
+ let typeLens = lenses[0]
+ Expect.equal typeLens.Command.Value.Title "int -> int" "first lens should be a type hint of int to int"
+ Expect.isNone typeLens.Command.Value.Arguments "No data required for type lenses"
+ Expect.equal typeLens.Command.Value.Command "" "No command for type lenses")
+
+ testCaseAsync "can show codelens for 0 reference count"
+ <| CodeLens.check server """
module X =
$0let func x = x + 1$0
- """ (fun (doc, lenses) ->
- Expect.hasLength lenses 2 "should have a type lens and a reference lens"
- let referenceLens = lenses[1]
- let emptyCommand = Some { Title = "0 References"; Arguments = None; Command = "" }
- Expect.equal referenceLens.Command emptyCommand "There should be no command or args for zero references"
- )
- testCaseAsync
- "can show codelens for multi reference count" <|
- CodeLens.check server
- """
+ """ (fun (_doc, lenses) ->
+ Expect.hasLength lenses 2 "should have a type lens and a reference lens"
+ let referenceLens = lenses[1]
+
+ let emptyCommand =
+ Some
+ { Title = "0 References"
+ Arguments = None
+ Command = "" }
+
+ Expect.equal referenceLens.Command emptyCommand "There should be no command or args for zero references")
+ testCaseAsync "can show codelens for multi reference count"
+ <| CodeLens.check server """
module X =
$0let func x = x + 1$0
let doThing () = func 1
""" (fun (doc, lenses) ->
- Expect.hasLength lenses 2 "should have a type lens and a reference lens"
- let referenceLens = lenses[1]
- Expect.isSome referenceLens.Command "There should be a command for multiple references"
- let referenceCommand = referenceLens.Command.Value
- Expect.equal referenceCommand.Title "1 References" "There should be a title for multiple references"
- Expect.equal referenceCommand.Command "fsharp.showReferences" "There should be a command for multiple references"
- Expect.isSome referenceCommand.Arguments "There should be arguments for multiple references"
- let args = referenceCommand.Arguments.Value
- Expect.equal args.Length 3 "There should be 2 args"
- let filePath, triggerPos, referenceRanges =
- args[0].Value(),
- (args[1] :?> JObject).ToObject(),
- (args[2] :?> JArray) |> Seq.map (fun t -> (t:?>JObject).ToObject()) |> Array.ofSeq
- Expect.equal filePath doc.Uri "File path should be the doc we're checking"
- Expect.equal triggerPos { Line = 1; Character = 6 } "Position should be 1:6"
- Expect.hasLength referenceRanges 1 "There should be 1 reference range for the `func` function"
- Expect.equal referenceRanges[0] { Uri = doc.Uri; Range = { Start = { Line = 3; Character = 19 }; End = { Line = 3; Character = 23 } } } "Reference range should be 0:0"
- )
- testCaseAsync "can show reference counts for 1-character identifier" <|
- CodeLens.check server
- """
- $0let f () = ""$0
- """ (fun (doc, lenses) ->
- Expect.hasLength lenses 2 "should have a type lens and a reference lens"
- let referenceLens = lenses[1]
- Expect.isSome referenceLens.Command "There should be a command for multiple references"
- let referenceCommand = referenceLens.Command.Value
- Expect.equal referenceCommand.Title "0 References" "There should be a title for multiple references"
- Expect.equal referenceCommand.Command "" "There should be no command for multiple references"
- Expect.isNone referenceCommand.Arguments "There should be arguments for multiple references"
- )
- ]
- )
+ Expect.hasLength lenses 2 "should have a type lens and a reference lens"
+ let referenceLens = lenses[1]
+ Expect.isSome referenceLens.Command "There should be a command for multiple references"
+ let referenceCommand = referenceLens.Command.Value
+ Expect.equal referenceCommand.Title "1 References" "There should be a title for multiple references"
+
+ Expect.equal
+ referenceCommand.Command
+ "fsharp.showReferences"
+ "There should be a command for multiple references"
+
+ Expect.isSome referenceCommand.Arguments "There should be arguments for multiple references"
+ let args = referenceCommand.Arguments.Value
+ Expect.equal args.Length 3 "There should be 2 args"
+ let filePath, triggerPos, referenceRanges =
+ args[0].Value(),
+ (args[1] :?> JObject).ToObject(),
+ (args[2] :?> JArray)
+ |> Seq.map (fun t -> (t :?> JObject).ToObject())
+ |> Array.ofSeq
+
+ Expect.equal filePath doc.Uri "File path should be the doc we're checking"
+ Expect.equal triggerPos { Line = 1; Character = 6 } "Position should be 1:6"
+ Expect.hasLength referenceRanges 1 "There should be 1 reference range for the `func` function"
+
+ Expect.equal
+ referenceRanges[0]
+ { Uri = doc.Uri
+ Range =
+ { Start = { Line = 3; Character = 19 }
+ End = { Line = 3; Character = 23 } } }
+ "Reference range should be 0:0")
+ testCaseAsync "can show reference counts for 1-character identifier"
+ <| CodeLens.check server """
+ $0let f () = ""$0
+ """ (fun (_doc, lenses) ->
+ Expect.hasLength lenses 2 "should have a type lens and a reference lens"
+ let referenceLens = lenses[1]
+ Expect.isSome referenceLens.Command "There should be a command for multiple references"
+ let referenceCommand = referenceLens.Command.Value
+ Expect.equal referenceCommand.Title "0 References" "There should be a title for multiple references"
+ Expect.equal referenceCommand.Command "" "There should be no command for multiple references"
+ Expect.isNone referenceCommand.Arguments "There should be arguments for multiple references") ])
diff --git a/test/FsAutoComplete.Tests.Lsp/CompletionTests.fs b/test/FsAutoComplete.Tests.Lsp/CompletionTests.fs
index 2c12d3395..b519a34ac 100644
--- a/test/FsAutoComplete.Tests.Lsp/CompletionTests.fs
+++ b/test/FsAutoComplete.Tests.Lsp/CompletionTests.fs
@@ -20,7 +20,7 @@ let tests state =
let tdop: DidOpenTextDocumentParams = { TextDocument = loadDocument path }
do! server.TextDocumentDidOpen tdop
- let! diagnostics =
+ let! _diagnostics =
waitForParseResultsForFile "Script.fsx" events
|> AsyncResult.bimap (fun _ -> failtest "Should have had errors") (fun e -> e)
@@ -759,7 +759,7 @@ let autoOpenTests state =
{ Line = pos.Line
Character = indentation }
- let getQuickFix (server: IFSharpLspServer, path: string) (word: string, ns: string) (cursor: Position) =
+ let getQuickFix (server: IFSharpLspServer, path: string) (word: string, _ns: string) (cursor: Position) =
async {
let p =
{ CodeActionParams.TextDocument = { Uri = Path.FilePathToUri path }
@@ -1062,22 +1062,31 @@ let fullNameExternalAutocompleteTest state =
let count =
items
- |> Array.distinctBy (function Ok x -> x.Detail | Error _ -> None)
+ |> Array.distinctBy (function
+ | Ok x -> x.Detail
+ | Error _ -> None)
|> Array.length
Expect.equal count items.Length "These completions doesn't have different description"
}
|> AsyncResult.bimap id (fun e -> failwithf "%O" e))
- makeAutocompleteTest
- serverConfig
- "Check Autocomplete for System.Text.RegularExpressions.Regex"
- (4, 5)
- (fun res ->
- let n = res.Items |> Array.tryFind (fun i -> i.Label = "Regex (System.Text.RegularExpressions)")
- Expect.isSome n "Completion doesn't exist"
- Expect.equal n.Value.InsertText (Some "System.Text.RegularExpressions.Regex") "Autocomplete for Regex is not System.Text.RegularExpressions.Regex"
- Expect.equal n.Value.FilterText (Some "RegexSystem.Text.RegularExpressions.Regex") "Autocomplete for Regex is not System.Text.RegularExpressions.Regex")
+ makeAutocompleteTest serverConfig "Check Autocomplete for System.Text.RegularExpressions.Regex" (4, 5) (fun res ->
+ let n =
+ res.Items
+ |> Array.tryFind (fun i -> i.Label = "Regex (System.Text.RegularExpressions)")
+
+ Expect.isSome n "Completion doesn't exist"
+
+ Expect.equal
+ n.Value.InsertText
+ (Some "System.Text.RegularExpressions.Regex")
+ "Autocomplete for Regex is not System.Text.RegularExpressions.Regex"
+
+ Expect.equal
+ n.Value.FilterText
+ (Some "RegexSystem.Text.RegularExpressions.Regex")
+ "Autocomplete for Regex is not System.Text.RegularExpressions.Regex")
makeAutocompleteTest serverConfig "Autocomplete for Result is just Result" (5, 6) (fun res ->
let n = res.Items |> Array.tryFind (fun i -> i.Label = "Result")
diff --git a/test/FsAutoComplete.Tests.Lsp/CoreTests.fs b/test/FsAutoComplete.Tests.Lsp/CoreTests.fs
index ed6a8b761..29d1db2e9 100644
--- a/test/FsAutoComplete.Tests.Lsp/CoreTests.fs
+++ b/test/FsAutoComplete.Tests.Lsp/CoreTests.fs
@@ -32,7 +32,7 @@ let initTests createServer =
let tempDir =
Path.Combine(Path.GetTempPath(), "FsAutoComplete.Tests", Guid.NewGuid().ToString())
- let (server: IFSharpLspServer, event) = createServer ()
+ let (server: IFSharpLspServer, _event) = createServer ()
let p: InitializeParams =
{ ProcessId = Some 1
@@ -59,9 +59,11 @@ let initTests createServer =
Expect.equal
res.Capabilities.CodeActionProvider
- (Some (U2.Second
- { CodeActionOptions.ResolveProvider = None
- CodeActionOptions.CodeActionKinds = None }))
+ (Some(
+ U2.Second
+ { CodeActionOptions.ResolveProvider = None
+ CodeActionOptions.CodeActionKinds = None }
+ ))
"Code Action Provider"
Expect.equal
@@ -75,15 +77,18 @@ let initTests createServer =
Expect.equal res.Capabilities.DocumentLinkProvider None "Document Link Provider"
Expect.equal res.Capabilities.DocumentOnTypeFormattingProvider None "Document OnType Formatting Provider"
Expect.equal res.Capabilities.DocumentRangeFormattingProvider (Some true) "Document Range Formatting Provider"
- Expect.equal res.Capabilities.DocumentSymbolProvider (Some (U2.Second { Label = Some "F#" })) "Document Symbol Provider"
+
+ Expect.equal
+ res.Capabilities.DocumentSymbolProvider
+ (Some(U2.Second { Label = Some "F#" }))
+ "Document Symbol Provider"
+
Expect.equal res.Capabilities.ExecuteCommandProvider None "Execute Command Provider"
Expect.equal res.Capabilities.Experimental None "Experimental"
Expect.equal res.Capabilities.HoverProvider (Some true) "Hover Provider"
Expect.equal res.Capabilities.ImplementationProvider (Some true) "Implementation Provider"
Expect.equal res.Capabilities.ReferencesProvider (Some true) "References Provider"
- Expect.equal res.Capabilities.RenameProvider (Some(U2.Second {
- PrepareProvider = Some true
- })) "Rename Provider"
+ Expect.equal res.Capabilities.RenameProvider (Some(U2.Second { PrepareProvider = Some true })) "Rename Provider"
Expect.equal
res.Capabilities.SignatureHelpProvider
@@ -100,9 +105,14 @@ let initTests createServer =
Expect.equal res.Capabilities.TextDocumentSync (Some td) "Text Document Provider"
Expect.equal res.Capabilities.TypeDefinitionProvider (Some true) "Type Definition Provider"
- Expect.equal res.Capabilities.WorkspaceSymbolProvider (Some (U2.Second { ResolveProvider = Some true })) "Workspace Symbol Provider"
+
+ Expect.equal
+ res.Capabilities.WorkspaceSymbolProvider
+ (Some(U2.Second { ResolveProvider = Some true }))
+ "Workspace Symbol Provider"
+
Expect.equal res.Capabilities.FoldingRangeProvider (Some true) "Folding Range Provider active"
- | Result.Error e -> failtest "Initialization failed"
+ | Result.Error _e -> failtest "Initialization failed"
})
///Tests for getting document symbols
@@ -110,7 +120,7 @@ let documentSymbolTest state =
let server =
async {
let path = Path.Combine(__SOURCE_DIRECTORY__, "TestCases", "DocumentSymbolTest")
- let! (server, event) = serverInitialize path defaultConfigDto state
+ let! (server, _event) = serverInitialize path defaultConfigDto state
let path = Path.Combine(path, "Script.fsx")
let tdop: DidOpenTextDocumentParams = { TextDocument = loadDocument path }
do! server.TextDocumentDidOpen tdop
@@ -137,7 +147,7 @@ let documentSymbolTest state =
res
(fun n -> n.Name = "MyDateTime" && n.Kind = SymbolKind.Class)
"Document symbol contains given symbol"
- | Result.Ok(Some(U2.Second res)) -> raise (NotImplementedException("DocumentSymbol isn't used in FSAC yet"))
+ | Result.Ok(Some(U2.Second _res)) -> raise (NotImplementedException("DocumentSymbol isn't used in FSAC yet"))
}) ]
let foldingTests state =
@@ -177,29 +187,32 @@ let tooltipTests state =
let (|Signature|_|) (hover: Hover) =
match hover with
| { Contents = MarkedStrings [| MarkedString.WithLanguage { Language = "fsharp"; Value = tooltip }
- MarkedString.String docComment
- MarkedString.String fullname
- MarkedString.String assembly |] } -> Some tooltip
+ MarkedString.String _docComment
+ MarkedString.String _fullname
+ MarkedString.String _assembly |] } -> Some tooltip
| { Contents = MarkedStrings [| MarkedString.WithLanguage { Language = "fsharp"; Value = tooltip }
- MarkedString.String docComment
- MarkedString.String showDocumentationLink
- MarkedString.String fullname
- MarkedString.String assembly |] } -> Some tooltip
+ MarkedString.String _docComment
+ MarkedString.String _showDocumentationLink
+ MarkedString.String _fullname
+ MarkedString.String _assembly |] } -> Some tooltip
| _ -> None
let (|Description|_|) (hover: Hover) =
match hover with
- | { Contents = MarkedStrings [| MarkedString.WithLanguage { Language = "fsharp"; Value = tooltip }
+ | { Contents = MarkedStrings [| MarkedString.WithLanguage { Language = "fsharp"
+ Value = _tooltip }
MarkedString.String description |] } -> Some description
- | { Contents = MarkedStrings [| MarkedString.WithLanguage { Language = "fsharp"; Value = tooltip }
+ | { Contents = MarkedStrings [| MarkedString.WithLanguage { Language = "fsharp"
+ Value = _tooltip }
MarkedString.String description
- MarkedString.String fullname
- MarkedString.String assembly |] } -> Some description
- | { Contents = MarkedStrings [| MarkedString.WithLanguage { Language = "fsharp"; Value = tooltip }
+ MarkedString.String _fullname
+ MarkedString.String _assembly |] } -> Some description
+ | { Contents = MarkedStrings [| MarkedString.WithLanguage { Language = "fsharp"
+ Value = _tooltip }
MarkedString.String description
- MarkedString.String showDocumentationLink
- MarkedString.String fullname
- MarkedString.String assembly |] } -> Some description
+ MarkedString.String _showDocumentationLink
+ MarkedString.String _fullname
+ MarkedString.String _assembly |] } -> Some description
| _ -> None
let server =
@@ -259,12 +272,6 @@ let tooltipTests state =
let verifyDescription line character expectedDescription =
verifyDescriptionImpl testCaseAsync line character expectedDescription
- let pverifyDescription reason line character expectedDescription =
- verifyDescriptionImpl ptestCaseAsync line character expectedDescription
-
- let fverifyDescription line character expectedDescription =
- verifyDescriptionImpl ftestCaseAsync line character expectedDescription
-
testSequenced
<| testList
"tooltip evaluation"
diff --git a/test/FsAutoComplete.Tests.Lsp/ExtensionsTests.fs b/test/FsAutoComplete.Tests.Lsp/ExtensionsTests.fs
index c0b1a6837..210096409 100644
--- a/test/FsAutoComplete.Tests.Lsp/ExtensionsTests.fs
+++ b/test/FsAutoComplete.Tests.Lsp/ExtensionsTests.fs
@@ -77,10 +77,7 @@ let uriTests =
testList
"Uri tests"
- [ testList
- "roundtrip tests"
- (samples
- |> List.map (fun (uriForm, filePath) -> verifyUri uriForm filePath))
+ [ testList "roundtrip tests" (samples |> List.map (fun (uriForm, filePath) -> verifyUri uriForm filePath))
testList
"fileName to uri tests"
(samples
@@ -91,7 +88,14 @@ let linterTests state =
let server =
async {
let path = Path.Combine(__SOURCE_DIRECTORY__, "TestCases", "LinterTest")
- let! (server, events) = serverInitialize path { defaultConfigDto with Linter = Some true } state
+
+ let! (server, events) =
+ serverInitialize
+ path
+ { defaultConfigDto with
+ Linter = Some true }
+ state
+
let path = Path.Combine(path, "Script.fsx")
let tdop: DidOpenTextDocumentParams = { TextDocument = loadDocument path }
do! server.TextDocumentDidOpen tdop
@@ -274,14 +278,23 @@ let formattingTests state =
do! server.TextDocumentDidOpen { TextDocument = loadDocument sourceFile }
match! waitForParseResultsForFile (Path.GetFileName sourceFile) events with
- | Ok () ->
+ | Ok() ->
match!
server.TextDocumentFormatting
{ TextDocument = { Uri = Path.FilePathToUri sourceFile }
- Options = {TabSize = 4; InsertSpaces = true; TrimTrailingWhitespace = None; InsertFinalNewline = None; TrimFinalNewlines = None; AdditionalData = System.Collections.Generic.Dictionary<_,_>() } }
- with
- | Ok (Some [| edit |]) ->
- let normalized = { edit with NewText = normalizeLineEndings edit.NewText }
+ Options =
+ { TabSize = 4
+ InsertSpaces = true
+ TrimTrailingWhitespace = None
+ InsertFinalNewline = None
+ TrimFinalNewlines = None
+ AdditionalData = System.Collections.Generic.Dictionary<_, _>() } }
+ with
+ | Ok(Some [| edit |]) ->
+ let normalized =
+ { edit with
+ NewText = normalizeLineEndings edit.NewText }
+
Expect.equal normalized expectedTextEdit "should replace the entire file range with the expected content"
| Ok other -> failwithf "Invalid formatting result: %A" other
| Result.Error e -> failwithf "Error while formatting %s: %A" sourceFile e
@@ -302,13 +315,7 @@ let analyzerTests state =
// because the analyzer is a project this project has a reference, the analyzer can be
// found in alongside this project, so we can use the directory this project is in
let analyzerPath =
- System.IO.Path.GetDirectoryName(
- System
- .Reflection
- .Assembly
- .GetExecutingAssembly()
- .Location
- )
+ System.IO.Path.GetDirectoryName(System.Reflection.Assembly.GetExecutingAssembly().Location)
let analyzerEnabledConfig =
{ defaultConfigDto with
@@ -331,7 +338,7 @@ let analyzerTests state =
[ testCaseAsync
"can run analyzer on file"
(async {
- let! (server, events, rootPath, testFilePath) = server
+ let! _server, events, _rootPath, testFilePath = server
let! diagnostics =
analyzerDiagnostics (System.IO.Path.GetFileName testFilePath) events
@@ -363,7 +370,7 @@ let signatureTests state =
do! server.TextDocumentDidOpen { TextDocument = loadDocument scriptPath }
match! waitForParseResultsForFile "Script.fsx" events with
- | Ok () -> () // all good, no parsing/checking errors
+ | Ok() -> () // all good, no parsing/checking errors
| Core.Result.Error errors -> failtestf "Errors while parsing script %s: %A" scriptPath errors
return server, scriptPath
@@ -380,7 +387,7 @@ let signatureTests state =
Position = { Line = line; Character = character } }
match! server.FSharpSignature pos with
- | Ok (Some { Content = content }) ->
+ | Ok(Some { Content = content }) ->
let r = JsonSerializer.readJson> (content)
Expect.equal r.Kind "typesig" "Should have a kind of 'typesig'"
diff --git a/test/FsAutoComplete.Tests.Lsp/FindReferencesTests.fs b/test/FsAutoComplete.Tests.Lsp/FindReferencesTests.fs
index 87c5165dc..320be417c 100644
--- a/test/FsAutoComplete.Tests.Lsp/FindReferencesTests.fs
+++ b/test/FsAutoComplete.Tests.Lsp/FindReferencesTests.fs
@@ -18,10 +18,12 @@ open FSharp.Compiler.CodeAnalysis
open Helpers.Expecto.ShadowedTimeouts
let private scriptTests state =
- testList "script"
+ testList
+ "script"
[ let server =
async {
- let path = Path.Combine(__SOURCE_DIRECTORY__, "TestCases", "FindReferences", "Script")
+ let path =
+ Path.Combine(__SOURCE_DIRECTORY__, "TestCases", "FindReferences", "Script")
let! (server, event) = serverInitialize path defaultConfigDto state
do! waitForWorkspaceFinishedParsing event
@@ -49,7 +51,7 @@ let private scriptTests state =
match response with
| Ok None -> failtestf "Should have gotten some references for this identifier"
| Error e -> failtestf "Errored while getting references for identifier: %A" e
- | Ok (Some references) ->
+ | Ok(Some references) ->
Expect.hasLength references 2 "Should have a reference for the definition and usage"
let reference = references.[0]
Expect.stringEnds reference.Uri (Path.GetFileName scriptPath) "should point to the same script"
@@ -72,48 +74,47 @@ let private extractRanges (sourceWithCursors: string) =
let (source, cursors) =
sourceWithCursors
|> Text.trimTripleQuotation
- |> Cursors.extractWith [| Cursor.cursor; Cursor.usageStart; Cursor.usageEnd; Cursor.defStart; Cursor.defEnd |]
+ |> Cursors.extractWith
+ [| Cursor.cursor
+ Cursor.usageStart
+ Cursor.usageEnd
+ Cursor.defStart
+ Cursor.defEnd |]
let cursor, cursors =
- let cs, cursors =
- cursors
- |> List.partition (fst >> (=) Cursor.cursor)
+ let cs, cursors = cursors |> List.partition (fst >> (=) Cursor.cursor)
+
if cs |> List.isEmpty then
(None, cursors)
else
Expect.hasLength cs 1 "There should be either 0 or 1 cursor $0"
- (Some (snd cs[0]), cursors)
+ (Some(snd cs[0]), cursors)
- let mkRange start fin = {
- Start = start
- End = fin
- }
+ let mkRange start fin = { Start = start; End = fin }
- let rec collectRanges (cursors: (string*Position) list) ((decls, usages) as ranges) =
+ let rec collectRanges (cursors: (string * Position) list) ((decls, usages) as ranges) =
match cursors with
| [] -> ranges
- | [(c,p)] -> failwith $"Lonely last cursor {c} at {p}"
- | (c1,p1)::(c2,p2)::cursors when c1 = Cursor.usageStart && c2 = Cursor.usageEnd ->
- let range = mkRange p1 p2
- let ranges = (range :: decls, usages)
- collectRanges cursors ranges
- | (c1,p1)::(c2,p2) :: cursors when c1 = Cursor.defStart && c2 = Cursor.defEnd ->
- let range = mkRange p1 p2
- let ranges = (decls, range :: usages)
- collectRanges cursors ranges
- | (c1,p1)::(c2,p2):: _ ->
- failwith $"Cursor pair {c1} & {c2} do not match (at {p1} & {p2})"
- let (decls, usages) = collectRanges cursors ([],[])
- source, {|
- Cursor = cursor
- Declarations = decls |> List.rev |> List.toArray
- Usages = usages |> List.rev |> List.toArray
- |}
-let private mkLocation doc range =
- {
- Uri = doc.Uri
- Range = range
- }
+ | [ (c, p) ] -> failwith $"Lonely last cursor {c} at {p}"
+ | (c1, p1) :: (c2, p2) :: cursors when c1 = Cursor.usageStart && c2 = Cursor.usageEnd ->
+ let range = mkRange p1 p2
+ let ranges = (range :: decls, usages)
+ collectRanges cursors ranges
+ | (c1, p1) :: (c2, p2) :: cursors when c1 = Cursor.defStart && c2 = Cursor.defEnd ->
+ let range = mkRange p1 p2
+ let ranges = (decls, range :: usages)
+ collectRanges cursors ranges
+ | (c1, p1) :: (c2, p2) :: _ -> failwith $"Cursor pair {c1} & {c2} do not match (at {p1} & {p2})"
+
+ let (decls, usages) = collectRanges cursors ([], [])
+
+ source,
+ {| Cursor = cursor
+ Declarations = decls |> List.rev |> List.toArray
+ Usages = usages |> List.rev |> List.toArray |}
+
+let private mkLocation doc range = { Uri = doc.Uri; Range = range }
+
/// mark locations in text
/// -> differences gets highlighted in source instead of Location array
///
@@ -123,14 +124,16 @@ let private markRanges (source: string) (locs: Location[]) =
locs
|> Array.map (fun l -> l.Range)
|> Array.sortByDescending (fun r -> (r.Start.Line, r.Start.Character))
+
ranges
- |> Array.fold (fun source range ->
+ |> Array.fold
+ (fun source range ->
source
|> Text.insert range.End "〉"
|> Flip.Expect.wantOk "Should be valid insert position"
|> Text.insert range.Start "〈"
- |> Flip.Expect.wantOk "Should be valid insert position"
- ) source
+ |> Flip.Expect.wantOk "Should be valid insert position")
+ source
module Expect =
/// `exact`:
@@ -152,29 +155,35 @@ module Expect =
let inspect () =
// Note: simplification: only find 1st doc that differs
let actualByUri, expectedByUri =
- (
- actual |> Array.groupBy (fun l -> l.Uri) |> Array.sortBy fst,
- expected |> Array.groupBy (fun l -> l.Uri) |> Array.sortBy fst
- )
+ (actual |> Array.groupBy (fun l -> l.Uri) |> Array.sortBy fst,
+ expected |> Array.groupBy (fun l -> l.Uri) |> Array.sortBy fst)
// cannot directly use zip: might be unequal number of docs
- Expect.sequenceEqual (actualByUri |> Array.map fst) (expectedByUri |> Array.map fst) "Should find references in correct docs"
+ Expect.sequenceEqual
+ (actualByUri |> Array.map fst)
+ (expectedByUri |> Array.map fst)
+ "Should find references in correct docs"
// from here on: actualByUri & expectedByUri have same length and same docs in same order (because sorted)
for ((uri, actual), (_, expected)) in Array.zip actualByUri expectedByUri do
let source = getSource uri
if exact then
- Expect.equal (markRanges source actual) (markRanges source expected) $"Should find correct & exact references in doc %s{uri}"
+ Expect.equal
+ (markRanges source actual)
+ (markRanges source expected)
+ $"Should find correct & exact references in doc %s{uri}"
else
let actual = actual |> Array.sortBy (fun l -> l.Range.Start)
let expected = expected |> Array.sortBy (fun l -> l.Range.Start)
// actual & expected might have different length
// again: find only first difference
- let exactDisclaimer = "\nNote: Ranges in actual might be longer than in expected. That's ok because `exact=false`\n"
+ let exactDisclaimer =
+ "\nNote: Ranges in actual might be longer than in expected. That's ok because `exact=false`\n"
if actual.Length <> expected.Length then
- let msg = $"Found %i{actual.Length} references in doc %s{uri}, but expected %i{expected.Length} references.%s{exactDisclaimer}"
+ let msg =
+ $"Found %i{actual.Length} references in doc %s{uri}, but expected %i{expected.Length} references.%s{exactDisclaimer}"
// this fails -> used for pretty printing of diff
Expect.equal (markRanges source actual) (markRanges source expected) msg
@@ -182,19 +191,17 @@ module Expect =
// expected must fit into actual
let inside =
aLoc.Range |> Range.containsStrictly eLoc.Range.Start
- &&
- aLoc.Range |> Range.containsStrictly eLoc.Range.End
+ && aLoc.Range |> Range.containsStrictly eLoc.Range.End
if not inside then
let msg = $"%i{i}. reference inside %s{uri} has incorrect range.%s{exactDisclaimer}"
- Expect.equal (markRanges source [|aLoc|]) (markRanges source [|eLoc|]) msg
+ Expect.equal (markRanges source [| aLoc |]) (markRanges source [| eLoc |]) msg
if exact then
try
Expect.sequenceEqual actual expected "Should find all references with correct range"
- with
- | :? AssertException ->
+ with :? AssertException ->
// pretty printing: Source with marked locations instead of lists with locations
inspect ()
else
@@ -203,6 +210,7 @@ module Expect =
let private solutionTests state =
let marker = "//>"
+
/// Format of Locations in file `path`:
/// In line after range:
/// * Mark start of line with `//>`
@@ -228,33 +236,31 @@ let private solutionTests state =
let readReferences path =
let lines = File.ReadAllLines path
let refs = Dictionary>()
- for i in 0..(lines.Length-1) do
+
+ for i in 0 .. (lines.Length - 1) do
let line = lines[i].TrimStart()
+
if line.StartsWith(marker, StringComparison.Ordinal) then
let l = line.Substring(marker.Length).Trim()
- let splits = l.Split([|' '|], 2)
+ let splits = l.Split([| ' ' |], 2)
let mark = splits[0]
- let ty = mark[0]
+ let _ty = mark[0]
+
let range =
let col = line.IndexOf(mark, StringComparison.Ordinal)
let length = mark.Length
- let line = i - 1 // marker is line AFTER actual range
- {
- Start = { Line = line; Character = col }
- End = { Line = line; Character = col + length }
- }
- let loc = {
- Uri =
- path
- |> normalizePath
- |> Path.LocalPathToUri
- Range = range
- }
- let name =
- if splits.Length > 1 then
- splits[1]
- else
- ""
+ let line = i - 1 // marker is line AFTER actual range
+
+ { Start = { Line = line; Character = col }
+ End =
+ { Line = line
+ Character = col + length } }
+
+ let loc =
+ { Uri = path |> normalizePath |> Path.LocalPathToUri
+ Range = range }
+
+ let name = if splits.Length > 1 then splits[1] else ""
if not (refs.ContainsKey name) then
refs[name] <- List<_>()
@@ -262,97 +268,113 @@ let private solutionTests state =
let existing = refs[name]
// Note: we're currently dismissing type (declaration, usage)
existing.Add loc |> ignore
+
refs
let readAllReferences dir =
// `.fs` & `.fsx`
let files = Directory.GetFiles(dir, "*.fs*", SearchOption.AllDirectories)
+
files
|> Seq.map readReferences
- |> Seq.map (fun dict ->
- dict
- |> Seq.map (fun kvp -> kvp.Key, kvp.Value)
- )
+ |> Seq.map (fun dict -> dict |> Seq.map (fun kvp -> kvp.Key, kvp.Value))
|> Seq.collect id
|> Seq.groupBy fst
|> Seq.map (fun (name, locs) -> (name, locs |> Seq.map snd |> Seq.collect id |> Seq.toArray))
- |> Seq.map (fun (name, locs) -> {| Name=name; Locations=locs |})
+ |> Seq.map (fun (name, locs) -> {| Name = name; Locations = locs |})
|> Seq.toArray
- let path = Path.Combine(__SOURCE_DIRECTORY__, "TestCases", "FindReferences", "Solution")
- serverTestList "solution" state defaultConfigDto (Some path) (fun server -> [
- // extra function instead of (just) testCase in front: to be able to run single test case
- let mutable scriptFilesLoaded = false
- let assertScriptFilesLoaded = async {
- if not scriptFilesLoaded then
- let files = Directory.GetFiles(path, "*.fsx", SearchOption.AllDirectories)
- for file in files do
- let relativePath = Path.GetRelativePath(path, file)
- let! (doc, _) = server |> Server.openDocument relativePath
- // keep script files open:
- // Unlike `FsharpLspServer`, AdaptiveLspServer doesn't keep closed scripts in cache
- // -> cannot find references inside closed script files
- // see https://github.com/fsharp/FsAutoComplete/pull/1037#issuecomment-1440016138
- // do! doc |> Document.close
- ()
- scriptFilesLoaded <- true
- }
- testCaseAsync "open script files" (async {
- // script files aren't loaded in background
- // -> cannot find references in unopened script files
+ let path =
+ Path.Combine(__SOURCE_DIRECTORY__, "TestCases", "FindReferences", "Solution")
- // -> load script files (don't need to be kept open -- just loaded once -> in FSAC cache)
- do! assertScriptFilesLoaded
+ serverTestList "solution" state defaultConfigDto (Some path) (fun server ->
+ [
+ // extra function instead of (just) testCase in front: to be able to run single test case
+ let mutable scriptFilesLoaded = false
- //Enhancement: implement auto-load (like for projects)?
- })
+ let assertScriptFilesLoaded =
+ async {
+ if not scriptFilesLoaded then
+ let files = Directory.GetFiles(path, "*.fsx", SearchOption.AllDirectories)
+
+ for file in files do
+ let relativePath = Path.GetRelativePath(path, file)
+ let! (_doc, _) = server |> Server.openDocument relativePath
+ // keep script files open:
+ // Unlike `FsharpLspServer`, AdaptiveLspServer doesn't keep closed scripts in cache
+ // -> cannot find references inside closed script files
+ // see https://github.com/fsharp/FsAutoComplete/pull/1037#issuecomment-1440016138
+ // do! doc |> Document.close
+ ()
+
+ scriptFilesLoaded <- true
+ }
- let mainDoc = Path.Combine("B", "WorkingModule.fs")
- documentTestList "inside B/WorkingModule.fs" server (Server.openDocument mainDoc) (fun doc -> [
- let refs = readAllReferences path
- for r in refs do
- testCaseAsync r.Name (async {
+ testCaseAsync
+ "open script files"
+ (async {
+ // script files aren't loaded in background
+ // -> cannot find references in unopened script files
+
+ // -> load script files (don't need to be kept open -- just loaded once -> in FSAC cache)
do! assertScriptFilesLoaded
- let! (doc, _) = doc
- let cursor =
- let cursor =
- r.Locations
- |> Seq.filter (fun l -> l.Uri = doc.Uri)
- |> Seq.minBy (fun l -> l.Range.Start)
- cursor.Range.Start
+ //Enhancement: implement auto-load (like for projects)?
+ })
- let request: ReferenceParams =
- { TextDocument = doc.TextDocumentIdentifier
- Position = cursor
- Context = { IncludeDeclaration = true } }
- let! refs = doc.Server.Server.TextDocumentReferences request
- let refs =
- refs
- |> Flip.Expect.wantOk "Should not fail"
- |> Flip.Expect.wantSome "Should return references"
+ let mainDoc = Path.Combine("B", "WorkingModule.fs")
- let expected = r.Locations
+ documentTestList "inside B/WorkingModule.fs" server (Server.openDocument mainDoc) (fun doc ->
+ [ let refs = readAllReferences path
- let getSource uri =
- let path = Path.FileUriToLocalPath uri
- File.ReadAllText path
+ for r in refs do
+ testCaseAsync
+ r.Name
+ (async {
+ do! assertScriptFilesLoaded
- Expect.locationsEqual getSource false refs expected
- })
- ])
- ])
+ let! (doc, _) = doc
+
+ let cursor =
+ let cursor =
+ r.Locations
+ |> Seq.filter (fun l -> l.Uri = doc.Uri)
+ |> Seq.minBy (fun l -> l.Range.Start)
+
+ cursor.Range.Start
+
+ let request: ReferenceParams =
+ { TextDocument = doc.TextDocumentIdentifier
+ Position = cursor
+ Context = { IncludeDeclaration = true } }
+
+ let! refs = doc.Server.Server.TextDocumentReferences request
+
+ let refs =
+ refs
+ |> Flip.Expect.wantOk "Should not fail"
+ |> Flip.Expect.wantSome "Should return references"
+
+ let expected = r.Locations
+
+ let getSource uri =
+ let path = Path.FileUriToLocalPath uri
+ File.ReadAllText path
+
+ Expect.locationsEqual getSource false refs expected
+ }) ]) ])
/// multiple untitled files (-> all docs are unrelated)
/// -> Tests for external symbols (-> over all docs) & symbol just in current doc (-> no matches in other unrelated docs)
let private untitledTests state =
- serverTestList "untitled" state defaultConfigDto None (fun server -> [
- testCaseAsync "can find external `Delay` in all open untitled docs" (async {
- // Note: Cursor MUST be in first source
- let sources =
- [|
- """
+ serverTestList "untitled" state defaultConfigDto None (fun server ->
+ [ testCaseAsync
+ "can find external `Delay` in all open untitled docs"
+ (async {
+ // Note: Cursor MUST be in first source
+ let sources =
+ [| """
open System
open System.Threading.Tasks
let _ = task {
@@ -367,7 +389,7 @@ let private untitledTests state =
.$$ (TimeSpan.MaxValue)
}
"""
- """
+ """
open System
open System.Threading.Tasks
let _ = task {
@@ -379,68 +401,62 @@ let private untitledTests state =
do! Task.$$ (TimeSpan.MaxValue)
}
"""
- // No Task.Delay
- """
+ // No Task.Delay
+ """
open System
printfn "do stuff"
- """
- |]
- |> Array.map (extractRanges)
-
- let! docs =
- sources
- |> Seq.map fst
- |> Seq.map (fun source -> async {
- let! (doc, diags) = server |> Server.createUntitledDocument source
- Expect.hasLength diags 0 $"There should be no diags in doc {doc.Uri}"
- return doc
- })
- |> Async.Sequential
-
- let (cursorDoc, cursor) =
- let cursors =
- Array.zip docs sources
- |> Array.choose (fun (doc, (_, cursors)) ->
- cursors.Cursor
- |> Option.map (fun cursor -> (doc, cursor))
- )
- Expect.hasLength cursors 1 "There should be exactly one cursor"
- cursors[0]
- let request: ReferenceParams =
- { TextDocument = cursorDoc.TextDocumentIdentifier
- Position = cursor
- Context = { IncludeDeclaration = true } }
- let! refs = cursorDoc.Server.Server.TextDocumentReferences request
- let refs =
- refs
- |> Flip.Expect.wantOk "Should not fail"
- |> Flip.Expect.wantSome "Should return references"
+ """ |]
+ |> Array.map (extractRanges)
+
+ let! docs =
+ sources
+ |> Seq.map fst
+ |> Seq.map (fun source ->
+ async {
+ let! (doc, diags) = server |> Server.createUntitledDocument source
+ Expect.hasLength diags 0 $"There should be no diags in doc {doc.Uri}"
+ return doc
+ })
+ |> Async.Sequential
+
+ let (cursorDoc, cursor) =
+ let cursors =
+ Array.zip docs sources
+ |> Array.choose (fun (doc, (_, cursors)) -> cursors.Cursor |> Option.map (fun cursor -> (doc, cursor)))
+
+ Expect.hasLength cursors 1 "There should be exactly one cursor"
+ cursors[0]
- let expected =
- Array.zip docs sources
- |> Array.collect (fun (doc, (_, cursors)) ->
- Array.append cursors.Declarations cursors.Usages
- |> Array.map (mkLocation doc)
- )
+ let request: ReferenceParams =
+ { TextDocument = cursorDoc.TextDocumentIdentifier
+ Position = cursor
+ Context = { IncludeDeclaration = true } }
- let getSource uri =
- let i = docs |> Array.findIndex (fun doc -> doc.Uri = uri)
- fst sources[i]
+ let! refs = cursorDoc.Server.Server.TextDocumentReferences request
- Expect.locationsEqual getSource true refs expected
+ let refs =
+ refs
+ |> Flip.Expect.wantOk "Should not fail"
+ |> Flip.Expect.wantSome "Should return references"
+
+ let expected =
+ Array.zip docs sources
+ |> Array.collect (fun (doc, (_, cursors)) ->
+ Array.append cursors.Declarations cursors.Usages |> Array.map (mkLocation doc))
- })
- ])
+ let getSource uri =
+ let i = docs |> Array.findIndex (fun doc -> doc.Uri = uri)
+ fst sources[i]
+
+ Expect.locationsEqual getSource true refs expected
+
+ }) ])
/// Tests to check references span the correct range. For example: `Delay`, not `Task.Delay`
let private rangeTests state =
- let checkRanges
- server
- sourceWithCursors
- = async {
- let (source, cursors) =
- sourceWithCursors
- |> extractRanges
+ let checkRanges server sourceWithCursors =
+ async {
+ let (source, cursors) = sourceWithCursors |> extractRanges
let! (doc, diags) = server |> Server.createUntitledDocument source
use doc = doc
Expect.hasLength diags 0 "There should be no diags"
@@ -449,7 +465,9 @@ let private rangeTests state =
{ TextDocument = doc.TextDocumentIdentifier
Position = cursors.Cursor.Value
Context = { IncludeDeclaration = true } }
+
let! refs = doc.Server.Server.TextDocumentReferences request
+
let refs =
refs
|> Flip.Expect.wantOk "Should not fail"
@@ -467,9 +485,11 @@ let private rangeTests state =
if refs <> expected then
Expect.equal (markRanges source refs) (markRanges source expected) "Should find correct references"
}
- serverTestList "range" state defaultConfigDto None (fun server -> [
- testCaseAsync "can get range of variable" <|
- checkRanges server
+
+ serverTestList "range" state defaultConfigDto None (fun server ->
+ [ testCaseAsync "can get range of variable"
+ <| checkRanges
+ server
"""
module MyModule =
let $DD$ = 42
@@ -480,8 +500,9 @@ let private rangeTests state =
let _ = MyModule.$$ + 42
let _ = MyModule.$<``value``>$ + 42
"""
- testCaseAsync "can get range of external function" <|
- checkRanges server
+ testCaseAsync "can get range of external function"
+ <| checkRanges
+ server
"""
open System
open System.Threading.Tasks
@@ -497,8 +518,9 @@ let private rangeTests state =
.$$ (TimeSpan.MaxValue)
}
"""
- testCaseAsync "can get range of variable with required backticks" <|
- checkRanges server
+ testCaseAsync "can get range of variable with required backticks"
+ <| checkRanges
+ server
"""
module MyModule =
let $D<``hello$0 world``>D$ = 42
@@ -507,23 +529,27 @@ let private rangeTests state =
let _ = $<``hello world``>$ + 42
let _ = MyModule.$<``hello world``>$ + 43
"""
- testCaseAsync "can get range of operator" <|
+ testCaseAsync "can get range of operator"
+ <|
// Note: Parens aren't part of result range
// Reason: range returned by FCS in last case (with namespace) contains opening paren, but not closing paren
- checkRanges server
+ checkRanges
+ server
"""
let _ = 1 $0$<+>$ 2
let _ = ($<+>$) 1 2
let _ = Microsoft.FSharp.Core.Operators.($<+>$) 1 2
"""
- testCaseAsync "can get range of full Active Pattern" <|
+ testCaseAsync "can get range of full Active Pattern"
+ <|
// Active Pattern is strange: all together are single symbol, but each individual too
// * get references on `(|Even|Odd|)` -> finds exactly `(|Even|Odd|)`
// * get references on `Even` -> finds single `Even` and `Even` inside Declaration `let (|Even|Odd|)`, but not usage `(|Even|Odd|)`
//
// Note: Find References in FCS return range with Namespace, Module, Type -> Find Refs for `XXXX` -> range is `MyModule.XXXX`
// Note: When XXXX in parens and Namespace, FCS returns range including opening paren, but NOT closing paren `MyModule.(XXXX` (happens for operators)
- checkRanges server
+ checkRanges
+ server
"""
module MyModule =
let ($D<|Ev$0en|Odd|>D$) value =
@@ -545,10 +571,12 @@ let private rangeTests state =
| MyModule.Even -> ()
| MyModule.Odd -> ()
"""
- testCaseAsync "can get range of partial Active Pattern (Even)" <|
+ testCaseAsync "can get range of partial Active Pattern (Even)"
+ <|
// Note: `Even` is found in Active Pattern declaration (`let (|Even|Odd|) = ...`)
// but NOT in usage of Full Active Pattern Name (`(|Even|Odd|)`)
- checkRanges server
+ checkRanges
+ server
"""
module MyModule =
let (|$DD$|Odd|) value =
@@ -570,8 +598,9 @@ let private rangeTests state =
| MyModule.$$ -> ()
| MyModule.Odd -> ()
"""
- testCaseAsync "can get range of type for static function call" <|
- checkRanges server
+ testCaseAsync "can get range of type for static function call"
+ <| checkRanges
+ server
"""
open System
open System.Threading.Tasks
@@ -586,132 +615,120 @@ let private rangeTests state =
.$$
.Delay (TimeSpan.MaxValue)
}
+ """ ])
+
+let tests state =
+ testList
+ "Find All References tests"
+ [ scriptTests state
+ solutionTests state
+ untitledTests state
+ rangeTests state ]
+
+
+let tryFixupRangeTests (sourceTextFactoryName, sourceTextFactory: ISourceTextFactory) =
+ testList
+ ($"{nameof Tokenizer.tryFixupRange}.{sourceTextFactoryName}")
+ [ let checker = lazy (FSharpChecker.Create())
+
+ let getSymbolUses (source: string) cursor =
+ async {
+ let checker = checker.Value
+ let file = "code.fsx"
+ let path: string = UMX.tag file
+ let source = sourceTextFactory.Create(path, source)
+
+ let! (projOptions, _) = checker.GetProjectOptionsFromScript(file, source, assumeDotNetFramework = false)
+ let! (parseResults, checkResults) = checker.ParseAndCheckFileInProject(file, 0, source, projOptions)
+ // Expect.isEmpty parseResults.Diagnostics "There should be no parse diags"
+ Expect.hasLength parseResults.Diagnostics 0 "There should be no parse diags"
+
+ let checkResults =
+ match checkResults with
+ | FSharpCheckFileAnswer.Succeeded checkResults -> checkResults
+ | _ -> failtest "CheckFile aborted"
+ // Expect.isEmpty checkResults.Diagnostics "There should be no check diags"
+ Expect.hasLength checkResults.Diagnostics 0 "There should be no check diags"
+ let line = source.Lines[cursor.Line]
+
+ let (col, idents) =
+ Lexer.findIdents cursor.Character line SymbolLookupKind.Fuzzy
+ |> Flip.Expect.wantSome "Should find idents"
+
+ let symbolUse =
+ checkResults.GetSymbolUseAtLocation(cursor.Line + 1, col, line, List.ofArray idents)
+ |> Flip.Expect.wantSome "Should find symbol"
+
+ let! ct = Async.CancellationToken
+ let usages = checkResults.GetUsesOfSymbolInFile(symbolUse.Symbol, ct)
+
+ return (source, symbolUse.Symbol, usages)
+ }
+
+ /// Markers:
+ /// * Cursor: `$0`
+ /// * Ranges: Inside `$<` ... `>$`
+ let extractCursorAndRanges sourceWithCursorAndRanges =
+ let (source, cursors) =
+ sourceWithCursorAndRanges
+ |> Text.trimTripleQuotation
+ |> Cursors.extractWith [| "$0"; "$<"; ">$" |]
+
+ let (cursor, cursors) =
+ let (c, cs) = cursors |> List.partition (fst >> (=) "$0")
+ let c = c |> List.map snd
+ Expect.hasLength c 1 "There should be exactly 1 cursor (`$0`)"
+ (c[0], cs)
+
+ let rec collectRanges cursors ranges =
+ match cursors with
+ | [] -> List.rev ranges
+ | ("$<", start) :: (">$", fin) :: cursors ->
+ let range = { Start = start; End = fin }
+ collectRanges cursors (range :: ranges)
+ | _ -> failtest $"Expected matching range pair '$<', '>$', but got: %A{cursors}"
+
+ let ranges = collectRanges cursors []
+
+ (source, cursor, ranges)
+
+ let check includeBackticks sourceWithCursorAndRanges =
+ async {
+ let (source, cursor, expected) = extractCursorAndRanges sourceWithCursorAndRanges
+
+ let! (source, symbol, usages) = getSymbolUses source cursor
+
+ let symbolNameCore = symbol.DisplayNameCore
+
+ let actual =
+ usages
+ |> Seq.map (fun u ->
+ Tokenizer.tryFixupRange (symbolNameCore, u.Range, source, includeBackticks)
+ |> Option.ofValueOption
+ |> Flip.Expect.wantSome $"Should be able to fixup usage '%A{u}'")
+ |> Seq.map fcsRangeToLsp
+ |> Seq.toArray
+ |> Array.sortBy (fun r -> (r.Start.Line, r.Start.Character))
+
+ let expected = expected |> Array.ofList
+
+ // Expect.equal actual expected "Should be correct range"
+ if actual <> expected then
+ // mark ranges for visual diff instead of range diff
+ let markRanges (ranges: Range[]) =
+ let locs = ranges |> Array.map (fun r -> { Uri = ""; Range = r })
+ let marked = markRanges source.String locs
+ // append range strings for additional range diff
+ let rangeStrs = ranges |> Seq.map (fun r -> r.DebuggerDisplay) |> String.concat "\n"
+ marked + "\n" + "\n" + rangeStrs
+
+ Expect.equal (markRanges actual) (markRanges expected) "Should be correct ranges"
+ }
+
+ testCaseAsync "Active Pattern - simple"
+ <| check
+ false
"""
- ])
-
-let tests state = testList "Find All References tests" [
- scriptTests state
- solutionTests state
- untitledTests state
- rangeTests state
-]
-
-
-let tryFixupRangeTests (sourceTextFactoryName, sourceTextFactory : ISourceTextFactory) = testList ($"{nameof Tokenizer.tryFixupRange}.{sourceTextFactoryName}") [
- let checker = lazy (FSharpChecker.Create())
- let getSymbolUses (source : string) cursor = async {
- let checker = checker.Value
- let file = "code.fsx"
- let path: string = UMX.tag file
- let source = sourceTextFactory.Create(path, source)
-
- let! (projOptions, _) = checker.GetProjectOptionsFromScript(file, source, assumeDotNetFramework=false)
- let! (parseResults, checkResults) = checker.ParseAndCheckFileInProject(file, 0, source, projOptions)
- // Expect.isEmpty parseResults.Diagnostics "There should be no parse diags"
- Expect.hasLength parseResults.Diagnostics 0 "There should be no parse diags"
- let checkResults =
- match checkResults with
- | FSharpCheckFileAnswer.Succeeded checkResults -> checkResults
- | _ -> failtest "CheckFile aborted"
- // Expect.isEmpty checkResults.Diagnostics "There should be no check diags"
- Expect.hasLength checkResults.Diagnostics 0 "There should be no check diags"
- let line = source.Lines[cursor.Line]
- let (col, idents) =
- Lexer.findIdents cursor.Character line SymbolLookupKind.Fuzzy
- |> Flip.Expect.wantSome "Should find idents"
- let symbolUse =
- checkResults.GetSymbolUseAtLocation(cursor.Line + 1, col, line, List.ofArray idents)
- |> Flip.Expect.wantSome "Should find symbol"
-
- let! ct = Async.CancellationToken
- let usages = checkResults.GetUsesOfSymbolInFile(symbolUse.Symbol, ct)
-
- return (source, symbolUse.Symbol, usages)
- }
-
- /// Markers:
- /// * Cursor: `$0`
- /// * Ranges: Inside `$<` ... `>$`
- let extractCursorAndRanges sourceWithCursorAndRanges =
- let (source, cursors) =
- sourceWithCursorAndRanges
- |> Text.trimTripleQuotation
- |> Cursors.extractWith [| "$0"; "$<"; ">$"|]
- let (cursor, cursors) =
- let (c, cs) =
- cursors
- |> List.partition (fst >> (=) "$0")
- let c = c |> List.map snd
- Expect.hasLength c 1 "There should be exactly 1 cursor (`$0`)"
- (c[0], cs)
-
- let rec collectRanges cursors ranges =
- match cursors with
- | [] -> List.rev ranges
- | ("$<", start)::(">$", fin)::cursors ->
- let range = {
- Start = start
- End = fin
- }
- collectRanges cursors (range::ranges)
- | _ ->
- failtest $"Expected matching range pair '$<', '>$', but got: %A{cursors}"
- let ranges =
- collectRanges cursors []
-
- (source, cursor, ranges)
-
- let check includeBackticks sourceWithCursorAndRanges = async {
- let (source, cursor, expected) = extractCursorAndRanges sourceWithCursorAndRanges
-
- let! (source, symbol, usages) = getSymbolUses source cursor
-
- let symbolNameCore = symbol.DisplayNameCore
- let actual =
- usages
- |> Seq.map (fun u ->
- Tokenizer.tryFixupRange(symbolNameCore, u.Range, source, includeBackticks)
- |> Option.ofValueOption
- |> Flip.Expect.wantSome $"Should be able to fixup usage '%A{u}'"
- )
- |> Seq.map fcsRangeToLsp
- |> Seq.toArray
- |> Array.sortBy (fun r -> (r.Start.Line, r.Start.Character))
-
- let expected = expected |> Array.ofList
-
- // Expect.equal actual expected "Should be correct range"
- if actual <> expected then
- // mark ranges for visual diff instead of range diff
- let markRanges (ranges: Range[]) =
- let locs =
- ranges
- |> Array.map (fun r ->
- {
- Uri = ""
- Range = r
- }
- )
- let marked = markRanges source.String locs
- // append range strings for additional range diff
- let rangeStrs =
- ranges
- |> Seq.map (fun r -> r.DebuggerDisplay)
- |> String.concat "\n"
- marked
- + "\n"
- + "\n"
- + rangeStrs
-
- Expect.equal
- (markRanges actual)
- (markRanges expected)
- "Should be correct ranges"
- }
-
- testCaseAsync "Active Pattern - simple" <|
- check false
- """
module MyModule =
let ($<|Even|Odd|>$) v = if v % 2 = 0 then Even else Odd
let _ = ($<|Ev$0en|Odd|>$) 42
@@ -765,9 +782,11 @@ let tryFixupRangeTests (sourceTextFactoryName, sourceTextFactory : ISourceTextFa
) 42
"""
- testCaseAsync "Active Pattern - simple - with backticks" <|
- check true
- """
+
+ testCaseAsync "Active Pattern - simple - with backticks"
+ <| check
+ true
+ """
module MyModule =
let ($<|Even|Odd|>$) v = if v % 2 = 0 then Even else Odd
let _ = ($<|Ev$0en|Odd|>$) 42
@@ -822,8 +841,9 @@ let tryFixupRangeTests (sourceTextFactoryName, sourceTextFactory : ISourceTextFa
) 42
"""
- testCaseAsync "Active Pattern - required backticks" <|
- check false
+ testCaseAsync "Active Pattern - required backticks"
+ <| check
+ false
"""
module MyModule =
let ($<|``Hello World``|_|>$) v = Some v
@@ -873,8 +893,10 @@ let tryFixupRangeTests (sourceTextFactoryName, sourceTextFactory : ISourceTextFa
// ``|Hello World|_|``
// ) 42
"""
- testCaseAsync "Active Pattern - required backticks - with backticks" <|
- check true
+
+ testCaseAsync "Active Pattern - required backticks - with backticks"
+ <| check
+ true
"""
module MyModule =
let ($<|``Hello World``|_|>$) v = Some v
@@ -925,8 +947,9 @@ let tryFixupRangeTests (sourceTextFactoryName, sourceTextFactory : ISourceTextFa
// ) 42
"""
- testCaseAsync "Active Pattern Case - simple - at usage" <|
- check false
+ testCaseAsync "Active Pattern Case - simple - at usage"
+ <| check
+ false
"""
module MyModule =
let (|$$|Odd|) v =
@@ -952,8 +975,10 @@ let tryFixupRangeTests (sourceTextFactoryName, sourceTextFactory : ISourceTextFa
| MyModule.``$$`` -> ()
| MyModule.``Odd`` -> ()
"""
- testCaseAsync "Active Pattern Case - simple - at usage - with backticks" <|
- check true
+
+ testCaseAsync "Active Pattern Case - simple - at usage - with backticks"
+ <| check
+ true
"""
module MyModule =
let (|$$|Odd|) v =
@@ -980,11 +1005,13 @@ let tryFixupRangeTests (sourceTextFactoryName, sourceTextFactory : ISourceTextFa
| MyModule.``Odd`` -> ()
"""
- testCaseAsync "Active Pattern Case - simple - at decl" <|
+ testCaseAsync "Active Pattern Case - simple - at decl"
+ <|
// Somehow `FSharpSymbolUse.Symbol.DisplayNameCore` is empty -- but references correct Even symbol
//
// Why? Cannot reproduce with just FCS -> happens just in FSAC
- check false
+ check
+ false
"""
module MyModule =
let (|$$|Odd|) v =
@@ -1010,8 +1037,10 @@ let tryFixupRangeTests (sourceTextFactoryName, sourceTextFactory : ISourceTextFa
| MyModule.``$$`` -> ()
| MyModule.``Odd`` -> ()
"""
- testCaseAsync "Active Pattern Case - simple - at decl - with backticks" <|
- check true
+
+ testCaseAsync "Active Pattern Case - simple - at decl - with backticks"
+ <| check
+ true
"""
module MyModule =
let (|$$|Odd|) v =
@@ -1038,8 +1067,9 @@ let tryFixupRangeTests (sourceTextFactoryName, sourceTextFactory : ISourceTextFa
| MyModule.``Odd`` -> ()
"""
- testCaseAsync "operator -.-" <|
- check false
+ testCaseAsync "operator -.-"
+ <| check
+ false
"""
module MyModule =
let ($<-.->$) a b = a - b
@@ -1079,9 +1109,12 @@ let tryFixupRangeTests (sourceTextFactoryName, sourceTextFactory : ISourceTextFa
$<-.->$
) 1 2
"""
- testCaseAsync "operator -.- - with backticks" <|
+
+ testCaseAsync "operator -.- - with backticks"
+ <|
// same as above -- just to ensure same result
- check true
+ check
+ true
"""
module MyModule =
let ($<-.->$) a b = a - b
@@ -1120,5 +1153,4 @@ let tryFixupRangeTests (sourceTextFactoryName, sourceTextFactory : ISourceTextFa
(
$<-.->$
) 1 2
- """
-]
+ """ ]
diff --git a/test/FsAutoComplete.Tests.Lsp/GoToTests.fs b/test/FsAutoComplete.Tests.Lsp/GoToTests.fs
index 09ebf66c8..b8eb3639c 100644
--- a/test/FsAutoComplete.Tests.Lsp/GoToTests.fs
+++ b/test/FsAutoComplete.Tests.Lsp/GoToTests.fs
@@ -53,439 +53,440 @@ let private gotoTest state =
// sequenced because we don't provide safe concurrent access to file downloads in Sourcelink.fs
testSequenced
<| testList
- "GoTo Tests"
- [ testCaseAsync
- "Go-to-definition on external symbol (System.Net.HttpWebRequest)"
- (async {
- let! server, path, externalPath, definitionPath = server
-
- let p: TextDocumentPositionParams =
- { TextDocument = { Uri = Path.FilePathToUri externalPath }
- Position = { Line = 4; Character = 30 } }
-
- let! res = server.TextDocumentDefinition p
-
- match res with
- | Result.Error e -> failtestf "Request failed: %A" e
- | Result.Ok None -> failtest "Request none"
- | Result.Ok (Some (GotoResult.Multiple _)) -> failtest "Should only get one location"
- | Result.Ok (Some (GotoResult.Single r)) when r.Uri.EndsWith("startup", StringComparison.Ordinal) ->
- failtest "Should not generate the startup dummy file"
- | Result.Ok (Some (GotoResult.Single r)) ->
- Expect.stringEnds r.Uri ".cs" "should have generated a C# code file"
-
- Expect.stringContains
- r.Uri
- "System.Net.HttpWebRequest"
- "The generated file should be for the HttpWebRequest type"
-
- () // should
- })
-
- testCaseAsync
- "Go-to-definition on external namespace (System.Net) should error when going to a namespace "
- (async {
- let! server, path, externalPath, definitionPath = server
-
- let p: TextDocumentPositionParams =
- { TextDocument = { Uri = Path.FilePathToUri externalPath }
- Position = { Line = 2; Character = 15 } }
-
- let! res = server.TextDocumentDefinition p
-
- match res with
- | Result.Error e ->
- Expect.equal "Could not find declaration" e.Message "Should report failure for navigating to a namespace"
- | Result.Ok r -> failtestf "Declaration request should not work on a namespace, instead we got %A" r
- })
-
- testCaseAsync
- "Go-to-definition"
- (async {
- let! server, path, externalPath, definitionPath = server
-
- let p: TextDocumentPositionParams =
- { TextDocument = { Uri = Path.FilePathToUri path }
- Position = { Line = 2; Character = 29 } }
-
- let! res = server.TextDocumentDefinition p
-
- match res with
- | Result.Error e -> failtestf "Request failed: %A" e
- | Result.Ok None -> failtest "Request none"
- | Result.Ok (Some res) ->
- match res with
- | GotoResult.Multiple _ -> failtest "Should be single GotoResult"
- | GotoResult.Single res ->
- Expect.stringContains res.Uri "Definition.fs" "Result should be in Definition.fs"
-
- Expect.equal
- res.Range
- { Start = { Line = 2; Character = 4 }
- End = { Line = 2; Character = 16 } }
- "Result should have correct range"
- })
-
- testCaseAsync
- "Go-to-definition on custom type binding"
- (async {
- let! server, path, externalPath, definitionPath = server
-
- let p: TextDocumentPositionParams =
- { TextDocument = { Uri = Path.FilePathToUri path }
- Position = { Line = 4; Character = 24 } }
-
- let! res = server.TextDocumentDefinition p
-
- match res with
- | Result.Error e -> failtestf "Request failed: %A" e
- | Result.Ok None -> failtest "Request none"
- | Result.Ok (Some res) ->
- match res with
- | GotoResult.Multiple _ -> failtest "Should be single GotoResult"
- | GotoResult.Single res ->
- Expect.stringContains res.Uri "Definition.fs" "Result should be in Definition.fs"
-
- Expect.equal
- res.Range
- { Start = { Line = 6; Character = 4 }
- End = { Line = 6; Character = 19 } }
- "Result should have correct range"
- })
-
- ptestCaseAsync
- "Go-to-implementation-on-interface-definition"
- (async {
- let! server, path, externalPath, definitionPath = server
-
- let p: TextDocumentPositionParams =
- { TextDocument = { Uri = Path.FilePathToUri definitionPath }
- Position = { Line = 8; Character = 11 } }
-
- let! res = server.TextDocumentImplementation p
-
- match res with
- | Result.Error e -> failtestf "Request failed: %A" e
- | Result.Ok None -> failtest "Request none"
- | Result.Ok (Some res) ->
- match res with
- | GotoResult.Single res -> failtest "Should be multiple GotoResult"
- | GotoResult.Multiple res ->
- // TODO???
- // Expect.exists res (fun r -> r.Uri.Contains "Library.fs" && r.Range = { Start = {Line = 7; Character = 8 }; End = {Line = 7; Character = 30 }}) "First result should be in Library.fs"
- // Expect.exists res (fun r -> r.Uri.Contains "Library.fs" && r.Range = { Start = {Line = 13; Character = 14 }; End = {Line = 13; Character = 36 }}) "Second result should be in Library.fs"
- ()
- })
-
- testCaseAsync
- "Go-to-implementation on sourcelink file with sourcelink in PDB"
- (async {
- let! server, path, externalPath, definitionPath = server
-
- // check for the 'button' member in giraffe view engine
- let p: TextDocumentPositionParams =
- { TextDocument = { Uri = Path.FilePathToUri externalPath }
- Position = { Line = 9; Character = 34 } }
-
- let! res = server.TextDocumentDefinition p
-
- match res with
- | Result.Error e -> failtestf "Request failed: %A" e
- | Result.Ok None -> failtest "Request none"
- | Result.Ok (Some res) ->
- match res with
- | GotoResult.Multiple _ -> failtest "Should be single GotoResult"
- | GotoResult.Single res ->
- Expect.stringContains res.Uri "GiraffeViewEngine.fs" "Result should be in GiraffeViewEngine"
- let localPath = Path.FileUriToLocalPath res.Uri
-
- Expect.isTrue
- (System.IO.File.Exists localPath)
- (sprintf "File '%s' should exist locally after being downloaded" localPath)
- })
-
- testCaseAsync
- "Go-to-implementation on sourcelink file with sourcelink in DLL"
- (async {
- let! server, path, externalPath, definitionPath = server
-
- // check for the 'List.concat' member in FSharp.Core
- let p: TextDocumentPositionParams =
- { TextDocument = { Uri = Path.FilePathToUri externalPath }
- Position = { Line = 12; Character = 36 } }
-
- let! res = server.TextDocumentDefinition p
-
- match res with
- | Result.Error e -> failtestf "Request failed: %A" e
- | Result.Ok None -> failtest "Request none"
- | Result.Ok (Some res) ->
- match res with
- | GotoResult.Multiple _ -> failtest "Should be single GotoResult"
- | GotoResult.Single res ->
- Expect.stringContains res.Uri "FSharp.Core/list.fs" "Result should be in FSharp.Core's list.fs"
- let localPath = Path.FileUriToLocalPath res.Uri
-
- Expect.isTrue
- (System.IO.File.Exists localPath)
- (sprintf "File '%s' should exist locally after being downloaded" localPath)
- })
-
- // marked pending because we don't have filename information for C# sources
- ptestCaseAsync
- "Go-to-implementation on C# file"
- (async {
- let! server, path, externalPath, definitionPath = server
-
- // check for the 'Stirng.Join' member in the BCL
- let p: TextDocumentPositionParams =
- { TextDocument = { Uri = Path.FilePathToUri externalPath }
- Position = { Line = 14; Character = 79 } }
-
- let! res = server.TextDocumentDefinition p
-
- match res with
- | Result.Error e -> failtestf "Request failed: %A" e
- | Result.Ok None -> failtest "Request none"
- | Result.Ok (Some res) ->
- match res with
- | GotoResult.Multiple _ -> failtest "Should be single GotoResult"
- | GotoResult.Single res ->
- let localPath = Path.FileUriToLocalPath res.Uri
-
- if
- localPath.Contains
- "System.String netstandard_ Version_2.0.0.0_ Culture_neutral_ PublicKeyToken_cc7b13ffcd2ddd51" then
- failtestf "should not decompile when sourcelink is available"
-
- Expect.stringContains localPath "System.String" "Result should be in the BCL's source files"
-
- Expect.isTrue
- (System.IO.File.Exists localPath)
- (sprintf "File '%s' should exist locally after being downloaded" localPath)
- })
-
- testCaseAsync
- "Go-to-type-definition"
- (async {
- let! server, path, externalPath, definitionPath = server
-
- let p: TextDocumentPositionParams =
- { TextDocument = { Uri = Path.FilePathToUri path }
- Position = { Line = 4; Character = 24 } }
-
- let! res = server.TextDocumentTypeDefinition p
-
- match res with
- | Result.Error e -> failtestf "Request failed: %A" e
- | Result.Ok None -> failtest "Request none"
- | Result.Ok (Some res) ->
- match res with
- | GotoResult.Multiple _ -> failtest "Should be single GotoResult"
- | GotoResult.Single res ->
- Expect.stringContains res.Uri "Definition.fs" "Result should be in Definition.fs"
-
- Expect.equal
- res.Range
- { Start = { Line = 4; Character = 5 }
- End = { Line = 4; Character = 6 } }
- "Result should have correct range"
- })
-
- testCaseAsync
- "Go-to-type-definition on first char of identifier"
- (async {
- let! server, path, externalPath, definitionPath = server
-
- let p: TextDocumentPositionParams =
- { TextDocument = { Uri = Path.FilePathToUri path }
- Position = { Line = 4; Character = 20 } }
-
- let! res = server.TextDocumentTypeDefinition p
-
- match res with
- | Result.Error e -> failtestf "Request failed: %A" e
- | Result.Ok None -> failtest "Request none"
- | Result.Ok (Some res) ->
- match res with
- | GotoResult.Multiple _ -> failtest "Should be single GotoResult"
- | GotoResult.Single res ->
- Expect.stringContains res.Uri "Definition.fs" "Result should be in Definition.fs"
-
- Expect.equal
- res.Range
- { Start = { Line = 4; Character = 5 }
- End = { Line = 4; Character = 6 } }
- "Result should have correct range"
- })
-
- testCaseAsync
- "Go-to-type-defintion on parameter"
- (async {
- let! server, path, externalPath, definitionPath = server
-
- // check for parameter of type `'a list` -> FSharp.Core
- (*
+ "GoTo Tests"
+ [ testCaseAsync
+ "Go-to-definition on external symbol (System.Net.HttpWebRequest)"
+ (async {
+ let! server, _path, externalPath, _definitionPath = server
+
+ let p: TextDocumentPositionParams =
+ { TextDocument = { Uri = Path.FilePathToUri externalPath }
+ Position = { Line = 4; Character = 30 } }
+
+ let! res = server.TextDocumentDefinition p
+
+ match res with
+ | Result.Error e -> failtestf "Request failed: %A" e
+ | Result.Ok None -> failtest "Request none"
+ | Result.Ok(Some(GotoResult.Multiple _)) -> failtest "Should only get one location"
+ | Result.Ok(Some(GotoResult.Single r)) when r.Uri.EndsWith("startup", StringComparison.Ordinal) ->
+ failtest "Should not generate the startup dummy file"
+ | Result.Ok(Some(GotoResult.Single r)) ->
+ Expect.stringEnds r.Uri ".cs" "should have generated a C# code file"
+
+ Expect.stringContains
+ r.Uri
+ "System.Net.HttpWebRequest"
+ "The generated file should be for the HttpWebRequest type"
+
+ () // should
+ })
+
+ testCaseAsync
+ "Go-to-definition on external namespace (System.Net) should error when going to a namespace "
+ (async {
+ let! server, _path, externalPath, _definitionPath = server
+
+ let p: TextDocumentPositionParams =
+ { TextDocument = { Uri = Path.FilePathToUri externalPath }
+ Position = { Line = 2; Character = 15 } }
+
+ let! res = server.TextDocumentDefinition p
+
+ match res with
+ | Result.Error e ->
+ Expect.equal "Could not find declaration" e.Message "Should report failure for navigating to a namespace"
+ | Result.Ok r -> failtestf "Declaration request should not work on a namespace, instead we got %A" r
+ })
+
+ testCaseAsync
+ "Go-to-definition"
+ (async {
+ let! server, path, _externalPath, _definitionPath = server
+
+ let p: TextDocumentPositionParams =
+ { TextDocument = { Uri = Path.FilePathToUri path }
+ Position = { Line = 2; Character = 29 } }
+
+ let! res = server.TextDocumentDefinition p
+
+ match res with
+ | Result.Error e -> failtestf "Request failed: %A" e
+ | Result.Ok None -> failtest "Request none"
+ | Result.Ok(Some res) ->
+ match res with
+ | GotoResult.Multiple _ -> failtest "Should be single GotoResult"
+ | GotoResult.Single res ->
+ Expect.stringContains res.Uri "Definition.fs" "Result should be in Definition.fs"
+
+ Expect.equal
+ res.Range
+ { Start = { Line = 2; Character = 4 }
+ End = { Line = 2; Character = 16 } }
+ "Result should have correct range"
+ })
+
+ testCaseAsync
+ "Go-to-definition on custom type binding"
+ (async {
+ let! server, path, _externalPath, _definitionPath = server
+
+ let p: TextDocumentPositionParams =
+ { TextDocument = { Uri = Path.FilePathToUri path }
+ Position = { Line = 4; Character = 24 } }
+
+ let! res = server.TextDocumentDefinition p
+
+ match res with
+ | Result.Error e -> failtestf "Request failed: %A" e
+ | Result.Ok None -> failtest "Request none"
+ | Result.Ok(Some res) ->
+ match res with
+ | GotoResult.Multiple _ -> failtest "Should be single GotoResult"
+ | GotoResult.Single res ->
+ Expect.stringContains res.Uri "Definition.fs" "Result should be in Definition.fs"
+
+ Expect.equal
+ res.Range
+ { Start = { Line = 6; Character = 4 }
+ End = { Line = 6; Character = 19 } }
+ "Result should have correct range"
+ })
+
+ ptestCaseAsync
+ "Go-to-implementation-on-interface-definition"
+ (async {
+ let! server, _path, _externalPath, definitionPath = server
+
+ let p: TextDocumentPositionParams =
+ { TextDocument = { Uri = Path.FilePathToUri definitionPath }
+ Position = { Line = 8; Character = 11 } }
+
+ let! res = server.TextDocumentImplementation p
+
+ match res with
+ | Result.Error e -> failtestf "Request failed: %A" e
+ | Result.Ok None -> failtest "Request none"
+ | Result.Ok(Some res) ->
+ match res with
+ | GotoResult.Single _res -> failtest "Should be multiple GotoResult"
+ | GotoResult.Multiple _res ->
+ // TODO???
+ // Expect.exists res (fun r -> r.Uri.Contains "Library.fs" && r.Range = { Start = {Line = 7; Character = 8 }; End = {Line = 7; Character = 30 }}) "First result should be in Library.fs"
+ // Expect.exists res (fun r -> r.Uri.Contains "Library.fs" && r.Range = { Start = {Line = 13; Character = 14 }; End = {Line = 13; Character = 36 }}) "Second result should be in Library.fs"
+ ()
+ })
+
+ testCaseAsync
+ "Go-to-implementation on sourcelink file with sourcelink in PDB"
+ (async {
+ let! server, _path, externalPath, _definitionPath = server
+
+ // check for the 'button' member in giraffe view engine
+ let p: TextDocumentPositionParams =
+ { TextDocument = { Uri = Path.FilePathToUri externalPath }
+ Position = { Line = 9; Character = 34 } }
+
+ let! res = server.TextDocumentDefinition p
+
+ match res with
+ | Result.Error e -> failtestf "Request failed: %A" e
+ | Result.Ok None -> failtest "Request none"
+ | Result.Ok(Some res) ->
+ match res with
+ | GotoResult.Multiple _ -> failtest "Should be single GotoResult"
+ | GotoResult.Single res ->
+ Expect.stringContains res.Uri "GiraffeViewEngine.fs" "Result should be in GiraffeViewEngine"
+ let localPath = Path.FileUriToLocalPath res.Uri
+
+ Expect.isTrue
+ (System.IO.File.Exists localPath)
+ (sprintf "File '%s' should exist locally after being downloaded" localPath)
+ })
+
+ testCaseAsync
+ "Go-to-implementation on sourcelink file with sourcelink in DLL"
+ (async {
+ let! server, _path, externalPath, _definitionPath = server
+
+ // check for the 'List.concat' member in FSharp.Core
+ let p: TextDocumentPositionParams =
+ { TextDocument = { Uri = Path.FilePathToUri externalPath }
+ Position = { Line = 12; Character = 36 } }
+
+ let! res = server.TextDocumentDefinition p
+
+ match res with
+ | Result.Error e -> failtestf "Request failed: %A" e
+ | Result.Ok None -> failtest "Request none"
+ | Result.Ok(Some res) ->
+ match res with
+ | GotoResult.Multiple _ -> failtest "Should be single GotoResult"
+ | GotoResult.Single res ->
+ Expect.stringContains res.Uri "FSharp.Core/list.fs" "Result should be in FSharp.Core's list.fs"
+ let localPath = Path.FileUriToLocalPath res.Uri
+
+ Expect.isTrue
+ (System.IO.File.Exists localPath)
+ (sprintf "File '%s' should exist locally after being downloaded" localPath)
+ })
+
+ // marked pending because we don't have filename information for C# sources
+ ptestCaseAsync
+ "Go-to-implementation on C# file"
+ (async {
+ let! server, _path, externalPath, _definitionPath = server
+
+ // check for the 'Stirng.Join' member in the BCL
+ let p: TextDocumentPositionParams =
+ { TextDocument = { Uri = Path.FilePathToUri externalPath }
+ Position = { Line = 14; Character = 79 } }
+
+ let! res = server.TextDocumentDefinition p
+
+ match res with
+ | Result.Error e -> failtestf "Request failed: %A" e
+ | Result.Ok None -> failtest "Request none"
+ | Result.Ok(Some res) ->
+ match res with
+ | GotoResult.Multiple _ -> failtest "Should be single GotoResult"
+ | GotoResult.Single res ->
+ let localPath = Path.FileUriToLocalPath res.Uri
+
+ if
+ localPath.Contains
+ "System.String netstandard_ Version_2.0.0.0_ Culture_neutral_ PublicKeyToken_cc7b13ffcd2ddd51"
+ then
+ failtestf "should not decompile when sourcelink is available"
+
+ Expect.stringContains localPath "System.String" "Result should be in the BCL's source files"
+
+ Expect.isTrue
+ (System.IO.File.Exists localPath)
+ (sprintf "File '%s' should exist locally after being downloaded" localPath)
+ })
+
+ testCaseAsync
+ "Go-to-type-definition"
+ (async {
+ let! server, path, _externalPath, _definitionPath = server
+
+ let p: TextDocumentPositionParams =
+ { TextDocument = { Uri = Path.FilePathToUri path }
+ Position = { Line = 4; Character = 24 } }
+
+ let! res = server.TextDocumentTypeDefinition p
+
+ match res with
+ | Result.Error e -> failtestf "Request failed: %A" e
+ | Result.Ok None -> failtest "Request none"
+ | Result.Ok(Some res) ->
+ match res with
+ | GotoResult.Multiple _ -> failtest "Should be single GotoResult"
+ | GotoResult.Single res ->
+ Expect.stringContains res.Uri "Definition.fs" "Result should be in Definition.fs"
+
+ Expect.equal
+ res.Range
+ { Start = { Line = 4; Character = 5 }
+ End = { Line = 4; Character = 6 } }
+ "Result should have correct range"
+ })
+
+ testCaseAsync
+ "Go-to-type-definition on first char of identifier"
+ (async {
+ let! server, path, _externalPath, _definitionPath = server
+
+ let p: TextDocumentPositionParams =
+ { TextDocument = { Uri = Path.FilePathToUri path }
+ Position = { Line = 4; Character = 20 } }
+
+ let! res = server.TextDocumentTypeDefinition p
+
+ match res with
+ | Result.Error e -> failtestf "Request failed: %A" e
+ | Result.Ok None -> failtest "Request none"
+ | Result.Ok(Some res) ->
+ match res with
+ | GotoResult.Multiple _ -> failtest "Should be single GotoResult"
+ | GotoResult.Single res ->
+ Expect.stringContains res.Uri "Definition.fs" "Result should be in Definition.fs"
+
+ Expect.equal
+ res.Range
+ { Start = { Line = 4; Character = 5 }
+ End = { Line = 4; Character = 6 } }
+ "Result should have correct range"
+ })
+
+ testCaseAsync
+ "Go-to-type-defintion on parameter"
+ (async {
+ let! server, _path, externalPath, _definitionPath = server
+
+ // check for parameter of type `'a list` -> FSharp.Core
+ (*
`let myConcat listA listB = List.concat [listA; listB]`
^
position
*)
- let p: TextDocumentPositionParams =
- { TextDocument = { Uri = Path.FilePathToUri externalPath }
- Position = { Line = 12; Character = 16 } }
-
- let! res = server.TextDocumentTypeDefinition p
-
- match res with
- | Result.Error e -> failtestf "Request failed: %A" e
- | Result.Ok None -> failtest "Request none"
- | Result.Ok (Some res) ->
- match res with
- | GotoResult.Multiple _ -> failtest "Should be single GotoResult"
- | GotoResult.Single res ->
- Expect.stringContains res.Uri "FSharp.Core/prim-types" "Result should be in FSharp.Core's prim-types"
- let localPath = Path.FileUriToLocalPath res.Uri
-
- Expect.isTrue
- (System.IO.File.Exists localPath)
- (sprintf "File '%s' should exist locally after being downloaded" localPath)
- })
-
- testCaseAsync
- "Go-to-type-defintion on variable"
- (async {
- let! server, path, externalPath, definitionPath = server
-
- // check for variable of type `System.Collections.Generic.List<_>`
- (*
+ let p: TextDocumentPositionParams =
+ { TextDocument = { Uri = Path.FilePathToUri externalPath }
+ Position = { Line = 12; Character = 16 } }
+
+ let! res = server.TextDocumentTypeDefinition p
+
+ match res with
+ | Result.Error e -> failtestf "Request failed: %A" e
+ | Result.Ok None -> failtest "Request none"
+ | Result.Ok(Some res) ->
+ match res with
+ | GotoResult.Multiple _ -> failtest "Should be single GotoResult"
+ | GotoResult.Single res ->
+ Expect.stringContains res.Uri "FSharp.Core/prim-types" "Result should be in FSharp.Core's prim-types"
+ let localPath = Path.FileUriToLocalPath res.Uri
+
+ Expect.isTrue
+ (System.IO.File.Exists localPath)
+ (sprintf "File '%s' should exist locally after being downloaded" localPath)
+ })
+
+ testCaseAsync
+ "Go-to-type-defintion on variable"
+ (async {
+ let! server, _path, externalPath, _definitionPath = server
+
+ // check for variable of type `System.Collections.Generic.List<_>`
+ (*
`let myList = System.Collections.Generic.List()`
^
position
*)
- let p: TextDocumentPositionParams =
- { TextDocument = { Uri = Path.FilePathToUri externalPath }
- Position = { Line = 16; Character = 6 } }
-
- let! res = server.TextDocumentTypeDefinition p
-
- match res with
- | Result.Error e -> failtestf "Request failed: %A" e
- | Result.Ok None -> failtest "Request none"
- | Result.Ok (Some res) ->
- match res with
- | GotoResult.Multiple _ -> failtest "Should be single GotoResult"
- | GotoResult.Single res ->
- let localPath = Path.FileUriToLocalPath res.Uri
-
- Expect.stringContains
- res.Uri
- "System.Collections.Generic.List"
- "Result should be for System.Collections.Generic.List"
-
- Expect.isTrue
- (System.IO.File.Exists localPath)
- (sprintf "File '%s' should exist locally after being downloaded" localPath)
- })
-
- testCaseAsync
- "Go-to-type-defintion on constructor"
- (async {
- let! server, path, externalPath, definitionPath = server
-
- // check for constructor of type `System.Collections.Generic.List<_>`
- (*
+ let p: TextDocumentPositionParams =
+ { TextDocument = { Uri = Path.FilePathToUri externalPath }
+ Position = { Line = 16; Character = 6 } }
+
+ let! res = server.TextDocumentTypeDefinition p
+
+ match res with
+ | Result.Error e -> failtestf "Request failed: %A" e
+ | Result.Ok None -> failtest "Request none"
+ | Result.Ok(Some res) ->
+ match res with
+ | GotoResult.Multiple _ -> failtest "Should be single GotoResult"
+ | GotoResult.Single res ->
+ let localPath = Path.FileUriToLocalPath res.Uri
+
+ Expect.stringContains
+ res.Uri
+ "System.Collections.Generic.List"
+ "Result should be for System.Collections.Generic.List"
+
+ Expect.isTrue
+ (System.IO.File.Exists localPath)
+ (sprintf "File '%s' should exist locally after being downloaded" localPath)
+ })
+
+ testCaseAsync
+ "Go-to-type-defintion on constructor"
+ (async {
+ let! server, _path, externalPath, _definitionPath = server
+
+ // check for constructor of type `System.Collections.Generic.List<_>`
+ (*
`let myList = System.Collections.Generic.List()`
^
position
*)
- let p: TextDocumentPositionParams =
- { TextDocument = { Uri = Path.FilePathToUri externalPath }
- Position = { Line = 16; Character = 42 } }
-
- let! res = server.TextDocumentTypeDefinition p
-
- match res with
- | Result.Error e -> failtestf "Request failed: %A" e
- | Result.Ok None -> failtest "Request none"
- | Result.Ok (Some res) ->
- match res with
- | GotoResult.Multiple _ -> failtest "Should be single GotoResult"
- | GotoResult.Single res ->
- let localPath = Path.FileUriToLocalPath res.Uri
-
- Expect.stringContains
- res.Uri
- "System.Collections.Generic.List"
- "Result should be for System.Collections.Generic.List"
-
- Expect.isTrue
- (System.IO.File.Exists localPath)
- (sprintf "File '%s' should exist locally after being downloaded" localPath)
- })
-
- testCaseAsync
- "Go-to-type-defintion on union case"
- (async {
- let! server, path, externalPath, definitionPath = server
-
- // check for union case of type `_ option`
- (*
+ let p: TextDocumentPositionParams =
+ { TextDocument = { Uri = Path.FilePathToUri externalPath }
+ Position = { Line = 16; Character = 42 } }
+
+ let! res = server.TextDocumentTypeDefinition p
+
+ match res with
+ | Result.Error e -> failtestf "Request failed: %A" e
+ | Result.Ok None -> failtest "Request none"
+ | Result.Ok(Some res) ->
+ match res with
+ | GotoResult.Multiple _ -> failtest "Should be single GotoResult"
+ | GotoResult.Single res ->
+ let localPath = Path.FileUriToLocalPath res.Uri
+
+ Expect.stringContains
+ res.Uri
+ "System.Collections.Generic.List"
+ "Result should be for System.Collections.Generic.List"
+
+ Expect.isTrue
+ (System.IO.File.Exists localPath)
+ (sprintf "File '%s' should exist locally after being downloaded" localPath)
+ })
+
+ testCaseAsync
+ "Go-to-type-defintion on union case"
+ (async {
+ let! server, _path, externalPath, _definitionPath = server
+
+ // check for union case of type `_ option`
+ (*
`let o v = Some v`
^
position
*)
- let p: TextDocumentPositionParams =
- { TextDocument = { Uri = Path.FilePathToUri externalPath }
- Position = { Line = 18; Character = 12 } }
-
- let! res = server.TextDocumentTypeDefinition p
-
- match res with
- | Result.Error e -> failtestf "Request failed: %A" e
- | Result.Ok None -> failtest "Request none"
- | Result.Ok (Some res) ->
- match res with
- | GotoResult.Multiple _ -> failtest "Should be single GotoResult"
- | GotoResult.Single res ->
- Expect.stringContains res.Uri "FSharp.Core/prim-types" "Result should be in FSharp.Core's prim-types"
- let localPath = Path.FileUriToLocalPath res.Uri
-
- Expect.isTrue
- (System.IO.File.Exists localPath)
- (sprintf "File '%s' should exist locally after being downloaded" localPath)
- })
-
- testCaseAsync
- "Go-to-type-definition on property"
- (async {
- let! server, path, externalPath, definitionPath = server
-
- // check for property of type `string option`
- (*
+ let p: TextDocumentPositionParams =
+ { TextDocument = { Uri = Path.FilePathToUri externalPath }
+ Position = { Line = 18; Character = 12 } }
+
+ let! res = server.TextDocumentTypeDefinition p
+
+ match res with
+ | Result.Error e -> failtestf "Request failed: %A" e
+ | Result.Ok None -> failtest "Request none"
+ | Result.Ok(Some res) ->
+ match res with
+ | GotoResult.Multiple _ -> failtest "Should be single GotoResult"
+ | GotoResult.Single res ->
+ Expect.stringContains res.Uri "FSharp.Core/prim-types" "Result should be in FSharp.Core's prim-types"
+ let localPath = Path.FileUriToLocalPath res.Uri
+
+ Expect.isTrue
+ (System.IO.File.Exists localPath)
+ (sprintf "File '%s' should exist locally after being downloaded" localPath)
+ })
+
+ testCaseAsync
+ "Go-to-type-definition on property"
+ (async {
+ let! server, _path, externalPath, _definitionPath = server
+
+ // check for property of type `string option`
+ (*
`b.Value |> ignore`
^
position
*)
- let p: TextDocumentPositionParams =
- { TextDocument = { Uri = Path.FilePathToUri externalPath }
- Position = { Line = 24; Character = 5 } }
-
- let! res = server.TextDocumentTypeDefinition p
-
- match res with
- | Result.Error e -> failtestf "Request failed: %A" e
- | Result.Ok None -> failtest "Request none"
- | Result.Ok (Some res) ->
- match res with
- | GotoResult.Multiple _ -> failtest "Should be single GotoResult"
- | GotoResult.Single res ->
- Expect.stringContains res.Uri "FSharp.Core/prim-types" "Result should be in FSharp.Core's prim-types"
- let localPath = Path.FileUriToLocalPath res.Uri
-
- Expect.isTrue
- (System.IO.File.Exists localPath)
- (sprintf "File '%s' should exist locally after being downloaded" localPath)
- }) ]
+ let p: TextDocumentPositionParams =
+ { TextDocument = { Uri = Path.FilePathToUri externalPath }
+ Position = { Line = 24; Character = 5 } }
+
+ let! res = server.TextDocumentTypeDefinition p
+
+ match res with
+ | Result.Error e -> failtestf "Request failed: %A" e
+ | Result.Ok None -> failtest "Request none"
+ | Result.Ok(Some res) ->
+ match res with
+ | GotoResult.Multiple _ -> failtest "Should be single GotoResult"
+ | GotoResult.Single res ->
+ Expect.stringContains res.Uri "FSharp.Core/prim-types" "Result should be in FSharp.Core's prim-types"
+ let localPath = Path.FileUriToLocalPath res.Uri
+
+ Expect.isTrue
+ (System.IO.File.Exists localPath)
+ (sprintf "File '%s' should exist locally after being downloaded" localPath)
+ }) ]
let private scriptGotoTests state =
let server =
@@ -502,122 +503,121 @@ let private scriptGotoTests state =
// sequenced because we don't provide safe concurrent access to file downloads in Sourcelink.fs
testSequenced
<| testList
- "Script GoTo Tests"
- [ testCaseAsync
- "Go-to-definition on #load integration test"
- (async {
- let! server, scriptPath = server
-
- let p: TextDocumentPositionParams =
- { TextDocument = { Uri = Path.FilePathToUri scriptPath }
- Position = { Line = 0; Character = 10 } }
-
- let! res = server.TextDocumentDefinition p
-
- match res with
- | Error e -> failtestf "Request failed: %A" e
- | Ok None -> failtest "Request none"
- | Ok (Some (GotoResult.Multiple _)) -> failtest "Should only get one location"
- | Ok (Some (GotoResult.Single r)) ->
- Expect.stringEnds r.Uri "/simple.fsx" "should navigate to the mentioned script file"
- })
- testCaseAsync
- "Go-to-definition on first char of identifier works"
- (async {
- let! server, scriptPath = server
-
- let p: TextDocumentPositionParams =
- { TextDocument = { Uri = Path.FilePathToUri scriptPath }
- Position = { Line = 5; Character = 0 } // beginning of the usage of `testFunction` in the script file
- }
-
- let! res = server.TextDocumentDefinition p
-
- match res with
- | Error e -> failtestf "Request failed: %A" e
- | Ok None -> failtest "Request none"
- | Ok (Some (GotoResult.Multiple _)) -> failtest "Should only get one location"
- | Ok (Some (GotoResult.Single r)) ->
- Expect.stringEnds r.Uri (Path.GetFileName scriptPath) "should navigate to the mentioned script file"
-
- Expect.equal
- r.Range
- { Start = { Line = 3; Character = 4 }
- End = { Line = 3; Character = 16 } }
- "should point to the range of the definition of `testFunction`"
- }) ]
+ "Script GoTo Tests"
+ [ testCaseAsync
+ "Go-to-definition on #load integration test"
+ (async {
+ let! server, scriptPath = server
+
+ let p: TextDocumentPositionParams =
+ { TextDocument = { Uri = Path.FilePathToUri scriptPath }
+ Position = { Line = 0; Character = 10 } }
+
+ let! res = server.TextDocumentDefinition p
+
+ match res with
+ | Error e -> failtestf "Request failed: %A" e
+ | Ok None -> failtest "Request none"
+ | Ok(Some(GotoResult.Multiple _)) -> failtest "Should only get one location"
+ | Ok(Some(GotoResult.Single r)) ->
+ Expect.stringEnds r.Uri "/simple.fsx" "should navigate to the mentioned script file"
+ })
+ testCaseAsync
+ "Go-to-definition on first char of identifier works"
+ (async {
+ let! server, scriptPath = server
+
+ let p: TextDocumentPositionParams =
+ { TextDocument = { Uri = Path.FilePathToUri scriptPath }
+ Position = { Line = 5; Character = 0 } // beginning of the usage of `testFunction` in the script file
+ }
+
+ let! res = server.TextDocumentDefinition p
+
+ match res with
+ | Error e -> failtestf "Request failed: %A" e
+ | Ok None -> failtest "Request none"
+ | Ok(Some(GotoResult.Multiple _)) -> failtest "Should only get one location"
+ | Ok(Some(GotoResult.Single r)) ->
+ Expect.stringEnds r.Uri (Path.GetFileName scriptPath) "should navigate to the mentioned script file"
+
+ Expect.equal
+ r.Range
+ { Start = { Line = 3; Character = 4 }
+ End = { Line = 3; Character = 16 } }
+ "should point to the range of the definition of `testFunction`"
+ }) ]
let private untitledGotoTests state =
- serverTestList "Untitled GoTo Tests" state defaultConfigDto None (fun server -> [
- testCaseAsync "can go to variable declaration" <| async {
- let (usagePos, declRange, text) =
- """
+ serverTestList "Untitled GoTo Tests" state defaultConfigDto None (fun server ->
+ [ testCaseAsync "can go to variable declaration"
+ <| async {
+ let (usagePos, declRange, text) =
+ """
let $0x$0 = 1
let _ = ()
let a = $0x
"""
- |> Text.trimTripleQuotation
- |> Cursor.assertExtractRange
- |> fun (decl, text) ->
- let (pos, text) =
- text
- |> Cursor.assertExtractPosition
- (pos, decl, text)
- let! (doc, diags) = server |> Server.createUntitledDocument text
-
- use doc = doc
-
- let p : TextDocumentPositionParams = {
- TextDocument = doc.TextDocumentIdentifier
- Position = usagePos
+ |> Text.trimTripleQuotation
+ |> Cursor.assertExtractRange
+ |> fun (decl, text) ->
+ let (pos, text) = text |> Cursor.assertExtractPosition
+ (pos, decl, text)
+
+ let! (doc, _diags) = server |> Server.createUntitledDocument text
+
+ use doc = doc
+
+ let p: TextDocumentPositionParams =
+ { TextDocument = doc.TextDocumentIdentifier
+ Position = usagePos }
+
+ let! res = doc.Server.Server.TextDocumentDefinition p
+
+ match res with
+ | Error e -> failtestf "Request failed: %A" e
+ | Ok None -> failtest "Request none"
+ | Ok(Some(GotoResult.Multiple _)) -> failtest "Should only get one location"
+ | Ok(Some(GotoResult.Single r)) ->
+ Expect.stringEnds r.Uri doc.Uri "should navigate to source file"
+ Expect.equal r.Range declRange "should point to the range of variable declaration"
}
- let! res = doc.Server.Server.TextDocumentDefinition p
- match res with
- | Error e -> failtestf "Request failed: %A" e
- | Ok None -> failtest "Request none"
- | Ok (Some (GotoResult.Multiple _)) -> failtest "Should only get one location"
- | Ok (Some (GotoResult.Single r)) ->
- Expect.stringEnds r.Uri doc.Uri "should navigate to source file"
- Expect.equal r.Range declRange "should point to the range of variable declaration"
- }
- testCaseAsync "can go to function declaration" <| async {
- let (usagePos, declRange, text) =
- """
+ testCaseAsync "can go to function declaration"
+ <| async {
+ let (usagePos, declRange, text) =
+ """
let $0myFun$0 a b = a + b
let _ = ()
let a = my$0Fun 1 1
"""
- |> Text.trimTripleQuotation
- |> Cursor.assertExtractRange
- |> fun (decl, text) ->
- let (pos, text) =
- text
- |> Cursor.assertExtractPosition
- (pos, decl, text)
- let! (doc, diags) = server |> Server.createUntitledDocument text
- use doc = doc
-
- let p : TextDocumentPositionParams = {
- TextDocument = doc.TextDocumentIdentifier
- Position = usagePos
- }
- let! res = doc.Server.Server.TextDocumentDefinition p
- match res with
- | Error e -> failtestf "Request failed: %A" e
- | Ok None -> failtest "Request none"
- | Ok (Some (GotoResult.Multiple _)) -> failtest "Should only get one location"
- | Ok (Some (GotoResult.Single r)) ->
- Expect.stringEnds r.Uri doc.Uri "should navigate to source file"
- Expect.equal r.Range declRange "should point to the range of function declaration"
- }
- ])
+ |> Text.trimTripleQuotation
+ |> Cursor.assertExtractRange
+ |> fun (decl, text) ->
+ let (pos, text) = text |> Cursor.assertExtractPosition
+ (pos, decl, text)
+
+ let! (doc, _diags) = server |> Server.createUntitledDocument text
+ use doc = doc
+
+ let p: TextDocumentPositionParams =
+ { TextDocument = doc.TextDocumentIdentifier
+ Position = usagePos }
+
+ let! res = doc.Server.Server.TextDocumentDefinition p
+
+ match res with
+ | Error e -> failtestf "Request failed: %A" e
+ | Ok None -> failtest "Request none"
+ | Ok(Some(GotoResult.Multiple _)) -> failtest "Should only get one location"
+ | Ok(Some(GotoResult.Single r)) ->
+ Expect.stringEnds r.Uri doc.Uri "should navigate to source file"
+ Expect.equal r.Range declRange "should point to the range of function declaration"
+ } ])
let tests createServer =
testSequenced
<| testList
- "Go to definition tests"
- [
- gotoTest createServer
- scriptGotoTests createServer
- untitledGotoTests createServer
- ]
+ "Go to definition tests"
+ [ gotoTest createServer
+ scriptGotoTests createServer
+ untitledGotoTests createServer ]
diff --git a/test/FsAutoComplete.Tests.Lsp/Helpers.fs b/test/FsAutoComplete.Tests.Lsp/Helpers.fs
index 13659bad5..59e32b886 100644
--- a/test/FsAutoComplete.Tests.Lsp/Helpers.fs
+++ b/test/FsAutoComplete.Tests.Lsp/Helpers.fs
@@ -14,35 +14,35 @@ open FSharp.UMX
module Expecto =
open System.Threading.Tasks
- let inline testBuilderWithTimeout (ts : TimeSpan) name testCase focus =
- TestLabel(name, TestCase (Test.timeout (int ts.TotalMilliseconds) (testCase), focus), focus)
-
- let inline testCaseWithTimeout (ts : TimeSpan) name test =
- testBuilderWithTimeout ts name (Sync test) Normal
- let inline ftestCaseWithTimeout (ts : TimeSpan) name test =
- testBuilderWithTimeout ts name (Sync test) Focused
- let inline ptestCaseWithTimeout (ts : TimeSpan) name test =
- testBuilderWithTimeout ts name (Sync test) Pending
-
- let inline testCaseAsyncWithTimeout (ts : TimeSpan) name test =
- testBuilderWithTimeout ts name (Async test) Normal
- let inline ftestCaseAsyncWithTimeout (ts : TimeSpan) name test =
- testBuilderWithTimeout ts name (Async test) Focused
- let inline ptestCaseAsyncWithTimeout (ts : TimeSpan) name test =
- testBuilderWithTimeout ts name (Async test) Pending
-
- let inline testCaseTaskWithTimeout (ts : TimeSpan) name (test : unit -> Task) =
+
+ let inline testBuilderWithTimeout (ts: TimeSpan) name testCase focus =
+ TestLabel(name, TestCase(Test.timeout (int ts.TotalMilliseconds) (testCase), focus), focus)
+
+ let inline testCaseWithTimeout (ts: TimeSpan) name test = testBuilderWithTimeout ts name (Sync test) Normal
+ let inline ftestCaseWithTimeout (ts: TimeSpan) name test = testBuilderWithTimeout ts name (Sync test) Focused
+ let inline ptestCaseWithTimeout (ts: TimeSpan) name test = testBuilderWithTimeout ts name (Sync test) Pending
+
+ let inline testCaseAsyncWithTimeout (ts: TimeSpan) name test = testBuilderWithTimeout ts name (Async test) Normal
+ let inline ftestCaseAsyncWithTimeout (ts: TimeSpan) name test = testBuilderWithTimeout ts name (Async test) Focused
+ let inline ptestCaseAsyncWithTimeout (ts: TimeSpan) name test = testBuilderWithTimeout ts name (Async test) Pending
+
+ let inline testCaseTaskWithTimeout (ts: TimeSpan) name (test: unit -> Task) =
testCaseAsyncWithTimeout ts name (async.Delay(fun () -> Async.AwaitTask(test ())))
- let inline ftestCaseTaskWithTimeout (ts : TimeSpan) name (test : unit -> Task) =
+
+ let inline ftestCaseTaskWithTimeout (ts: TimeSpan) name (test: unit -> Task) =
ftestCaseAsyncWithTimeout ts name (async.Delay(fun () -> Async.AwaitTask(test ())))
- let inline ptestCaseTaskWithTimeout (ts : TimeSpan) name (test : unit -> Task) =
+
+ let inline ptestCaseTaskWithTimeout (ts: TimeSpan) name (test: unit -> Task) =
ptestCaseAsyncWithTimeout ts name (async.Delay(fun () -> Async.AwaitTask(test ())))
// millisecond
let DEFAULT_TIMEOUT =
Environment.GetEnvironmentVariable "FSAC_TEST_DEFAULT_TIMEOUT"
|> Option.ofObj
- |> Option.bind(fun x -> match Int32.TryParse x with | (true, v) -> Some v | _ -> None )
+ |> Option.bind (fun x ->
+ match Int32.TryParse x with
+ | (true, v) -> Some v
+ | _ -> None)
|> Option.defaultValue (60000)
|> TimeSpan.FromMilliseconds
@@ -159,17 +159,16 @@ type Async =
/// previous execution.
static member Cache(input: Async<'T>) =
let agent =
- MailboxProcessor>.Start
- (fun agent ->
- async {
+ MailboxProcessor>.Start(fun agent ->
+ async {
+ let! repl = agent.Receive()
+ let! res = input |> Async.Catch
+ repl.Reply(res)
+
+ while true do
let! repl = agent.Receive()
- let! res = input |> Async.Catch
repl.Reply(res)
-
- while true do
- let! repl = agent.Receive()
- repl.Reply(res)
- })
+ })
async {
let! result = agent.PostAndAsyncReply(id)
@@ -343,9 +342,7 @@ let clientCaps: ClientCapabilities =
CompletionItemKind = Some cikCaps
ContextSupport = Some true
InsertTextMode = Some InsertTextMode.AsIs
- CompletionList = Some {
- ItemDefaults = None
- } }
+ CompletionList = Some { ItemDefaults = None } }
let hoverCaps: HoverCapabilities =
{ DynamicRegistration = Some true
@@ -368,7 +365,7 @@ let clientCaps: ClientCapabilities =
SymbolKind = Some skCaps
HierarchicalDocumentSymbolSupport = Some false
TagSupport = None
- LabelSupport = Some true }
+ LabelSupport = Some true }
let foldingRangeCaps: FoldingRangeCapabilities =
{ DynamicRegistration = Some true
@@ -403,7 +400,7 @@ let clientCaps: ClientCapabilities =
{ DynamicRegistration = Some true
ResolveSupport = None }
- let inlineValueCaps: InlineValueClientCapabilities =
+ let _inlineValueCaps: InlineValueClientCapabilities =
{ DynamicRegistration = Some true
ResolveSupport = None }
@@ -430,16 +427,12 @@ let clientCaps: ClientCapabilities =
LinkSupport = Some false }
let docLinkCaps: DocumentLinkCapabilities =
- {
- DynamicRegistration = Some true
- TooltipSupport = Some true
- }
+ { DynamicRegistration = Some true
+ TooltipSupport = Some true }
let diagCaps: DiagnosticCapabilities =
- {
- DynamicRegistration = Some true
- RelatedDocumentSupport = Some true
- }
+ { DynamicRegistration = Some true
+ RelatedDocumentSupport = Some true }
{ Synchronization = Some syncCaps
PublishDiagnostics = Some publishDiagCaps
@@ -470,8 +463,7 @@ let clientCaps: ClientCapabilities =
LinkedEditingRange = Some dynCaps
Moniker = Some dynCaps
InlineValue = Some dynCaps
- Diagnostic = Some diagCaps
- }
+ Diagnostic = Some diagCaps }
{ Workspace = Some workspaceCaps
TextDocument = Some textCaps
@@ -486,8 +478,7 @@ open FsAutoComplete.CommandResponse
open CliWrap
open CliWrap.Buffered
-let logEvent (name, payload) =
- logger.Value.Debug("{name}: {payload}", name, payload)
+let logEvent (name, payload) = logger.Value.Debug("{name}: {payload}", name, payload)
let logDotnetRestore section line =
if not (String.IsNullOrWhiteSpace(line)) then
@@ -504,15 +495,12 @@ let runProcess (workingDir: string) (exePath: string) (args: string) =
let! ctok = Async.CancellationToken
let! result =
- Cli.Wrap(exePath).WithArguments(args).WithWorkingDirectory(
- workingDir
- )
- .WithValidation(
- CommandResultValidation.None
- )
- .ExecuteBufferedAsync(
- ctok
- )
+ Cli
+ .Wrap(exePath)
+ .WithArguments(args)
+ .WithWorkingDirectory(workingDir)
+ .WithValidation(CommandResultValidation.None)
+ .ExecuteBufferedAsync(ctok)
.Task
|> Async.AwaitTask
@@ -525,15 +513,17 @@ let inline expectExitCodeZero (r: BufferedCommandResult) =
0
$"Expected exit code zero but was %i{r.ExitCode}.\nStdOut: %s{r.StandardOutput}\nStdErr: %s{r.StandardError}"
-let dotnetRestore dir = async {
- let! r = runProcess (DirectoryInfo(dir).FullName) "dotnet" "restore -v d"
- return expectExitCodeZero r
-}
+let dotnetRestore dir =
+ async {
+ let! r = runProcess (DirectoryInfo(dir).FullName) "dotnet" "restore -v d"
+ return expectExitCodeZero r
+ }
-let dotnetToolRestore dir = async {
- let! r = runProcess (DirectoryInfo(dir).FullName) "dotnet" "tool restore"
- return expectExitCodeZero r
-}
+let dotnetToolRestore dir =
+ async {
+ let! r = runProcess (DirectoryInfo(dir).FullName) "dotnet" "tool restore"
+ return expectExitCodeZero r
+ }
let serverInitialize path (config: FSharpConfigDto) createServer =
async {
@@ -565,10 +555,10 @@ let serverInitialize path (config: FSharpConfigDto) createServer =
let! result = server.Initialize p
match result with
- | Result.Ok res ->
+ | Result.Ok _res ->
do! server.Initialized(InitializedParams())
return (server, clientNotifications)
- | Result.Error e -> return failwith "Initialization failed"
+ | Result.Error _e -> return failwith "Initialization failed"
}
let loadDocument path : TextDocumentItem =
@@ -600,8 +590,8 @@ let waitForWorkspaceFinishedParsing (events: ClientEvents) =
match name with
| "fsharp/notifyWorkspace" ->
match unbox payload with
- | (UnwrappedPlainNotification "workspaceLoad"
- (workspaceLoadResponse: FsAutoComplete.CommandResponse.WorkspaceLoadResponse)) ->
+ | (UnwrappedPlainNotification "workspaceLoad" (workspaceLoadResponse:
+ FsAutoComplete.CommandResponse.WorkspaceLoadResponse)) ->
if workspaceLoadResponse.Status = "finished" then
Some()
else
@@ -619,13 +609,10 @@ let waitForWorkspaceFinishedParsing (events: ClientEvents) =
let private typedEvents<'t> (typ: string) : IObservable -> IObservable<'t> =
Observable.choose (fun (typ', _o) -> if typ' = typ then Some(unbox _o) else None)
-let private payloadAs<'t> = Observable.map (fun (_typ, o) -> unbox<'t> o)
-
let private getDiagnosticsEvents: IObservable -> IObservable<_> =
typedEvents "textDocument/publishDiagnostics"
-let private fileName (u: DocumentUri) =
- u.Split([| '/'; '\\' |], StringSplitOptions.RemoveEmptyEntries) |> Array.last
+let private fileName (u: DocumentUri) = u.Split([| '/'; '\\' |], StringSplitOptions.RemoveEmptyEntries) |> Array.last
/// note that the files here are intended to be the filename only., not the full URI.
let private matchFiles (files: string Set) =
@@ -670,40 +657,38 @@ let fileDiagnosticsForUri (uri: string) =
let diagnosticsFromSource (desiredSource: String) =
Observable.choose (fun (diags: Diagnostic[]) ->
- match diags
- |> Array.choose (fun d ->
- match d.Source with
- | Some s -> if s.StartsWith(desiredSource, StringComparison.Ordinal) then Some d else None
- | None -> None)
- with
+ match
+ diags
+ |> Array.choose (fun d ->
+ match d.Source with
+ | Some s ->
+ if s.StartsWith(desiredSource, StringComparison.Ordinal) then
+ Some d
+ else
+ None
+ | None -> None)
+ with
| [||] -> None
| diags -> Some diags)
-let analyzerDiagnostics file =
- fileDiagnostics file >> diagnosticsFromSource "F# Analyzers"
+let analyzerDiagnostics file = fileDiagnostics file >> diagnosticsFromSource "F# Analyzers"
-let linterDiagnostics file =
- fileDiagnostics file >> diagnosticsFromSource "F# Linter"
+let linterDiagnostics file = fileDiagnostics file >> diagnosticsFromSource "F# Linter"
-let fsacDiagnostics file =
- fileDiagnostics file >> diagnosticsFromSource "FSAC"
+let fsacDiagnostics file = fileDiagnostics file >> diagnosticsFromSource "FSAC"
-let compilerDiagnostics file =
- fileDiagnostics file >> diagnosticsFromSource "F# Compiler"
+let compilerDiagnostics file = fileDiagnostics file >> diagnosticsFromSource "F# Compiler"
let diagnosticsToResult =
Observable.map (function
| [||] -> Ok()
| diags -> Core.Error diags)
-let waitForParseResultsForFile file =
- fileDiagnostics file >> diagnosticsToResult >> Async.AwaitObservable
+let waitForParseResultsForFile file = fileDiagnostics file >> diagnosticsToResult >> Async.AwaitObservable
-let waitForFsacDiagnosticsForFile file =
- fsacDiagnostics file >> diagnosticsToResult >> Async.AwaitObservable
+let waitForFsacDiagnosticsForFile file = fsacDiagnostics file >> diagnosticsToResult >> Async.AwaitObservable
-let waitForCompilerDiagnosticsForFile file =
- compilerDiagnostics file >> diagnosticsToResult >> Async.AwaitObservable
+let waitForCompilerDiagnosticsForFile file = compilerDiagnostics file >> diagnosticsToResult >> Async.AwaitObservable
let waitForParsedScript (event: ClientEvents) =
event
@@ -721,8 +706,7 @@ let waitForTestDetected (fileName: string) (events: ClientEvents) : Async Async.AwaitObservable
-let waitForEditsForFile file =
- workspaceEdits >> editsFor file >> Async.AwaitObservable
+let waitForEditsForFile file = workspaceEdits >> editsFor file >> Async.AwaitObservable
let trySerialize (t: string) : 't option =
try
@@ -732,7 +716,7 @@ let trySerialize (t: string) : 't option =
let (|As|_|) (m: PlainNotification) : 't option =
match trySerialize m.Content with
- | Some (r: FsAutoComplete.CommandResponse.ResponseMsg<'t>) -> Some r.Data
+ | Some(r: FsAutoComplete.CommandResponse.ResponseMsg<'t>) -> Some r.Data
| None -> None
let (|CodeActions|_|) (t: TextDocumentCodeActionResult) =
diff --git a/test/FsAutoComplete.Tests.Lsp/HighlightingTests.fs b/test/FsAutoComplete.Tests.Lsp/HighlightingTests.fs
index 3d1d40c59..78c93951a 100644
--- a/test/FsAutoComplete.Tests.Lsp/HighlightingTests.fs
+++ b/test/FsAutoComplete.Tests.Lsp/HighlightingTests.fs
@@ -20,18 +20,16 @@ let tests state =
do! server.TextDocumentDidOpen tdop
match! waitForParseResultsForFile "Script.fsx" event with
- | Ok () -> return server
+ | Ok() -> return server
| Error errors ->
let errorStrings =
- errors
- |> Array.map (fun e -> e.DebuggerDisplay)
- |> String.concat "\n\t* "
+ errors |> Array.map (fun e -> e.DebuggerDisplay) |> String.concat "\n\t* "
return failtestf "Errors while parsing highlighting script:\n\t* %s" errorStrings
}
|> Async.Cache
- let decodeHighlighting (data: uint32 []) =
+ let decodeHighlighting (data: uint32[]) =
let zeroLine = [| 0u; 0u; 0u; 0u; 0u |]
let lines = Array.append [| zeroLine |] (Array.chunkBySize 5 data)
@@ -76,7 +74,7 @@ let tests state =
let! highlights = server.TextDocumentSemanticTokensFull p
match highlights with
- | Ok (Some highlights) ->
+ | Ok(Some highlights) ->
let decoded = highlights.Data |> decodeHighlighting
// printfn "%A" decoded
return decoded
@@ -94,7 +92,7 @@ let tests state =
let tokenIsOfType
((line, char) as pos)
testTokenType
- (highlights: (Range * ClassificationUtils.SemanticTokenTypes * ClassificationUtils.SemanticTokenModifier) [] Async)
+ (highlights: (Range * ClassificationUtils.SemanticTokenTypes * ClassificationUtils.SemanticTokenModifier)[] Async)
=
testCaseAsync
$"can find token of type {testTokenType} at %A{pos}"
@@ -109,7 +107,7 @@ let tests state =
})
/// this tests the range endpoint by getting highlighting for a range then doing the normal highlighting test
- let tokenIsOfTypeInRange ((startLine, startChar), (endLine, endChar)) ((line, char)) testTokenType =
+ let _tokenIsOfTypeInRange ((startLine, startChar), (endLine, endChar)) ((line, char)) testTokenType =
testCaseAsync
$"can find token of type {testTokenType} in a subrange from ({startLine}, {startChar})-({endLine}, {endChar})"
(async {
@@ -127,8 +125,8 @@ let tests state =
server.TextDocumentSemanticTokensRange
{ Range = range
TextDocument = { Uri = Path.FilePathToUri scriptPath } }
- with
- | Ok (Some highlights) ->
+ with
+ | Ok(Some highlights) ->
let decoded = decodeHighlighting highlights.Data
Expect.exists
diff --git a/test/FsAutoComplete.Tests.Lsp/InfoPanelTests.fs b/test/FsAutoComplete.Tests.Lsp/InfoPanelTests.fs
index ffb21047b..078535b59 100644
--- a/test/FsAutoComplete.Tests.Lsp/InfoPanelTests.fs
+++ b/test/FsAutoComplete.Tests.Lsp/InfoPanelTests.fs
@@ -20,7 +20,7 @@ let docFormattingTest state =
do!
waitForParseResultsForFile "Script.fsx" events
- |> AsyncResult.bimap id (fun e -> failtest "should have not had check errors")
+ |> AsyncResult.bimap id (fun _e -> failtest "should have not had check errors")
return (server, path)
}
@@ -40,7 +40,7 @@ let docFormattingTest state =
match doc with
| Result.Error err -> failtest $"Doc error: {err.Message}"
- | Result.Ok (Some(As (model: FsAutoComplete.CommandResponse.DocumentationDescription ))) ->
+ | Result.Ok(Some(As(model: FsAutoComplete.CommandResponse.DocumentationDescription))) ->
Expect.stringContains model.Signature "'Key, 'U" "Formatted doc contains both params separated by (, )"
| Result.Ok _ -> failtest "couldn't parse doc as the json type we expected"
})
@@ -57,7 +57,7 @@ let docFormattingTest state =
match doc with
| Result.Error err -> failtest $"Doc error: {err.Message}"
- | Result.Ok (Some (As (model: FsAutoComplete.CommandResponse.DocumentationDescription))) ->
+ | Result.Ok(Some(As(model: FsAutoComplete.CommandResponse.DocumentationDescription))) ->
Expect.stringContains model.Signature "'T1 * 'T2 * 'T3" "Formatted doc contains 3 params separated by ( * )"
| Result.Ok _ -> failtest "couldn't parse doc as the json type we expected"
}) ]
diff --git a/test/FsAutoComplete.Tests.Lsp/InlayHintTests.fs b/test/FsAutoComplete.Tests.Lsp/InlayHintTests.fs
index 6b8a6e331..b45c93e3d 100644
--- a/test/FsAutoComplete.Tests.Lsp/InlayHintTests.fs
+++ b/test/FsAutoComplete.Tests.Lsp/InlayHintTests.fs
@@ -147,7 +147,7 @@ module private LspInlayHints =
(server: CachedServer)
(text: string)
(range: Range)
- (validateInlayHints: Document -> string -> InlayHint[] -> Async)
+ (validateInlayHints: string -> InlayHint[] -> Async)
=
async {
let! (doc, diags) = server |> Server.createUntitledDocument text
@@ -156,11 +156,10 @@ module private LspInlayHints =
let! hints = doc |> Document.inlayHintsAt range
let hints = hints |> Array.sortBy (fun h -> h.Position)
- do! validateInlayHints doc text hints
+ do! validateInlayHints text hints
}
let private validateHint
- (doc: Document)
(expectedBase: InlayHint)
(textAfterEdits: string option)
(text: string)
@@ -232,12 +231,12 @@ module private LspInlayHints =
let hint = { hint with Position = cursor }
(hint, textAfterEdits))
- let validateHints doc (text: string) (hints: InlayHint[]) =
+ let validateHints (text: string) (hints: InlayHint[]) =
async {
Expect.hasLength hints expected.Length "Number of actual hints and expected hints don't match"
for (actual, (expected, textAfterEdits)) in Seq.zip hints expected do
- do! validateHint doc expected textAfterEdits text actual
+ do! validateHint expected textAfterEdits text actual
}
do! checkInRange server text range validateHints
diff --git a/test/FsAutoComplete.Tests.Lsp/InlineHintsTests.fs b/test/FsAutoComplete.Tests.Lsp/InlineHintsTests.fs
index 5f5cc51b5..17bc613bd 100644
--- a/test/FsAutoComplete.Tests.Lsp/InlineHintsTests.fs
+++ b/test/FsAutoComplete.Tests.Lsp/InlineHintsTests.fs
@@ -17,7 +17,7 @@ let private testInlineHints (server : CachedServer) text checkResp =
|> Text.trimTripleQuotation
|> Cursor.assertExtractRange
- let! (doc, diags) = server |> Server.createUntitledDocument text
+ let! doc, _diags = server |> Server.createUntitledDocument text
use doc = doc
let inlineValueRequest: InlineValueParams = {
diff --git a/test/FsAutoComplete.Tests.Lsp/Program.fs b/test/FsAutoComplete.Tests.Lsp/Program.fs
index 7444aacd8..a58565b8e 100644
--- a/test/FsAutoComplete.Tests.Lsp/Program.fs
+++ b/test/FsAutoComplete.Tests.Lsp/Program.fs
@@ -32,23 +32,19 @@ let testTimeout =
Environment.SetEnvironmentVariable("FSAC_WORKSPACELOAD_DELAY", "250")
let loaders =
- [
- "Ionide WorkspaceLoader", (fun toolpath -> WorkspaceLoader.Create(toolpath, FsAutoComplete.Core.ProjectLoader.globalProperties))
+ [ "Ionide WorkspaceLoader",
+ (fun toolpath -> WorkspaceLoader.Create(toolpath, FsAutoComplete.Core.ProjectLoader.globalProperties))
// "MSBuild Project Graph WorkspaceLoader", (fun toolpath -> WorkspaceLoaderViaProjectGraph.Create(toolpath, FsAutoComplete.Core.ProjectLoader.globalProperties))
- ]
+ ]
let adaptiveLspServerFactory toolsPath workspaceLoaderFactory sourceTextFactory =
Helpers.createAdaptiveServer (fun () -> workspaceLoaderFactory toolsPath) sourceTextFactory
-let lspServers =
- [
- "AdaptiveLspServer", adaptiveLspServerFactory
- ]
+let lspServers = [ "AdaptiveLspServer", adaptiveLspServerFactory ]
-let sourceTextFactories: (string * ISourceTextFactory) list = [
- "RoslynSourceText", RoslynSourceTextFactory()
-]
+let sourceTextFactories: (string * ISourceTextFactory) list =
+ [ "RoslynSourceText", RoslynSourceTextFactory() ]
let mutable toolsPath =
Ionide.ProjInfo.Init.init (System.IO.DirectoryInfo Environment.CurrentDirectory) None
@@ -62,10 +58,8 @@ let lspTests =
testList
$"{loaderName}.{lspName}.{sourceTextName}"
- [
- Templates.tests ()
- let createServer () =
- lspFactory toolsPath workspaceLoaderFactory sourceTextFactory
+ [ Templates.tests ()
+ let createServer () = lspFactory toolsPath workspaceLoaderFactory sourceTextFactory
initTests createServer
closeTests createServer
@@ -111,25 +105,19 @@ let lspTests =
DependentFileChecking.tests createServer
UnusedDeclarationsTests.tests createServer
EmptyFileTests.tests createServer
- CallHierarchy.tests createServer
- ] ]
+ CallHierarchy.tests createServer ] ]
/// Tests that do not require a LSP server
-let generalTests = testList "general" [
- testList (nameof (Utils)) [ Utils.Tests.Utils.tests; Utils.Tests.TextEdit.tests ]
- for (name, factory) in sourceTextFactories do
- InlayHintTests.explicitTypeInfoTests (name, factory)
- FindReferences.tryFixupRangeTests (name, factory)
-]
+let generalTests =
+ testList
+ "general"
+ [ testList (nameof (Utils)) [ Utils.Tests.Utils.tests; Utils.Tests.TextEdit.tests ]
+ for (name, factory) in sourceTextFactories do
+ InlayHintTests.explicitTypeInfoTests (name, factory)
+ FindReferences.tryFixupRangeTests (name, factory) ]
[]
-let tests =
- testList
- "FSAC"
- [
- generalTests
- lspTests
- ]
+let tests = testList "FSAC" [ generalTests; lspTests ]
[]
@@ -146,7 +134,7 @@ let main args =
|> Array.tryFind (fun arg -> arg.StartsWith(logMarker, StringComparison.Ordinal))
|> Option.map (fun log -> log.Substring(logMarker.Length))
with
- | Some ("warn" | "warning") -> Logging.LogLevel.Warn
+ | Some("warn" | "warning") -> Logging.LogLevel.Warn
| Some "error" -> Logging.LogLevel.Error
| Some "fatal" -> Logging.LogLevel.Fatal
| Some "info" -> Logging.LogLevel.Info
@@ -177,7 +165,9 @@ let main args =
|> Array.filter (fun arg -> arg.StartsWith(excludeMarker, StringComparison.Ordinal))
|> Array.collect (fun arg -> arg.Substring(excludeMarker.Length).Split(','))
- let args = args |> Array.filter (fun arg -> not <| arg.StartsWith(excludeMarker, StringComparison.Ordinal))
+ let args =
+ args
+ |> Array.filter (fun arg -> not <| arg.StartsWith(excludeMarker, StringComparison.Ordinal))
toExclude, args
@@ -191,7 +181,7 @@ let main args =
fun s -> s <> null && logSourcesToExclude |> Array.contains s
)
- let argsToRemove, loaders =
+ let argsToRemove, _loaders =
args
|> Array.windowed 2
|> Array.tryPick (function
@@ -208,10 +198,8 @@ let main args =
.Filter.ByExcluding(Matching.FromSource("FileSystem"))
.Filter.ByExcluding(sourcesToExclude)
- .Destructure
- .FSharpTypes()
- .Destructure
- .ByTransforming(fun r ->
+ .Destructure.FSharpTypes()
+ .Destructure.ByTransforming(fun r ->
box
{| FileName = r.FileName
Start = r.Start
@@ -219,8 +207,7 @@ let main args =
.Destructure.ByTransforming(fun r -> box {| Line = r.Line; Column = r.Column |})
.Destructure.ByTransforming(fun tok -> tok.ToString() |> box)
.Destructure.ByTransforming(fun di -> box di.FullName)
- .WriteTo
- .Async(fun c ->
+ .WriteTo.Async(fun c ->
c.Console(
outputTemplate = outputTemplate,
standardErrorFromLevel = Nullable<_>(LogEventLevel.Verbose),
@@ -237,13 +224,10 @@ let main args =
let cts = new CancellationTokenSource(testTimeout)
- let args =
- [
- CLIArguments.Printer (Expecto.Impl.TestPrinters.summaryWithLocationPrinter defaultConfig.printer)
+ let args =
+ [ CLIArguments.Printer(Expecto.Impl.TestPrinters.summaryWithLocationPrinter defaultConfig.printer)
CLIArguments.Verbosity logLevel
// CLIArguments.Parallel
- ]
+ ]
runTestsWithCLIArgsAndCancel cts.Token args fixedUpArgs tests
-
-
diff --git a/test/FsAutoComplete.Tests.Lsp/RenameTests.fs b/test/FsAutoComplete.Tests.Lsp/RenameTests.fs
index e8f410c51..e9ba9e8b7 100644
--- a/test/FsAutoComplete.Tests.Lsp/RenameTests.fs
+++ b/test/FsAutoComplete.Tests.Lsp/RenameTests.fs
@@ -14,9 +14,7 @@ open Utils.TextEdit
open Helpers.Expecto.ShadowedTimeouts
let private normalizePathCasing =
- Path.FilePathToUri
- >> Path.FileUriToLocalPath
- >> Path.FilePathToUri
+ Path.FilePathToUri >> Path.FileUriToLocalPath >> Path.FilePathToUri
let private sameProjectTests state =
let testDir =
@@ -40,7 +38,7 @@ let private sameProjectTests state =
match res with
| Result.Error e -> failtestf "Request failed: %A" e
| Result.Ok None -> failtest "Request none"
- | Result.Ok (Some res) ->
+ | Result.Ok(Some res) ->
match res.DocumentChanges with
| None -> failtest "No changes"
| Some result ->
@@ -51,9 +49,9 @@ let private sameProjectTests state =
(fun n ->
n.TextDocument.Uri.Contains "Program.fs"
&& n.Edits
- |> Seq.exists (fun r ->
- r.Range = { Start = { Line = 7; Character = 12 }
- End = { Line = 7; Character = 13 } }))
+ |> Seq.exists (fun r ->
+ r.Range = { Start = { Line = 7; Character = 12 }
+ End = { Line = 7; Character = 13 } }))
"Rename contains changes in Program.fs"
Expect.exists
@@ -61,9 +59,9 @@ let private sameProjectTests state =
(fun n ->
n.TextDocument.Uri.Contains "Test.fs"
&& n.Edits
- |> Seq.exists (fun r ->
- r.Range = { Start = { Line = 2; Character = 4 }
- End = { Line = 2; Character = 5 } }))
+ |> Seq.exists (fun r ->
+ r.Range = { Start = { Line = 2; Character = 4 }
+ End = { Line = 2; Character = 5 } }))
"Rename contains changes in Test.fs"
()
@@ -73,7 +71,7 @@ let private sameProjectTests state =
"Rename from definition within the same project with usages across different files"
(async {
- let! (programDoc, programDiags) = server |> Server.openDocument "Program.fs"
+ let! (_programDoc, programDiags) = server |> Server.openDocument "Program.fs"
let! (testDoc, testDiags) = server |> Server.openDocument "Test.fs"
Expect.isEmpty programDiags "There should be no errors in Program.fs"
@@ -90,7 +88,7 @@ let private sameProjectTests state =
match res with
| Result.Error e -> failtestf "Request failed: %A" e
| Result.Ok None -> failtest "Request none"
- | Result.Ok (Some res) ->
+ | Result.Ok(Some res) ->
match res.DocumentChanges with
| None -> failtest "No changes"
| Some result ->
@@ -102,9 +100,9 @@ let private sameProjectTests state =
(fun n ->
n.TextDocument.Uri.Contains "Program.fs"
&& n.Edits
- |> Seq.exists (fun r ->
- r.Range = { Start = { Line = 7; Character = 12 }
- End = { Line = 7; Character = 13 } }))
+ |> Seq.exists (fun r ->
+ r.Range = { Start = { Line = 7; Character = 12 }
+ End = { Line = 7; Character = 13 } }))
"Rename contains changes in Program.fs"
Expect.exists
@@ -112,9 +110,9 @@ let private sameProjectTests state =
(fun n ->
n.TextDocument.Uri.Contains "Test.fs"
&& n.Edits
- |> Seq.exists (fun r ->
- r.Range = { Start = { Line = 2; Character = 4 }
- End = { Line = 2; Character = 5 } }))
+ |> Seq.exists (fun r ->
+ r.Range = { Start = { Line = 2; Character = 4 }
+ End = { Line = 2; Character = 5 } }))
"Rename contains changes in Test.fs"
()
@@ -123,57 +121,56 @@ let private sameProjectTests state =
])
let private sameScriptTests state =
- serverTestList "Within same script file" state defaultConfigDto None (fun server -> [
- /// `expectedText = None` -> Rename not valid at location
- let checkRename' textWithCursor newName (expectedText: string option) = async {
- let (cursor, text) =
- textWithCursor
- |> Text.trimTripleQuotation
- |> Cursor.assertExtractPosition
-
- let! (doc, diags) = server |> Server.createUntitledDocument text
- use doc = doc
- Expect.isEmpty diags "There should be no diags"
-
- let p: RenameParams =
- {
- TextDocument = doc.TextDocumentIdentifier
- Position = cursor
- NewName = newName
- }
- let! res = doc.Server.Server.TextDocumentRename p
- match expectedText with
- | None ->
- // Note: `Error` instead of `Ok None` -> error message
- Expect.isError res "Rename should not be valid!"
- | Some expectedText ->
- let edits =
- match res with
- | Result.Error e -> failtestf "Request failed: %A" e
- | Result.Ok None -> failtest "Request none"
- | Result.Ok (Some { DocumentChanges = Some edits }) -> edits
- | Result.Ok edits -> failtestf "got some unexpected edits: %A" edits
+ serverTestList "Within same script file" state defaultConfigDto None (fun server ->
+ [ /// `expectedText = None` -> Rename not valid at location
+ let checkRename' textWithCursor newName (expectedText: string option) =
+ async {
+ let (cursor, text) =
+ textWithCursor |> Text.trimTripleQuotation |> Cursor.assertExtractPosition
- Expect.hasLength edits 1 "should have just one file worth of edits"
- let edit = edits.[0]
- Expect.equal edit.TextDocument.Uri doc.Uri "should be for this file"
- let edits = edit.Edits |> List.ofArray |> TextEdits.sortByRange
- let actual =
- text
- |> TextEdits.applyWithErrorCheck edits
- |> Flip.Expect.wantOk "TextEdits should be valid"
+ let! (doc, diags) = server |> Server.createUntitledDocument text
+ use doc = doc
+ Expect.isEmpty diags "There should be no diags"
- let expected = expectedText |> Text.trimTripleQuotation
+ let p: RenameParams =
+ { TextDocument = doc.TextDocumentIdentifier
+ Position = cursor
+ NewName = newName }
- Expect.equal actual expected "Text after TextEdits should be correct"
- }
- let checkRename textWithCursor newName expectedText =
- checkRename' textWithCursor newName (Some expectedText)
- let checkRenameNotValid textWithCursor newName =
- checkRename' textWithCursor newName None
+ let! res = doc.Server.Server.TextDocumentRename p
+
+ match expectedText with
+ | None ->
+ // Note: `Error` instead of `Ok None` -> error message
+ Expect.isError res "Rename should not be valid!"
+ | Some expectedText ->
+ let edits =
+ match res with
+ | Result.Error e -> failtestf "Request failed: %A" e
+ | Result.Ok None -> failtest "Request none"
+ | Result.Ok(Some { DocumentChanges = Some edits }) -> edits
+ | Result.Ok edits -> failtestf "got some unexpected edits: %A" edits
+
+ Expect.hasLength edits 1 "should have just one file worth of edits"
+ let edit = edits.[0]
+ Expect.equal edit.TextDocument.Uri doc.Uri "should be for this file"
+ let edits = edit.Edits |> List.ofArray |> TextEdits.sortByRange
+
+ let actual =
+ text
+ |> TextEdits.applyWithErrorCheck edits
+ |> Flip.Expect.wantOk "TextEdits should be valid"
+
+ let expected = expectedText |> Text.trimTripleQuotation
+
+ Expect.equal actual expected "Text after TextEdits should be correct"
+ }
- testCaseAsync "Rename from definition within script file" <|
- checkRename
+ let checkRename textWithCursor newName expectedText = checkRename' textWithCursor newName (Some expectedText)
+ let checkRenameNotValid textWithCursor newName = checkRename' textWithCursor newName None
+
+ testCaseAsync "Rename from definition within script file"
+ <| checkRename
"""
let $0initial () = printfn "hi"
@@ -185,8 +182,9 @@ let private sameScriptTests state =
afterwards ()
"""
- testCaseAsync "Rename from usage within script file" <|
- checkRename
+
+ testCaseAsync "Rename from usage within script file"
+ <| checkRename
"""
let initial () = printfn "hi"
@@ -198,8 +196,9 @@ let private sameScriptTests state =
afterwards ()
"""
- testCaseAsync "can add backticks to new name with space" <|
- checkRename
+
+ testCaseAsync "can add backticks to new name with space"
+ <| checkRename
"""
let initial () = printfn "hi"
@@ -211,8 +210,9 @@ let private sameScriptTests state =
``hello world`` ()
"""
- testCaseAsync "doesn't add additional backticks to new name with backticks" <|
- checkRename
+
+ testCaseAsync "doesn't add additional backticks to new name with backticks"
+ <| checkRename
"""
let initial () = printfn "hi"
@@ -225,8 +225,8 @@ let private sameScriptTests state =
``hello world`` ()
"""
- testCaseAsync "can rename operator to valid operator name" <|
- checkRename
+ testCaseAsync "can rename operator to valid operator name"
+ <| checkRename
"""
let (+$0++) a b = a + b
"""
@@ -234,15 +234,16 @@ let private sameScriptTests state =
"""
let (---) a b = a + b
"""
- testCaseAsync "cannot rename operator to invalid operator name" <|
- checkRenameNotValid
+
+ testCaseAsync "cannot rename operator to invalid operator name"
+ <| checkRenameNotValid
"""
let (+$0++) a b = a + b
"""
"foo"
- testCaseAsync "removes backticks for new name without backticks" <|
- checkRename
+ testCaseAsync "removes backticks for new name without backticks"
+ <| checkRename
"""
let ``my $0value`` = 42
@@ -253,8 +254,7 @@ let private sameScriptTests state =
let value = 42
let _ = value + 42
- """
- ])
+ """ ])
let private crossProjectTests state =
let server =
@@ -270,320 +270,338 @@ let private crossProjectTests state =
testSequenced
<| testList
- "Across projects"
- [ testCaseAsync
- "Rename from usage across projects"
- (async {
- let! (server, rootDir, events) = server
- let declarationFile = Path.Combine(rootDir, "LibA", "Library.fs")
- let usageFile = Path.Combine(rootDir, "LibB", "Library.fs")
-
- // open and parse the usage file
- let tdop: DidOpenTextDocumentParams = { TextDocument = loadDocument usageFile }
- do! server.TextDocumentDidOpen tdop
-
- do!
- waitForParseResultsForFile (Path.GetFileName usageFile) events
- |> AsyncResult.foldResult id (fun e -> failtestf "%A" e)
-
- // now, request renames
- let renameHelloUsageInUsageFile: RenameParams =
- { TextDocument = { Uri = normalizePathCasing usageFile }
- Position = { Line = 6; Character = 28 }
- NewName = "sup" }
-
- let! res = server.TextDocumentRename(renameHelloUsageInUsageFile)
+ "Across projects"
+ [ testCaseAsync
+ "Rename from usage across projects"
+ (async {
+ let! (server, rootDir, events) = server
+ let _declarationFile = Path.Combine(rootDir, "LibA", "Library.fs")
+ let usageFile = Path.Combine(rootDir, "LibB", "Library.fs")
- match res with
- | Result.Error e -> failtest $"Expected to get renames, but got error: {e.Message}"
- | Result.Ok None -> failtest $"Expected to get renames, but got none"
- | Result.Ok (Some { DocumentChanges = Some edits }) ->
- Expect.equal edits.Length 2 "Rename has the correct expected edits"
-
- Expect.exists
- edits
- (fun n ->
- n.TextDocument.Uri.Contains "LibA"
- && n.TextDocument.Uri.Contains "Library.fs"
- && n.Edits
- |> Seq.exists (fun r ->
- r.Range = { Start = { Line = 3; Character = 8 }
- End = { Line = 3; Character = 13 } }
- && r.NewText = "sup"))
- "Rename contains changes in LibA"
-
- Expect.exists
- edits
- (fun n ->
- n.TextDocument.Uri.Contains "LibB"
- && n.TextDocument.Uri.Contains "Library.fs"
- && n.Edits
- |> Seq.exists (fun r ->
- r.Range = { Start = { Line = 6; Character = 28 }
- End = { Line = 6; Character = 33 } }
- && r.NewText = "sup"))
- "Rename contains changes in LibB"
- | Result.Ok edits -> failtestf "got some unexpected edits: %A" edits
- })
- testCaseAsync
- "Rename where there are fully-qualified usages"
- (async {
- let! (server, rootDir, events) = server
- let declarationFile = Path.Combine(rootDir, "LibA", "Library.fs")
- let usageFile = Path.Combine(rootDir, "LibB", "Library.fs")
-
- // open and parse the usage file
- let tdop: DidOpenTextDocumentParams = { TextDocument = loadDocument usageFile }
- do! server.TextDocumentDidOpen tdop
-
- do!
- waitForParseResultsForFile (Path.GetFileName usageFile) events
- |> AsyncResult.foldResult id (fun e -> failtestf "%A" e)
-
- // now, request renames
- let renameHelloUsageInUsageFile: RenameParams =
- { TextDocument = { Uri = normalizePathCasing usageFile }
- Position = { Line = 9; Character = 37 } // in the 'yell' part of 'A.Say.yell'
- NewName = "sup" }
-
- let! res = server.TextDocumentRename(renameHelloUsageInUsageFile)
+ // open and parse the usage file
+ let tdop: DidOpenTextDocumentParams = { TextDocument = loadDocument usageFile }
+ do! server.TextDocumentDidOpen tdop
- match res with
- | Result.Error e -> failtest $"Expected to get renames, but got error: {e.Message}"
- | Result.Ok None -> failtest $"Expected to get renames, but got none"
- | Result.Ok (Some { DocumentChanges = Some edits }) ->
- Expect.equal edits.Length 2 "Rename has the correct expected edits"
-
- Expect.exists
- edits
- (fun n ->
- n.TextDocument.Uri.Contains "LibA"
- && n.TextDocument.Uri.Contains "Library.fs"
- && n.Edits
- |> Seq.exists (fun r ->
- r.Range = { Start = { Line = 6; Character = 8 }
- End = { Line = 6; Character = 12 } }
- && r.NewText = "sup"))
- $"Rename contains changes in LibA in the list %A{edits}"
-
- Expect.exists
- edits
- (fun n ->
- n.TextDocument.Uri.Contains "LibB"
- && n.TextDocument.Uri.Contains "Library.fs"
- && n.Edits
- |> Seq.exists (fun r ->
- r.Range = { Start = { Line = 9; Character = 37 }
- End = { Line = 9; Character = 41 } }
- && r.NewText = "sup"))
- $"Rename contains changes in LibB in the list %A{edits}"
- | Result.Ok edits -> failtestf "got some unexpected edits: %A" edits
- }) ]
-
-let private prepareRenameTests state = serverTestList "Prepare Rename tests" state defaultConfigDto None (fun server -> [
- let check shouldBeAbleToRename sourceWithCursor = async {
- let (cursor, text) =
- sourceWithCursor
- |> Text.trimTripleQuotation
- |> Cursor.assertExtractPosition
- let! (doc, diags) = server |> Server.createUntitledDocument text
-
- let p: PrepareRenameParams = {
- TextDocument = doc.TextDocumentIdentifier
- Position = cursor
- }
+ do!
+ waitForParseResultsForFile (Path.GetFileName usageFile) events
+ |> AsyncResult.foldResult id (fun e -> failtestf "%A" e)
- let! res = doc.Server.Server.TextDocumentPrepareRename p
-
- if shouldBeAbleToRename then
- res
- |> Flip.Expect.wantOk "Should be able to rename"
- |> Flip.Expect.isSome "Should be able to rename"
- else
- // Note: we always want `Error` instead of `Ok None` because of Error message
- Expect.isError res "Should not be able to rename"
- }
- let checkCanRename = check true
- let checkCannotRename = check false
-
- testCaseAsync "can rename variable at decl" <|
- checkCanRename
- """
+ // now, request renames
+ let renameHelloUsageInUsageFile: RenameParams =
+ { TextDocument = { Uri = normalizePathCasing usageFile }
+ Position = { Line = 6; Character = 28 }
+ NewName = "sup" }
+
+ let! res = server.TextDocumentRename(renameHelloUsageInUsageFile)
+
+ match res with
+ | Result.Error e -> failtest $"Expected to get renames, but got error: {e.Message}"
+ | Result.Ok None -> failtest $"Expected to get renames, but got none"
+ | Result.Ok(Some { DocumentChanges = Some edits }) ->
+ Expect.equal edits.Length 2 "Rename has the correct expected edits"
+
+ Expect.exists
+ edits
+ (fun n ->
+ n.TextDocument.Uri.Contains "LibA"
+ && n.TextDocument.Uri.Contains "Library.fs"
+ && n.Edits
+ |> Seq.exists (fun r ->
+ r.Range = { Start = { Line = 3; Character = 8 }
+ End = { Line = 3; Character = 13 } }
+ && r.NewText = "sup"))
+ "Rename contains changes in LibA"
+
+ Expect.exists
+ edits
+ (fun n ->
+ n.TextDocument.Uri.Contains "LibB"
+ && n.TextDocument.Uri.Contains "Library.fs"
+ && n.Edits
+ |> Seq.exists (fun r ->
+ r.Range = { Start = { Line = 6; Character = 28 }
+ End = { Line = 6; Character = 33 } }
+ && r.NewText = "sup"))
+ "Rename contains changes in LibB"
+ | Result.Ok edits -> failtestf "got some unexpected edits: %A" edits
+ })
+ testCaseAsync
+ "Rename where there are fully-qualified usages"
+ (async {
+ let! (server, rootDir, events) = server
+ let _declarationFile = Path.Combine(rootDir, "LibA", "Library.fs")
+ let usageFile = Path.Combine(rootDir, "LibB", "Library.fs")
+
+ // open and parse the usage file
+ let tdop: DidOpenTextDocumentParams = { TextDocument = loadDocument usageFile }
+ do! server.TextDocumentDidOpen tdop
+
+ do!
+ waitForParseResultsForFile (Path.GetFileName usageFile) events
+ |> AsyncResult.foldResult id (fun e -> failtestf "%A" e)
+
+ // now, request renames
+ let renameHelloUsageInUsageFile: RenameParams =
+ { TextDocument = { Uri = normalizePathCasing usageFile }
+ Position = { Line = 9; Character = 37 } // in the 'yell' part of 'A.Say.yell'
+ NewName = "sup" }
+
+ let! res = server.TextDocumentRename(renameHelloUsageInUsageFile)
+
+ match res with
+ | Result.Error e -> failtest $"Expected to get renames, but got error: {e.Message}"
+ | Result.Ok None -> failtest $"Expected to get renames, but got none"
+ | Result.Ok(Some { DocumentChanges = Some edits }) ->
+ Expect.equal edits.Length 2 "Rename has the correct expected edits"
+
+ Expect.exists
+ edits
+ (fun n ->
+ n.TextDocument.Uri.Contains "LibA"
+ && n.TextDocument.Uri.Contains "Library.fs"
+ && n.Edits
+ |> Seq.exists (fun r ->
+ r.Range = { Start = { Line = 6; Character = 8 }
+ End = { Line = 6; Character = 12 } }
+ && r.NewText = "sup"))
+ $"Rename contains changes in LibA in the list %A{edits}"
+
+ Expect.exists
+ edits
+ (fun n ->
+ n.TextDocument.Uri.Contains "LibB"
+ && n.TextDocument.Uri.Contains "Library.fs"
+ && n.Edits
+ |> Seq.exists (fun r ->
+ r.Range = { Start = { Line = 9; Character = 37 }
+ End = { Line = 9; Character = 41 } }
+ && r.NewText = "sup"))
+ $"Rename contains changes in LibB in the list %A{edits}"
+ | Result.Ok edits -> failtestf "got some unexpected edits: %A" edits
+ }) ]
+
+let private prepareRenameTests state =
+ serverTestList "Prepare Rename tests" state defaultConfigDto None (fun server ->
+ [ let check shouldBeAbleToRename sourceWithCursor =
+ async {
+ let (cursor, text) =
+ sourceWithCursor |> Text.trimTripleQuotation |> Cursor.assertExtractPosition
+
+ let! (doc, _diags) = server |> Server.createUntitledDocument text
+
+ let p: PrepareRenameParams =
+ { TextDocument = doc.TextDocumentIdentifier
+ Position = cursor }
+
+ let! res = doc.Server.Server.TextDocumentPrepareRename p
+
+ if shouldBeAbleToRename then
+ res
+ |> Flip.Expect.wantOk "Should be able to rename"
+ |> Flip.Expect.isSome "Should be able to rename"
+ else
+ // Note: we always want `Error` instead of `Ok None` because of Error message
+ Expect.isError res "Should not be able to rename"
+ }
+
+ let checkCanRename = check true
+ let checkCannotRename = check false
+
+ testCaseAsync "can rename variable at decl"
+ <| checkCanRename
+ """
let val$0ue = 42
let _ = value + 42
"""
- testCaseAsync "can rename variable at usage" <|
- checkCanRename
- """
+
+ testCaseAsync "can rename variable at usage"
+ <| checkCanRename
+ """
let value = 42
let _ = val$0ue + 42
"""
- testCaseAsync "can rename unnecessarily backticked variable at decl" <|
- checkCanRename
- """
+ testCaseAsync "can rename unnecessarily backticked variable at decl"
+ <| checkCanRename
+ """
let ``val$0ue`` = 42
let _ = value + 42
"""
- testCaseAsync "can rename unnecessarily backticked variable at usage" <|
- checkCanRename
- """
+
+ testCaseAsync "can rename unnecessarily backticked variable at usage"
+ <| checkCanRename
+ """
let value = 42
let _ = ``val$0ue`` + 42
"""
- testCaseAsync "can rename variable with required backticks at decl" <|
- checkCanRename
- """
+ testCaseAsync "can rename variable with required backticks at decl"
+ <| checkCanRename
+ """
let ``my v$0alue`` = 42
let _ = ``my value`` + 42
"""
- testCaseAsync "can rename variable with required backticks at usage" <|
- checkCanRename
- """
+
+ testCaseAsync "can rename variable with required backticks at usage"
+ <| checkCanRename
+ """
let ``my value`` = 42
let _ = ``my va$0lue`` + 42
"""
- testCaseAsync "can rename function at decl" <|
- checkCanRename
- """
+ testCaseAsync "can rename function at decl"
+ <| checkCanRename
+ """
let myFun$0ction value = value + 42
myFunction 42 |> ignore
"""
- testCaseAsync "can rename function at usage" <|
- checkCanRename
- """
+
+ testCaseAsync "can rename function at usage"
+ <| checkCanRename
+ """
let myFunction value = value + 42
myFun$0ction 42 |> ignore
"""
- testCaseAsync "can rename function parameter at decl" <|
- checkCanRename
- """
+
+ testCaseAsync "can rename function parameter at decl"
+ <| checkCanRename
+ """
let myFunction va$0lue = value + 42
myFunction 42 |> ignore
"""
- testCaseAsync "can rename function parameter at usage" <|
- checkCanRename
- """
+
+ testCaseAsync "can rename function parameter at usage"
+ <| checkCanRename
+ """
let myFunction va$0lue = value + 42
myFunction 42 |> ignore
"""
- testCaseAsync "Can rename Type at decl" <|
- checkCanRename
- """
+ testCaseAsync "Can rename Type at decl"
+ <| checkCanRename
+ """
type MyT$0ype () = class end
let v: MyType = MyType()
"""
- testCaseAsync "Can rename Type at instantiation usage" <|
- checkCanRename
- """
+
+ testCaseAsync "Can rename Type at instantiation usage"
+ <| checkCanRename
+ """
type MyType () = class end
let v: MyType = My$0Type()
"""
- testCaseAsync "Can rename Type at Type usage" <|
- checkCanRename
- """
+
+ testCaseAsync "Can rename Type at Type usage"
+ <| checkCanRename
+ """
type MyType () = class end
let v: MyT$0ype = MyType()
"""
- testCaseAsync "Can rename Method at decl" <|
- checkCanRename
- """
- type MyType =
+ testCaseAsync "Can rename Method at decl"
+ <| checkCanRename
+ """
+ type MyType =
static member DoS$0tuff () = ()
MyType.DoStuff ()
"""
- testCaseAsync "Can rename Method at usage" <|
- checkCanRename
- """
- type MyType =
+
+ testCaseAsync "Can rename Method at usage"
+ <| checkCanRename
+ """
+ type MyType =
static member DoStuff () = ()
MyType.DoS$0tuff ()
"""
-
- testCaseAsync "cannot rename Active Pattern at decl" <|
- checkCannotRename
- """
- let (|Ev$0en|Odd|) v =
+
+ testCaseAsync "cannot rename Active Pattern at decl"
+ <| checkCannotRename
+ """
+ let (|Ev$0en|Odd|) v =
if v % 2 = 0 then Even else Odd
let _ = (|Even|Odd|) 42
"""
- testCaseAsync "cannot rename Active Pattern at usage" <|
- checkCannotRename
- """
- let (|Even|Odd|) v =
+
+ testCaseAsync "cannot rename Active Pattern at usage"
+ <| checkCannotRename
+ """
+ let (|Even|Odd|) v =
if v % 2 = 0 then Even else Odd
let _ = (|Ev$0en|Odd|) 42
"""
- testCaseAsync "cannot rename Active Pattern Case at decl" <|
- checkCannotRename
- """
- let (|Ev$0en|Odd|) v =
+
+ testCaseAsync "cannot rename Active Pattern Case at decl"
+ <| checkCannotRename
+ """
+ let (|Ev$0en|Odd|) v =
if v % 2 = 0 then Ev$0en else Odd
match 42 with
| Even -> ()
| Odd -> ()
"""
- testCaseAsync "cannot rename Active Pattern Case at usage" <|
- checkCannotRename
- """
- let (|Even|Odd|) v =
+
+ testCaseAsync "cannot rename Active Pattern Case at usage"
+ <| checkCannotRename
+ """
+ let (|Even|Odd|) v =
if v % 2 = 0 then Ev$0en else Odd
match 42 with
| Ev$0en -> ()
| Odd -> ()
"""
- testCaseAsync "cannot rename external function" <|
- checkCannotRename
- """
+ testCaseAsync "cannot rename external function"
+ <| checkCannotRename
+ """
42 |> igno$0re
"""
- testCaseAsync "cannot rename external method" <|
- checkCannotRename
- """
+
+ testCaseAsync "cannot rename external method"
+ <| checkCannotRename
+ """
String.IsNullOr$0WhiteSpace "foo"
|> ignore
"""
- testCaseAsync "cannot rename number" <|
- checkCannotRename
- """
+
+ testCaseAsync "cannot rename number"
+ <| checkCannotRename
+ """
4$02 |> ignore
"""
- testCaseAsync "cannot rename string" <|
- checkCannotRename
- """
+
+ testCaseAsync "cannot rename string"
+ <| checkCannotRename
+ """
"hel$0lo world" |> ignore
"""
- testCaseAsync "cannot rename keyword" <|
- checkCannotRename
- """
+
+ testCaseAsync "cannot rename keyword"
+ <| checkCannotRename
+ """
l$0et foo = 42
"""
- testCaseAsync "cannot rename comment" <|
- checkCannotRename
- """
+
+ testCaseAsync "cannot rename comment"
+ <| checkCannotRename
+ """
/// So$0me value
let foo = 42
"""
- testCaseAsync "cannot rename space" <|
- checkCannotRename
- """
+
+ testCaseAsync "cannot rename space"
+ <| checkCannotRename
+ """
let foo = 42
- $0
+ $0
let bar = 42
- """
-])
+ """ ])
let tests state =
- testList "Rename Tests" [
- sameProjectTests state
- sameScriptTests state
- crossProjectTests state
+ testList
+ "Rename Tests"
+ [ sameProjectTests state
+ sameScriptTests state
+ crossProjectTests state
- prepareRenameTests state
- ]
+ prepareRenameTests state ]
diff --git a/test/FsAutoComplete.Tests.Lsp/ScriptTests.fs b/test/FsAutoComplete.Tests.Lsp/ScriptTests.fs
index 5e2b40527..cea2d7ebe 100644
--- a/test/FsAutoComplete.Tests.Lsp/ScriptTests.fs
+++ b/test/FsAutoComplete.Tests.Lsp/ScriptTests.fs
@@ -21,7 +21,11 @@ let scriptPreviewTests state =
let scriptPath = Path.Combine(path, "Script.fsx")
let! (server, events) =
- serverInitialize path { defaultConfigDto with FSIExtraParameters = Some [| "--langversion:preview" |] } state
+ serverInitialize
+ path
+ { defaultConfigDto with
+ FSIExtraParameters = Some [| "--langversion:preview" |] }
+ state
do! waitForWorkspaceFinishedParsing events
return server, events, scriptPath
@@ -39,7 +43,7 @@ let scriptPreviewTests state =
do! server.TextDocumentDidOpen { TextDocument = loadDocument scriptPath }
match! waitForParseResultsForFile "Script.fsx" events with
- | Ok () -> () // all good, no parsing/checking errors
+ | Ok() -> () // all good, no parsing/checking errors
| Core.Result.Error errors -> failwithf "Errors while parsing script %s: %A" scriptPath errors
}) ] ]
@@ -64,8 +68,7 @@ let scriptEvictionTests state =
(async {
let! server, events, scriptPath = server
- let openScript () =
- server.TextDocumentDidOpen { TextDocument = loadDocument scriptPath }
+ let openScript () = server.TextDocumentDidOpen { TextDocument = loadDocument scriptPath }
do! openScript ()
@@ -75,7 +78,10 @@ let scriptEvictionTests state =
let configChange: DidChangeConfigurationParams =
let config: FSharpConfigRequest =
- { FSharp = Some { defaultConfigDto with FSIExtraParameters = Some [| "--nowarn:760" |] } }
+ { FSharp =
+ Some
+ { defaultConfigDto with
+ FSIExtraParameters = Some [| "--nowarn:760" |] } }
{ Settings = Server.serialize config }
@@ -97,7 +103,7 @@ let dependencyManagerTests state =
let workingDir =
Path.Combine(__SOURCE_DIRECTORY__, "TestCases", "DependencyManagement")
- let dependencyManagerAssemblyDir =
+ let _dependencyManagerAssemblyDir =
Path.Combine(
__SOURCE_DIRECTORY__,
"..",
@@ -108,7 +114,8 @@ let dependencyManagerTests state =
)
let dependencyManagerEnabledConfig =
- { defaultConfigDto with FSIExtraParameters = Some [| "--langversion:preview" |] }
+ { defaultConfigDto with
+ FSIExtraParameters = Some [| "--langversion:preview" |] }
let! (server, events) = serverInitialize workingDir dependencyManagerEnabledConfig state
do! waitForWorkspaceFinishedParsing events
@@ -156,7 +163,8 @@ let scriptProjectOptionsCacheTests state =
Path.Combine(__SOURCE_DIRECTORY__, "TestCases", "ScriptProjectOptsCache")
let previewEnabledConfig =
- { defaultConfigDto with FSIExtraParameters = Some [| "--langversion:preview" |] }
+ { defaultConfigDto with
+ FSIExtraParameters = Some [| "--langversion:preview" |] }
let! (server, events) = serverInitialize workingDir previewEnabledConfig state
let options = ResizeArray()
@@ -177,7 +185,7 @@ let scriptProjectOptionsCacheTests state =
[ testCaseAsync
"reopening an unchanged script file should return same project options for file"
(async {
- let! server, events, workingDir, testFilePath, allOpts = server
+ let! server, _events, _workingDir, testFilePath, allOpts = server
do! server.TextDocumentDidOpen { TextDocument = loadDocument testFilePath }
do! Async.Sleep(TimeSpan.FromSeconds 3.)
do! server.TextDocumentDidOpen { TextDocument = loadDocument testFilePath }
diff --git a/test/FsAutoComplete.Tests.Lsp/SignatureHelpTests.fs b/test/FsAutoComplete.Tests.Lsp/SignatureHelpTests.fs
index 32174ed80..f4eca713c 100644
--- a/test/FsAutoComplete.Tests.Lsp/SignatureHelpTests.fs
+++ b/test/FsAutoComplete.Tests.Lsp/SignatureHelpTests.fs
@@ -16,7 +16,7 @@ type private TriggerType =
let private testSignatureHelp' (server: CachedServer) text pos triggerType checkResp =
async {
- let! (doc, diags) = server |> Server.createUntitledDocument text
+ let! (doc, _diags) = server |> Server.createUntitledDocument text
use doc = doc
let sigHelpRequest: SignatureHelpParams =
@@ -27,7 +27,7 @@ let private testSignatureHelp' (server: CachedServer) text pos triggerType check
{ TriggerKind =
match triggerType with
| Manual -> SignatureHelpTriggerKind.Invoked
- | Char c -> SignatureHelpTriggerKind.TriggerCharacter
+ | Char _c -> SignatureHelpTriggerKind.TriggerCharacter
TriggerCharacter =
match triggerType with
| Manual -> None
@@ -45,9 +45,7 @@ let private wantSignatureHelp = Flip.Expect.wantOk "unexpected request error"
let private testSignatureHelp (server: CachedServer) textWithCursor triggerType checkResp =
async {
let (pos, text) =
- textWithCursor
- |> Text.trimTripleQuotation
- |> Cursor.assertExtractPosition
+ textWithCursor |> Text.trimTripleQuotation |> Cursor.assertExtractPosition
let checkResp = wantSignatureHelp >> checkResp
return! testSignatureHelp' server text pos triggerType checkResp
@@ -148,17 +146,15 @@ let private overloadEdgeCasesTests server =
testCaseAsync $"Can get overloads at whitespace position {c - 37} of unattached parens"
<| testSignatureHelp'
- server
- text
- pos
- Manual
- (wantSignatureHelp
- >> fun resp ->
- Expect.isSome
- resp
- $"Should get some signature overloads at position %A{pos} on file Overloads.fsx"
-
- Expect.isNonEmpty resp.Value.Signatures "Should have some overloads") ]
+ server
+ text
+ pos
+ Manual
+ (wantSignatureHelp
+ >> fun resp ->
+ Expect.isSome resp $"Should get some signature overloads at position %A{pos} on file Overloads.fsx"
+
+ Expect.isNonEmpty resp.Value.Signatures "Should have some overloads") ]
testList
"attached parens"
[ let text = "let _____ = new System.IO.MemoryStream(42)"
@@ -168,17 +164,15 @@ let private overloadEdgeCasesTests server =
testCaseAsync $"Can get overloads at whitespace position {c - 39} of attached parens"
<| testSignatureHelp'
- server
- text
- pos
- Manual
- (wantSignatureHelp
- >> fun resp ->
- Expect.isSome
- resp
- $"Should get some signature overloads at position %A{pos} on file Overloads.fsx"
-
- Expect.isNonEmpty resp.Value.Signatures "Should have some overloads") ] ]
+ server
+ text
+ pos
+ Manual
+ (wantSignatureHelp
+ >> fun resp ->
+ Expect.isSome resp $"Should get some signature overloads at position %A{pos} on file Overloads.fsx"
+
+ Expect.isNonEmpty resp.Value.Signatures "Should have some overloads") ] ]
let issuesTests server =
testList
@@ -201,9 +195,7 @@ let issuesTests server =
"val count: x: int -> int"
"Should have no backticks because signatures are only ever rendered in `code` form")
testCaseAsync "issue #1040" // IndexOutOfRangeException
- <| testSignatureHelp server "().ToString(\n\n,$0\n)" Manual (fun sigs ->
- Expect.isSome sigs "Should have sigs"
- ) ]
+ <| testSignatureHelp server "().ToString(\n\n,$0\n)" Manual (fun sigs -> Expect.isSome sigs "Should have sigs") ]
let tests state =
serverTestList "signature help" state defaultConfigDto None (fun server ->
diff --git a/test/FsAutoComplete.Tests.Lsp/TestCases/Directory.Build.props b/test/FsAutoComplete.Tests.Lsp/TestCases/Directory.Build.props
new file mode 100644
index 000000000..e728bc6dc
--- /dev/null
+++ b/test/FsAutoComplete.Tests.Lsp/TestCases/Directory.Build.props
@@ -0,0 +1,5 @@
+
+
+ $(NoWarn);1182
+
+
diff --git a/test/FsAutoComplete.Tests.Lsp/UnsedDeclarationsTests.fs b/test/FsAutoComplete.Tests.Lsp/UnsedDeclarationsTests.fs
index a76e1de5c..7f9d5c383 100644
--- a/test/FsAutoComplete.Tests.Lsp/UnsedDeclarationsTests.fs
+++ b/test/FsAutoComplete.Tests.Lsp/UnsedDeclarationsTests.fs
@@ -1,4 +1,5 @@
module FsAutoComplete.Tests.UnusedDeclarationsTests
+
open Expecto
open FsAutoComplete
open Helpers
@@ -13,152 +14,166 @@ open Helpers.Expecto.ShadowedTimeouts
type private Expected =
| Used
| Unused
+
type private Doc =
| Untitled
- | Existing of path:string
-let private checkUsageAt
- server
- doc
- sourceWithoutCursor
- cursor
- expected
- = async {
+ | Existing of path: string
+
+let private checkUsageAt server doc sourceWithoutCursor cursor expected =
+ async {
let source = sourceWithoutCursor |> Text.trimTripleQuotation
+
let! (doc, diags) =
let getDoc =
match doc with
| Untitled -> Server.createUntitledDocument
| Existing path -> Server.openDocumentWithText path
+
server |> getDoc source
- use doc = doc
+
+ use _doc = doc
let diagsAtCursor =
- diags
- |> Array.filter (fun d -> d.Range |> Range.containsStrictly cursor)
+ diags |> Array.filter (fun d -> d.Range |> Range.containsStrictly cursor)
let isUnused (diag: Diagnostic) =
diag.Source = Some "FSAC"
- &&
- diag.Code = Some "FSAC0003"
- &&
- diag.Message = "This value is unused"
- &&
- diag.Tags |> Option.map (Array.contains DiagnosticTag.Unnecessary) |> Option.defaultValue false
+ && diag.Code = Some "FSAC0003"
+ && diag.Message = "This value is unused"
+ && diag.Tags
+ |> Option.map (Array.contains DiagnosticTag.Unnecessary)
+ |> Option.defaultValue false
let diag = diagsAtCursor |> Array.filter isUnused
+
match expected with
- | Unused ->
- Expect.hasLength diag 1 "There should be exactly one unused value diagnostic at cursor position"
- | Used ->
- Expect.hasLength diag 0 "There should be no unused value diagnostic at cursor position"
+ | Unused -> Expect.hasLength diag 1 "There should be exactly one unused value diagnostic at cursor position"
+ | Used -> Expect.hasLength diag 0 "There should be no unused value diagnostic at cursor position"
}
-let private checkUsage
- server
- doc
- sourceWithCursor
- expected
- = async {
- let (cursor, source) =
- sourceWithCursor
- |> Text.trimTripleQuotation
- |> Cursor.assertExtractPosition
+let private checkUsage server doc sourceWithCursor expected =
+ async {
+ let cursor, source =
+ sourceWithCursor |> Text.trimTripleQuotation |> Cursor.assertExtractPosition
+
do! checkUsageAt server doc source cursor expected
}
let private scriptTests state =
- let config = {
- defaultConfigDto with
- UnusedDeclarationsAnalyzer = Some true
- }
- serverTestList "script" state config None (fun server -> [
- testCaseAsync "unused variable" <|
- checkUsage server Untitled
+ let config =
+ { defaultConfigDto with
+ UnusedDeclarationsAnalyzer = Some true }
+
+ serverTestList "script" state config None (fun server ->
+ [ testCaseAsync "unused variable"
+ <| checkUsage
+ server
+ Untitled
"""
let $0alpha = 42
"""
Unused
- testCaseAsync "used variable" <|
- checkUsage server Untitled
+ testCaseAsync "used variable"
+ <| checkUsage
+ server
+ Untitled
"""
let $0alpha = 42
let _ = alpha
"""
Used
- testCaseAsync "unused tuple element variable" <|
- checkUsage server Untitled
+ testCaseAsync "unused tuple element variable"
+ <| checkUsage
+ server
+ Untitled
"""
let (alpha, $0beta) = (42, 42)
let _ = alpha
"""
Unused
- testCaseAsync "used tuple element variable" <|
- checkUsage server Untitled
+ testCaseAsync "used tuple element variable"
+ <| checkUsage
+ server
+ Untitled
"""
let ($0alpha, beta) = (42, 42)
let _ = alpha
"""
Used
- testCaseAsync "unused this" <|
- checkUsage server Untitled
+ testCaseAsync "unused this"
+ <| checkUsage
+ server
+ Untitled
"""
type T() =
member $0this.F _ = ()
"""
Unused
- testCaseAsync "used this" <|
- checkUsage server Untitled
+ testCaseAsync "used this"
+ <| checkUsage
+ server
+ Untitled
"""
type T() =
member $0this.F _ = this.GetType() |> ignore
"""
Used
- testCaseAsync "unused function variable" <|
- checkUsage server Untitled
+ testCaseAsync "unused function variable"
+ <| checkUsage
+ server
+ Untitled
"""
let $0f _ = ()
"""
Unused
- testCaseAsync "used function variable" <|
- checkUsage server Untitled
+ testCaseAsync "used function variable"
+ <| checkUsage
+ server
+ Untitled
"""
let $0f _ = ()
f ()
"""
- Used
- ])
+ Used ])
let private projectTests state =
let path = Path.Combine(__SOURCE_DIRECTORY__, "TestCases", "UnusedDeclarations")
let file = Existing "Library.fs"
- let config = {
- defaultConfigDto with
- UnusedDeclarationsAnalyzer = Some true
- }
- serverTestList "project" state config (Some path) (fun server -> [
- // Note: public variables aren't considered unused (-> can be used somewhere else)
+ let config =
+ { defaultConfigDto with
+ UnusedDeclarationsAnalyzer = Some true }
+
+ serverTestList "project" state config (Some path) (fun server ->
+ [
+ // Note: public variables aren't considered unused (-> can be used somewhere else)
- testCaseAsync "unused private variable" <|
- checkUsage server file
+ testCaseAsync "unused private variable"
+ <| checkUsage
+ server
+ file
"""
module Root
let private $0alpha = 42
"""
Unused
- testCaseAsync "unused public variable" <|
- checkUsage server file
+ testCaseAsync "unused public variable"
+ <| checkUsage
+ server
+ file
"""
module Root
let $0alpha = 42
"""
Used
- testCaseAsync "used private variable" <|
- checkUsage server file
+ testCaseAsync "used private variable"
+ <| checkUsage
+ server
+ file
"""
// module Root
@@ -167,10 +182,13 @@ let private projectTests state =
let _ = alpha
"""
Used
- testCaseAsync "unused private tuple element variable" <|
+ testCaseAsync "unused private tuple element variable"
+ <|
// Note: `let private (alpha, beta) =` is incorrect!
// must be: `let (private alpha, private beta) =`
- checkUsage server file
+ checkUsage
+ server
+ file
"""
module Root
@@ -178,8 +196,10 @@ let private projectTests state =
let _ = alpha
"""
Unused
- testCaseAsync "used private tuple element variable" <|
- checkUsage server file
+ testCaseAsync "used private tuple element variable"
+ <| checkUsage
+ server
+ file
"""
module Root
@@ -187,8 +207,10 @@ let private projectTests state =
let _ = alpha
"""
Used
- testCaseAsync "unused public tuple element variable" <|
- checkUsage server file
+ testCaseAsync "unused public tuple element variable"
+ <| checkUsage
+ server
+ file
"""
module Root
@@ -196,8 +218,10 @@ let private projectTests state =
let _ = alpha
"""
Used
- testCaseAsync "used public tuple element variable" <|
- checkUsage server file
+ testCaseAsync "used public tuple element variable"
+ <| checkUsage
+ server
+ file
"""
module Root
@@ -206,8 +230,10 @@ let private projectTests state =
"""
Used
- testCaseAsync "unused this" <|
- checkUsage server file
+ testCaseAsync "unused this"
+ <| checkUsage
+ server
+ file
"""
module Root
@@ -215,8 +241,10 @@ let private projectTests state =
member $0this.F _ = ()
"""
Unused
- testCaseAsync "used this" <|
- checkUsage server file
+ testCaseAsync "used this"
+ <| checkUsage
+ server
+ file
"""
module Root
@@ -226,16 +254,20 @@ let private projectTests state =
Used
- testCaseAsync "unused private function variable" <|
- checkUsage server file
+ testCaseAsync "unused private function variable"
+ <| checkUsage
+ server
+ file
"""
module Root
let private $0f _ = ()
"""
Unused
- testCaseAsync "used private function variable" <|
- checkUsage server file
+ testCaseAsync "used private function variable"
+ <| checkUsage
+ server
+ file
"""
module Root
@@ -243,8 +275,10 @@ let private projectTests state =
f ()
"""
Used
- testCaseAsync "unused public function variable" <|
- checkUsage server file
+ testCaseAsync "unused public function variable"
+ <| checkUsage
+ server
+ file
"""
module Root
@@ -252,11 +286,14 @@ let private projectTests state =
"""
Used
- // https://github.com/fsharp/FsAutoComplete/issues/832
- testList "issue #832" [
- // `$P`: used (or public -> not marked unused)
- // `$U`: unused
- let source = """
+ // https://github.com/fsharp/FsAutoComplete/issues/832
+ testList
+ "issue #832"
+ [
+ // `$P`: used (or public -> not marked unused)
+ // `$U`: unused
+ let source =
+ """
module External =
let $Pa = 123
@@ -276,25 +313,19 @@ module private Internal =
let private $Uy _ = ()
"""
- let (source, cursors) =
- source
- |> Text.trimTripleQuotation
- |> Cursors.extractWith [| "$P"; "$U" |]
-
- for (marker, pos) in cursors do
- let expected = if marker = "$P" then Used else Unused
- let title = $"%A{expected} at %s{pos.DebuggerDisplay}"
- testCaseAsync title <|
- checkUsageAt server file
- source pos
- expected
- ]
- ])
+ let (source, cursors) =
+ source |> Text.trimTripleQuotation |> Cursors.extractWith [| "$P"; "$U" |]
+
+ for (marker, pos) in cursors do
+ let expected = if marker = "$P" then Used else Unused
+ let title = $"%A{expected} at %s{pos.DebuggerDisplay}"
+ testCaseAsync title <| checkUsageAt server file source pos expected ] ])
let tests state =
- testList ("Unused Declarations") [
- // Note: difference between Script & Project:
- // Public in Script can be unused, public in Project cannot
- scriptTests state
- projectTests state
- ]
+ testList
+ ("Unused Declarations")
+ [
+ // Note: difference between Script & Project:
+ // Public in Script can be unused, public in Project cannot
+ scriptTests state
+ projectTests state ]
diff --git a/test/FsAutoComplete.Tests.Lsp/Utils/CursorbasedTests.fs b/test/FsAutoComplete.Tests.Lsp/Utils/CursorbasedTests.fs
index e861e8746..1c99527f4 100644
--- a/test/FsAutoComplete.Tests.Lsp/Utils/CursorbasedTests.fs
+++ b/test/FsAutoComplete.Tests.Lsp/Utils/CursorbasedTests.fs
@@ -1,4 +1,5 @@
module Utils.CursorbasedTests
+
open Expecto
open Expecto.Diff
open Ionide.LanguageServerProtocol.Types
@@ -14,11 +15,10 @@ open Ionide.ProjInfo.Logging
/// * `check`: Check to use inside a `testCaseAsync`. Not a Test itself!
/// * `test`: Returns Expecto Test. Usually combines multiple tests (like: test all positions).
module CodeFix =
- let private logger = LogProvider.getLoggerByName "CursorbasedTests.CodeFix"
+ let private _logger = LogProvider.getLoggerByName "CursorbasedTests.CodeFix"
let private diagnosticsIn (range: Range) (diags: Diagnostic[]) =
- diags
- |> Array.filter (fun diag -> range |> Range.overlapsStrictly diag.Range)
+ diags |> Array.filter (fun diag -> range |> Range.overlapsStrictly diag.Range)
/// Note: Return should be just ONE `CodeAction` (for Applicable) or ZERO `CodeAction` (for Not Applicable).
/// But actual return type is an array of `CodeAction`s:
@@ -26,6 +26,7 @@ module CodeFix =
/// * Returning `CodeAction option` would mean different filters for `check` (exactly one fix) and `checkNotApplicable` (exactly zero fix).
/// Both error with multiple matching fixes!
type ChooseFix = CodeAction[] -> CodeAction[]
+
type ExpectedResult =
| NotApplicable
| Applicable
@@ -37,21 +38,22 @@ module CodeFix =
(validateDiagnostics: Diagnostic[] -> unit)
(chooseFix: ChooseFix)
(expected: ExpectedResult)
- = async {
+ =
+ async {
// filter to only diags matching the cursor range
let diags = diagnostics |> diagnosticsIn cursorRange
validateDiagnostics diags
// get code fixes from server
let! res = doc |> Document.codeActionAt diags cursorRange
+
let allCodeActions =
match res, expected with
| None, (Applicable | After _) ->
- // error here instead of later to return error noting it was `None` instead of empty CodeAction array
- Expect.isSome res "No CodeAction returned (`None`)"
- failwith "unreachable"
- | None, NotApplicable ->
- [||]
- | Some (Helpers.CodeActions actions), _ -> actions
+ // error here instead of later to return error noting it was `None` instead of empty CodeAction array
+ Expect.isSome res "No CodeAction returned (`None`)"
+ failwith "unreachable"
+ | None, NotApplicable -> [||]
+ | Some(Helpers.CodeActions actions), _ -> actions
| Some _, _ -> failwith "Expected some code actions from the server"
@@ -71,42 +73,41 @@ module CodeFix =
match expected with
| NotApplicable ->
// Expect.isEmpty codeActions "There should be no applicable code action" // doesn't show `actual` when not empty
- if not (codeActions |> Array.isEmpty) then
+ if not (codeActions |> Array.isEmpty) then
failtestf "There should be no applicable code action, but was %A" codeActions
- | Applicable ->
- codeActions
- |> getCodeAction
- |> ignore
- //ENHANCEMENT?: apply edits to check valid?
+ | Applicable -> codeActions |> getCodeAction |> ignore
+ //ENHANCEMENT?: apply edits to check valid?
| After expected ->
- let codeAction = codeActions |> getCodeAction
+ let codeAction = codeActions |> getCodeAction
+
+ /// Error message is appended by selected `codeAction`
+ let inline failCodeFixTest (msg: string) =
+ let msg =
+ if
+ System.String.IsNullOrWhiteSpace msg
+ || System.Char.IsPunctuation(msg, msg.Length - 1)
+ then
+ msg
+ else
+ msg + "."
- /// Error message is appended by selected `codeAction`
- let inline failCodeFixTest (msg: string) =
- let msg =
- if System.String.IsNullOrWhiteSpace msg || System.Char.IsPunctuation(msg, msg.Length-1) then
- msg
- else
- msg + "."
- failtest $"{msg} CodeAction was: %A{codeAction}"
-
- // only text edits supported
- if codeAction.Command |> Option.isSome then
- failCodeFixTest "Code action contains commands. Commands aren't supported in this test!"
-
- let edits =
- codeAction.Edit
- |> Option.defaultWith (fun _ -> failCodeFixTest "Code action doesn't contain any edits")
- |> WorkspaceEdit.tryExtractTextEditsInSingleFile doc.VersionedTextDocumentIdentifier
- |> Result.valueOr failCodeFixTest
-
- // apply fix
- let actual =
- beforeWithoutCursor
- |> TextEdits.apply edits
- |> Result.valueOr failCodeFixTest
-
- Expecto.Diff.equals actual expected "Incorrect text after applying the chosen code action"
+ failtest $"{msg} CodeAction was: %A{codeAction}"
+
+ // only text edits supported
+ if codeAction.Command |> Option.isSome then
+ failCodeFixTest "Code action contains commands. Commands aren't supported in this test!"
+
+ let edits =
+ codeAction.Edit
+ |> Option.defaultWith (fun _ -> failCodeFixTest "Code action doesn't contain any edits")
+ |> WorkspaceEdit.tryExtractTextEditsInSingleFile doc.VersionedTextDocumentIdentifier
+ |> Result.valueOr failCodeFixTest
+
+ // apply fix
+ let actual =
+ beforeWithoutCursor |> TextEdits.apply edits |> Result.valueOr failCodeFixTest
+
+ Expecto.Diff.equals actual expected "Incorrect text after applying the chosen code action"
}
let private checkFix
@@ -115,16 +116,15 @@ module CodeFix =
(validateDiagnostics: Diagnostic[] -> unit)
(chooseFix: ChooseFix)
(expected: unit -> ExpectedResult)
- = async {
+ =
+ async {
let (range, text) =
- beforeWithCursor
- |> Text.trimTripleQuotation
- |> Cursor.assertExtractRange
+ beforeWithCursor |> Text.trimTripleQuotation |> Cursor.assertExtractRange
// load text file
let! (doc, diags) = server |> Server.createUntitledDocument text
use doc = doc // ensure doc gets closed (disposed) after test
- do! checkFixAt (doc, diags) (text, range) validateDiagnostics chooseFix (expected())
+ do! checkFixAt (doc, diags) (text, range) validateDiagnostics chooseFix (expected ())
}
/// Checks a CodeFix (CodeAction) for validity.
@@ -158,50 +158,18 @@ module CodeFix =
/// Linebreaks from edits in selected CodeFix are all transformed to just `\n`
/// -> CodeFix can use `\r` and `\r\n`
/// If you want to validate Line Endings of CodeFix, add a validation step to your `chooseFix`
- let check
- server
- beforeWithCursor
- validateDiagnostics
- chooseFix
- expected
- =
- checkFix
- server
- beforeWithCursor
- validateDiagnostics
- chooseFix
- (fun () -> After (expected |> Text.trimTripleQuotation))
+ let check server beforeWithCursor validateDiagnostics chooseFix expected =
+ checkFix server beforeWithCursor validateDiagnostics chooseFix (fun () ->
+ After(expected |> Text.trimTripleQuotation))
/// Note: Doesn't apply Fix! Just checks its existence!
- let checkApplicable
- server
- beforeWithCursor
- validateDiagnostics
- chooseFix
- =
- checkFix
- server
- beforeWithCursor
- validateDiagnostics
- chooseFix
- (fun () -> Applicable)
-
- let checkNotApplicable
- server
- beforeWithCursor
- validateDiagnostics
- chooseFix
- =
- checkFix
- server
- beforeWithCursor
- validateDiagnostics
- chooseFix
- (fun () -> NotApplicable)
-
- let matching cond (fixes: CodeAction array) =
- fixes
- |> Array.filter cond
+ let checkApplicable server beforeWithCursor validateDiagnostics chooseFix =
+ checkFix server beforeWithCursor validateDiagnostics chooseFix (fun () -> Applicable)
+
+ let checkNotApplicable server beforeWithCursor validateDiagnostics chooseFix =
+ checkFix server beforeWithCursor validateDiagnostics chooseFix (fun () -> NotApplicable)
+
+ let matching cond (fixes: CodeAction array) = fixes |> Array.filter cond
let withTitle title = matching (fun f -> f.Title = title)
let ofKind kind = matching (fun f -> f.Kind = Some kind)
@@ -225,18 +193,21 @@ module CodeFix =
(expected: ExpectedResult)
=
Expect.isNonEmpty cursorRanges "No Range(s) specified"
- ServerTests.documentTestList name server (Server.createUntitledDocument beforeWithoutCursor) (fun doc -> [
- for (i, range) in cursorRanges |> Seq.indexed do
- let pos =
- if range |> Range.isPosition then
- range.Start.DebuggerDisplay
- else
- $"{range.Start.DebuggerDisplay}..{range.End.DebuggerDisplay}"
- testCaseAsync $"Cursor {i} at {pos}" (async {
- let! (doc, diags) = doc
- do! checkFixAt (doc, diags) (beforeWithoutCursor, range) validateDiagnostics chooseFix expected
- })
- ])
+
+ ServerTests.documentTestList name server (Server.createUntitledDocument beforeWithoutCursor) (fun doc ->
+ [ for (i, range) in cursorRanges |> Seq.indexed do
+ let pos =
+ if range |> Range.isPosition then
+ range.Start.DebuggerDisplay
+ else
+ $"{range.Start.DebuggerDisplay}..{range.End.DebuggerDisplay}"
+
+ testCaseAsync
+ $"Cursor {i} at {pos}"
+ (async {
+ let! (doc, diags) = doc
+ do! checkFixAt (doc, diags) (beforeWithoutCursor, range) validateDiagnostics chooseFix expected
+ }) ])
/// One test for each Cursor.
///
@@ -250,51 +221,18 @@ module CodeFix =
(chooseFix: ChooseFix)
(expected: unit -> ExpectedResult)
=
- let (beforeWithoutCursor, poss) = beforeWithCursors |> Text.trimTripleQuotation |> Cursors.extract
+ let (beforeWithoutCursor, poss) =
+ beforeWithCursors |> Text.trimTripleQuotation |> Cursors.extract
+
let ranges = poss |> List.map (fun p -> { Start = p; End = p })
- checkFixAll name server beforeWithoutCursor ranges validateDiagnostics chooseFix (expected())
-
- let testAllPositions
- name
- server
- beforeWithCursors
- validateDiagnostics
- chooseFix
- expected
- =
- Test.checkAllPositions
- name
- server
- beforeWithCursors
- validateDiagnostics
- chooseFix
- (fun () -> After (expected |> Text.trimTripleQuotation))
-
- let testApplicableAllPositions
- name
- server
- beforeWithCursors
- validateDiagnostics
- chooseFix
- =
- Test.checkAllPositions
- name
- server
- beforeWithCursors
- validateDiagnostics
- chooseFix
- (fun () -> Applicable)
- let testNotApplicableAllPositions
- name
- server
- beforeWithCursors
- validateDiagnostics
- chooseFix
- =
- Test.checkAllPositions
- name
- server
- beforeWithCursors
- validateDiagnostics
- chooseFix
- (fun () -> NotApplicable)
+ checkFixAll name server beforeWithoutCursor ranges validateDiagnostics chooseFix (expected ())
+
+ let testAllPositions name server beforeWithCursors validateDiagnostics chooseFix expected =
+ Test.checkAllPositions name server beforeWithCursors validateDiagnostics chooseFix (fun () ->
+ After(expected |> Text.trimTripleQuotation))
+
+ let testApplicableAllPositions name server beforeWithCursors validateDiagnostics chooseFix =
+ Test.checkAllPositions name server beforeWithCursors validateDiagnostics chooseFix (fun () -> Applicable)
+
+ let testNotApplicableAllPositions name server beforeWithCursors validateDiagnostics chooseFix =
+ Test.checkAllPositions name server beforeWithCursors validateDiagnostics chooseFix (fun () -> NotApplicable)
diff --git a/test/FsAutoComplete.Tests.Lsp/Utils/Server.Tests.fs b/test/FsAutoComplete.Tests.Lsp/Utils/Server.Tests.fs
index 6281501f6..9fcd9fc98 100644
--- a/test/FsAutoComplete.Tests.Lsp/Utils/Server.Tests.fs
+++ b/test/FsAutoComplete.Tests.Lsp/Utils/Server.Tests.fs
@@ -1,4 +1,5 @@
module Utils.Tests.Server
+
open System
open Expecto
open Helpers
@@ -13,470 +14,645 @@ open Utils.Utils
open FsToolkit.ErrorHandling
open FSharpx.Control
-let tests state = testList (nameof(Server)) [
- testList "no root path" [
- testList "can get diagnostics" [
- let config =
- { defaultConfigDto with
- UnusedOpensAnalyzer = Some false
- UnusedDeclarationsAnalyzer = Some false
- SimplifyNameAnalyzer = Some false
- }
- serverTestList "no analyzers" state config None (fun server -> [
- testCaseAsync "can get nothing wrong" (async {
- let! (doc, diags) = server |> Server.createUntitledDocument ""
- use doc = doc
- Expect.isEmpty diags "There should be no diagnostics"
-
- for i in 1..5 do
- let! diags = doc |> Document.changeTextTo (string i)
- Expect.isEmpty diags "There should be no diagnostics"
- })
- testCaseAsync "can get single error" (async {
- let! (doc, diags) = server |> Server.createUntitledDocument "let foo = notdefined"
- use doc = doc
- Expect.hasLength diags 1 "There should be 1 error"
- Expect.exists diags (fun d -> d.Message.Contains "notdefined") ""
- let! diags = doc |> Document.changeTextTo "let bar = doesnotexist"
- Expect.hasLength diags 1 "There should be 1 error"
- Expect.exists diags (fun d -> d.Message.Contains "doesnotexist") ""
- let! diags = doc |> Document.changeTextTo "let baz = nope"
- Expect.hasLength diags 1 "There should be 1 error"
- Expect.exists diags (fun d -> d.Message.Contains "nope") ""
- })
- testCaseAsync "can get multiple errors" (async {
- let source = "let foo = {0}\nlet bar = {1}\nlet baz = {2}"
- let names = [|"notdefined"; "doesnotexist"; "nope"|]
- let fnames i = names |> Array.map (fun n -> sprintf "%s%i" n i)
- let fsource i = String.Format(source, fnames i |> Seq.cast |> Seq.toArray)
-
- let! (doc, diags) = server |> Server.createUntitledDocument (fsource 0)
- use doc = doc
- Expect.hasLength diags (names.Length) ""
- for name in fnames 0 do
- Expect.exists diags (fun d -> d.Message.Contains name) ""
-
- for i in 1..2 do
- let! diags = doc |> Document.changeTextTo (fsource i)
- Expect.hasLength diags (names.Length) ""
- for name in fnames i do
- Expect.exists diags (fun d -> d.Message.Contains name) ""
- })
- ])
-
- let config =
- { defaultConfigDto with
- UnusedOpensAnalyzer = Some false
- UnusedDeclarationsAnalyzer = Some true
- SimplifyNameAnalyzer = Some false
- }
- serverTestList "just unused decl analyzer" state config None (fun server -> [
- testCaseAsync "can get nothing wrong" <| (async {
- let! (doc, diags) = server |> Server.createUntitledDocument ""
- use doc = doc
- Expect.isEmpty diags "There should be no diagnostics"
-
- for i in 1..5 do
- let! diags = doc |> Document.changeTextTo (string i)
- Expect.isEmpty diags "There should be no diagnostics"
- })
- testCaseAsync "can get diags for single line" (async {
- let! (doc, diags) = server |> Server.createUntitledDocument "let foo = notdefined"
- use doc = doc
- Expect.hasLength diags 2 ""
- Expect.exists diags (fun d -> d.Message.Contains "notdefined") ""
- Expect.exists diags (fun d -> d.Message = "This value is unused") ""
- let! diags = doc |> Document.changeTextTo "let bar = doesnotexist"
- Expect.hasLength diags 2 ""
- Expect.exists diags (fun d -> d.Message.Contains "doesnotexist") ""
- Expect.exists diags (fun d -> d.Message = "This value is unused") ""
- let! diags = doc |> Document.changeTextTo "let baz = nope"
- Expect.hasLength diags 2 ""
- Expect.exists diags (fun d -> d.Message.Contains "nope") ""
- Expect.exists diags (fun d -> d.Message = "This value is unused") ""
- })
- testCaseAsync "can get diags for multiple lines" (async {
- let nVars = 3
- let values i = Array.init nVars (sprintf "someValue%i%i" i)
- let source i =
- values i
- |> Seq.mapi (sprintf "let var%i = %s")
- |> String.concat "\n"
-
- let! (doc, diags) = server |> Server.createUntitledDocument (source 0)
- use doc = doc
- Expect.hasLength diags (nVars * 2) ""
- values 0
- |> Array.iteri (fun i name ->
- Expect.exists diags (fun d -> d.Message.Contains name) $"No diags with name {name}"
- Expect.exists diags (fun d -> d.Message = "This value is unused" && d.Range.Start.Line = i) $"No unused value error in line {i}"
- )
-
- for i in 1..2 do
- let! diags = doc |> Document.changeTextTo (source i)
- Expect.hasLength diags (nVars * 2) ""
- values i
- |> Array.iteri (fun i name ->
- Expect.exists diags (fun d -> d.Message.Contains name) $"No diags with name {name}"
- Expect.exists diags (fun d -> d.Message = "This value is unused" && d.Range.Start.Line = i) $"No unused value error in line {i}"
- )
- })
- ])
-
- let config =
- { defaultConfigDto with
- UnusedOpensAnalyzer = Some true
- UnusedDeclarationsAnalyzer = Some true
- SimplifyNameAnalyzer = Some true
- }
- serverTestList "three analyzers" state config None (fun server -> [
- testCaseAsync "can get nothing wrong" (async {
- let! (doc, diags) = server |> Server.createUntitledDocument ""
- use doc = doc
- Expect.isEmpty diags "There should be no diagnostics"
-
- for i in 1..5 do
- let! diags = doc |> Document.changeTextTo (string i)
- Expect.isEmpty diags "There should be no diagnostics"
- })
- testCaseAsync "can get all diags" (async {
- let source = "open System\nlet foo = bar\nSystem.String.Empty |> ignore"
- let! (doc, diags) = server |> Server.createUntitledDocument source
- use doc = doc
-
- Expect.hasLength diags 4 ""
- Expect.exists diags (fun d -> d.Message = "Unused open statement" && d.Range.Start.Line = 0) ""
- Expect.exists diags (fun d -> d.Message = "This value is unused" && d.Range.Start.Line = 1) ""
- Expect.exists diags (fun d -> d.Message.Contains "bar" && d.Range.Start.Line = 1) ""
- Expect.exists diags (fun d -> d.Message = "This qualifier is redundant" && d.Range.Start.Line = 2) ""
-
-
- let source = "open System.Collections\nlet baz = foo\nSystem.Collections.Generic.List() |> ignore"
- let! diags = doc |> Document.changeTextTo source
-
- Expect.hasLength diags 4 ""
- Expect.exists diags (fun d -> d.Message = "Unused open statement" && d.Range.Start.Line = 0) ""
- Expect.exists diags (fun d -> d.Message = "This value is unused" && d.Range.Start.Line = 1) ""
- Expect.exists diags (fun d -> d.Message.Contains "foo" && d.Range.Start.Line = 1) ""
- Expect.exists diags (fun d -> d.Message = "This qualifier is redundant" && d.Range.Start.Line = 2) ""
-
-
- let source = "open System.Diagnostics\nlet bar = baz\nSystem.Diagnostics.Debugger.IsAttached"
- let! diags = doc |> Document.changeTextTo source
-
- Expect.hasLength diags 4 ""
- Expect.exists diags (fun d -> d.Message = "Unused open statement" && d.Range.Start.Line = 0) ""
- Expect.exists diags (fun d -> d.Message = "This value is unused" && d.Range.Start.Line = 1) ""
- Expect.exists diags (fun d -> d.Message.Contains "baz" && d.Range.Start.Line = 1) ""
- Expect.exists diags (fun d -> d.Message = "This qualifier is redundant" && d.Range.Start.Line = 2) ""
- })
- ])
- ]
-
- testList "untitled document" [
- serverTestList "untitled counter in server for createUntitledDocument" state defaultConfigDto None (fun server -> [
- testCaseAsync "creating document increases untitled counter" (async {
- let! actualServer = server
- let preCounter = actualServer.UntitledCounter
- let! (doc, _) = server |> Server.createUntitledDocument ""
- use doc = doc
- let postCounter = actualServer.UntitledCounter
-
- Expect.isGreaterThan postCounter preCounter "Untitled Counter should increase"
- })
- testCaseAsync "creating multiple documents increases untitled counter" (async {
- let getCounter server = server |> Async.map (fun s -> s.UntitledCounter)
-
- let! preCounter = getCounter server
- let mutable preCounter = preCounter
- for i in 1..5 do
- let! (doc, _) = server |> Server.createUntitledDocument ""
- use doc = doc
- let! postCounter = getCounter server
- Expect.isGreaterThan postCounter preCounter "Untitled Counter should increase"
- preCounter <- postCounter
- })
- ])
-
- serverTestList "document version" state defaultConfigDto None (fun server -> [
- testCaseAsync "changing document text increases document version" (async {
- let! (doc, _) = server |> Server.createUntitledDocument ""
- let preVersion = doc.Version
- let! _ = doc |> Document.changeTextTo "42"
- let postVersion = doc.Version
-
- Expect.isGreaterThan postVersion preVersion "Document Version should increase"
- })
- testCaseAsync "changing document text multiple times should always increase document version" (async {
- let! (doc, _) = server |> Server.createUntitledDocument ""
- let mutable preVersion = doc.Version
- for _ in 1..5 do
- let! _ = doc |> Document.changeTextTo ""
- let postVersion = doc.Version
- Expect.isGreaterThan postVersion preVersion "Document Version should increase"
- preVersion <- postVersion
- })
- ])
- ]
- ]
-
- testList "with root path" [
- let inTestCases name =
- System.IO.Path.Combine(__SOURCE_DIRECTORY__, "..", "TestCases", "ServerTests", name)
- |> Some
-
- let noAnalyzersConfig =
- { defaultConfigDto with
- UnusedOpensAnalyzer = Some false
- UnusedDeclarationsAnalyzer = Some false
- SimplifyNameAnalyzer = Some false
- }
- let allAnalyzersConfig =
- { defaultConfigDto with
- UnusedOpensAnalyzer = Some true
- UnusedDeclarationsAnalyzer = Some true
- SimplifyNameAnalyzer = Some true
- }
- serverTestList "dir with just a script and no analyzers" state noAnalyzersConfig (inTestCases "JustScript") (fun server -> [
- testCaseAsync "can load script file" (async {
- let! (doc, diags) = server |> Server.openDocument "Script.fsx"
- use doc = doc
-
- Expect.hasLength diags 1 "Should be one diagnostics"
- let diag = diags |> Array.head
- Expect.stringContains diag.Message "The value or constructor 'bar' is not defined." "Should be not defined error"
- Expect.equal diag.Range.Start.Line 0 "Error should be in line 1"
- })
- testCaseAsync "can load script file again" (async {
- let! (doc, diags) = server |> Server.openDocument "Script.fsx"
- use doc = doc
-
- Expect.hasLength diags 1 "Should be one diagnostics"
- let diag = diags |> Array.head
- Expect.stringContains diag.Message "The value or constructor 'bar' is not defined." "Should be not defined error"
- Expect.equal diag.Range.Start.Line 0 "Error should be in line 1"
- })
- ])
- serverTestList "dir with just a script and all anaylzers" state allAnalyzersConfig (inTestCases "JustScript") (fun server -> [
- testCaseAsync "can load script file" (async {
- let! (doc, diags) = server |> Server.openDocument "Script.fsx"
- use doc = doc
-
- Expect.exists diags (fun diag -> diag.Message.Contains "The value or constructor 'bar' is not defined." && diag.Range.Start.Line = 0) "Should be not defined error"
- Expect.exists diags (fun diag -> diag.Message.Contains "This value is unused" && diag.Range.Start.Line = 0) "Should be unused value"
- })
- testCaseAsync "can load script file again" (async {
- let! (doc, diags) = server |> Server.openDocument "Script.fsx"
- use doc = doc
-
- Expect.exists diags (fun diag -> diag.Message.Contains "The value or constructor 'bar' is not defined." && diag.Range.Start.Line = 0) "Should be not defined error"
- Expect.exists diags (fun diag -> diag.Message.Contains "This value is unused" && diag.Range.Start.Line = 0) "Should be unused value"
- })
- ])
-
- testSequenced <| testList "contesting" [
- let projectDir = inTestCases "Project"
- serverTestList "dir with project and no analyzers" state noAnalyzersConfig projectDir (fun server -> [
- testCaseAsync "can load file in project" (async {
- let! (doc, diags) = server |> Server.openDocument "Other.fs"
- use doc = doc
-
- Expect.hasLength diags 1 "Should be one diagnostics"
- let diag = diags |> Array.head
- Expect.stringContains diag.Message "The value or constructor 'otherBar' is not defined." "Should be not defined error"
- Expect.equal diag.Range.Start.Line 5 "Error should be in line 6"
- })
- testCaseAsync "can load file in project again" (async {
- let! (doc, diags) = server |> Server.openDocument "Other.fs"
- use doc = doc
-
- Expect.hasLength diags 1 "Should be one diagnostics"
- let diag = diags |> Array.head
- Expect.stringContains diag.Message "The value or constructor 'otherBar' is not defined." "Should be not defined error"
- Expect.equal diag.Range.Start.Line 5 "Error should be in line 6"
- })
- testCaseAsync "can load other file in project" (async {
- let! (doc, diags) = server |> Server.openDocument "Program.fs"
- use doc = doc
-
- Expect.hasLength diags 1 "Should be one diagnostics"
- let diag = diags |> Array.head
- Expect.stringContains diag.Message "The value or constructor 'programBar' is not defined." "Should be not defined error"
- Expect.equal diag.Range.Start.Line 4 "Error should be in line 5"
- })
- ])
- serverTestList "dir with project and all analyzers" state allAnalyzersConfig projectDir (fun server -> [
- testCaseAsync "can load file in project" (async {
- let! (doc, diags) = server |> Server.openDocument "Other.fs"
- use doc = doc
-
- Expect.hasLength diags 1 "Should be one diagnostics"
- let diag = diags |> Array.head
- Expect.stringContains diag.Message "The value or constructor 'otherBar' is not defined." "Should be not defined error"
- Expect.equal diag.Range.Start.Line 5 "Error should be in line 6"
- })
- testCaseAsync "can load file in project again" (async {
- let! (doc, diags) = server |> Server.openDocument "Other.fs"
- use doc = doc
-
- Expect.hasLength diags 1 "Should be one diagnostics"
- let diag = diags |> Array.head
- Expect.stringContains diag.Message "The value or constructor 'otherBar' is not defined." "Should be not defined error"
- Expect.equal diag.Range.Start.Line 5 "Error should be in line 6"
- })
- testCaseAsync "can load other file in project" (async {
- let! (doc, diags) = server |> Server.openDocument "Program.fs"
- use doc = doc
-
- Expect.exists diags (fun diag -> diag.Message.Contains "The value or constructor 'programBar' is not defined." && diag.Range.Start.Line = 4) "Should be not defined error"
- // `argv`
- Expect.exists diags (fun diag -> diag.Message.Contains "This value is unused" && diag.Range.Start.Line = 11) "Should be unused value"
- Expect.exists diags (fun diag -> diag.Message.Contains "Unused open statement" && diag.Range.Start.Line = 2) "Should be unused open"
- })
- ])
- ]
- ]
-
- testList "Waiting for diagnostics" [
- let allAnalyzersConfig =
- { defaultConfigDto with
- UnusedOpensAnalyzer = Some true
- UnusedDeclarationsAnalyzer = Some true
- SimplifyNameAnalyzer = Some true
- }
- serverTestList "waitForLatestDiagnostics" state allAnalyzersConfig None (fun server -> [
- // `Document.waitForLatestDiagnostics` is crucial for success of tests: Must wait for newest, current Diagnostics, but ignore diags from previous parses.
- // Issues:
- // * must ignore old events
- // * multiple `publishDiagnostics` for each parse
-
- // Test in here: a script with a lot of Analyzer Diagnostics:
- // Analyzers are checked after F# Compiler Checking is done (-> already one `publishDiagnostics`)
- // After analyzers `documentAnalyzed` gets sent. But might arrive before analyzer diags.
-
- let genSource nCompilerErrorsPerRepeat nUnusedOpensPerRepeat nUnusedDeclsPerRepeat nSimplifyNamesPerRepeat repeats identifier =
- // generate source with lots of Analyzer Diagnostics (and F# compiler errors)
- // identifier to force some textual changes
- let nss = [|
- "System"
- "System.Diagnostics"
- "System.Text"
- "System.Text.RegularExpressions"
- "System.Threading"
- "System.Runtime"
- "FSharp.Control"
- "FSharp.Linq"
- "FSharp.Quotations"
- "FSharp.Reflection"
- |]
- let tys = [|
- "System.String"
- "System.Index"
- "System.Int32"
- "System.Random"
- "System.Guid"
- "System.Text.RegularExpressions.Regex"
- "System.Text.RegularExpressions.Match"
- "System.Text.StringBuilder"
- "System.Diagnostics.TraceLevel"
- "System.Diagnostics.Stopwatch"
- |]
-
- let lines = [
- $"// {identifier}"
- for i in 1..repeats do
- $"// Rep {i}"
- for j in 1..nUnusedOpensPerRepeat do
- let o = Array.get nss ((j-1) % nss.Length)
- $"open {o}"
-
- for j in 1..nUnusedDeclsPerRepeat do
- $"let {identifier}Rep{i}Val{j} = 0"
-
- // note: requires at least 4 UnusedOpens (to `open ...` required for Simplify Name)
- for j in 1..nSimplifyNamesPerRepeat do
- let ty = Array.get tys ((j-1) % tys.Length )
- $"let _{identifier}Rep{i}F{j} (v: {ty}) = v"
-
- // `let _identifier = value`:
- // * value not defined
- // * no unused warning because `_`
- for j in 1..nCompilerErrorsPerRepeat do
- $"let _{identifier}ErrorRep{i}Val{j} = valueRep{i}Val{j}"
-
- ""
- ]
-
- String.concat "\n" lines
-
- testCaseAsync "lots of diagnostics for all analyzers" (async {
- // count for each: n * repeats
- let nCompilerErrorsPerRepeat = 3
- let nUnusedOpensPerRepeat = 7
- let nUnusedDeclsPerRepeat = 5
- let nSimplifyNamesPerRepeat = 9
- let calcExpected repeats =
- {|
- UnusedOpens = nUnusedOpensPerRepeat * repeats
- UnusedDecls = nUnusedDeclsPerRepeat * repeats
- SimplifyNames = nSimplifyNamesPerRepeat * repeats
- CompilerErrors = nCompilerErrorsPerRepeat * repeats
- |}
-
- let repeats = 2
- let source = genSource nCompilerErrorsPerRepeat nUnusedOpensPerRepeat nUnusedDeclsPerRepeat nSimplifyNamesPerRepeat repeats "init"
- let! (doc, diags) = server |> Server.createUntitledDocument source
- use doc = doc
-
- let checkDiags repeats loop diags =
- let expected = calcExpected repeats
- let groups =
- diags
- |> Array.map (fun d ->
- // simplify `The value or constructor 'value' is not defined.` error (contains names and recommendations)
- if d.Code = Some "39" then
- "The value or constructor is not defined"
- else
- d.Message
- )
- |> Array.countBy id
- |> Map.ofArray
- let actual = {|
- UnusedOpens = groups.["Unused open statement"]
- UnusedDecls = groups.["This value is unused"]
- SimplifyNames = groups.["This qualifier is redundant"]
- CompilerErrors = groups.["The value or constructor is not defined"]
- |}
-
- // exact count isn't actually that important because each analyzers sends all its diags together.
- // important part is just: has arrived -> `waitForLatestDiagnostics` waited long enough for all diags
- Expect.equal actual expected $"Incorrect dags in loop {loop}"
-
- checkDiags repeats 0 diags
-
- for i in 1..5 do
- let repeats = repeats + i // to get different numbers of diagnostics
- let source = genSource nCompilerErrorsPerRepeat nUnusedOpensPerRepeat nUnusedDeclsPerRepeat nSimplifyNamesPerRepeat repeats $"loop{i}"
- let! diags = doc |> Document.changeTextTo source
- checkDiags repeats i diags
- })
-
- testCaseAsync "diagnostics for some analyzers" (async {
- let checkDiags (unusedOpen, unusedValue, simplifyName) diags =
- let actual = {|
- UnusedOpen = diags |> Array.exists (fun d -> d.Message = "Unused open statement")
- UnusedDecl = diags |> Array.exists (fun d -> d.Message = "This value is unused")
- SimplifyName = diags |> Array.exists (fun d -> d.Message = "This qualifier is redundant")
- |}
- let expected = {|
- UnusedOpen = unusedOpen
- UnusedDecl = unusedValue
- SimplifyName = simplifyName
- |}
-
- Expect.equal actual expected "Should contain correct diagnostics"
-
- let source = Text.trimTripleQuotation """
+let tests state =
+ testList
+ (nameof (Server))
+ [ testList
+ "no root path"
+ [ testList
+ "can get diagnostics"
+ [ let config =
+ { defaultConfigDto with
+ UnusedOpensAnalyzer = Some false
+ UnusedDeclarationsAnalyzer = Some false
+ SimplifyNameAnalyzer = Some false }
+
+ serverTestList "no analyzers" state config None (fun server ->
+ [ testCaseAsync
+ "can get nothing wrong"
+ (async {
+ let! (doc, diags) = server |> Server.createUntitledDocument ""
+ use doc = doc
+ Expect.isEmpty diags "There should be no diagnostics"
+
+ for i in 1..5 do
+ let! diags = doc |> Document.changeTextTo (string i)
+ Expect.isEmpty diags "There should be no diagnostics"
+ })
+ testCaseAsync
+ "can get single error"
+ (async {
+ let! (doc, diags) = server |> Server.createUntitledDocument "let foo = notdefined"
+ use doc = doc
+ Expect.hasLength diags 1 "There should be 1 error"
+ Expect.exists diags (fun d -> d.Message.Contains "notdefined") ""
+ let! diags = doc |> Document.changeTextTo "let bar = doesnotexist"
+ Expect.hasLength diags 1 "There should be 1 error"
+ Expect.exists diags (fun d -> d.Message.Contains "doesnotexist") ""
+ let! diags = doc |> Document.changeTextTo "let baz = nope"
+ Expect.hasLength diags 1 "There should be 1 error"
+ Expect.exists diags (fun d -> d.Message.Contains "nope") ""
+ })
+ testCaseAsync
+ "can get multiple errors"
+ (async {
+ let source = "let foo = {0}\nlet bar = {1}\nlet baz = {2}"
+ let names = [| "notdefined"; "doesnotexist"; "nope" |]
+ let fnames i = names |> Array.map (fun n -> sprintf "%s%i" n i)
+ let fsource i = String.Format(source, fnames i |> Seq.cast |> Seq.toArray)
+
+ let! (doc, diags) = server |> Server.createUntitledDocument (fsource 0)
+ use doc = doc
+ Expect.hasLength diags (names.Length) ""
+
+ for name in fnames 0 do
+ Expect.exists diags (fun d -> d.Message.Contains name) ""
+
+ for i in 1..2 do
+ let! diags = doc |> Document.changeTextTo (fsource i)
+ Expect.hasLength diags (names.Length) ""
+
+ for name in fnames i do
+ Expect.exists diags (fun d -> d.Message.Contains name) ""
+ }) ])
+
+ let config =
+ { defaultConfigDto with
+ UnusedOpensAnalyzer = Some false
+ UnusedDeclarationsAnalyzer = Some true
+ SimplifyNameAnalyzer = Some false }
+
+ serverTestList "just unused decl analyzer" state config None (fun server ->
+ [ testCaseAsync "can get nothing wrong"
+ <| (async {
+ let! (doc, diags) = server |> Server.createUntitledDocument ""
+ use doc = doc
+ Expect.isEmpty diags "There should be no diagnostics"
+
+ for i in 1..5 do
+ let! diags = doc |> Document.changeTextTo (string i)
+ Expect.isEmpty diags "There should be no diagnostics"
+ })
+ testCaseAsync
+ "can get diags for single line"
+ (async {
+ let! (doc, diags) = server |> Server.createUntitledDocument "let foo = notdefined"
+ use doc = doc
+ Expect.hasLength diags 2 ""
+ Expect.exists diags (fun d -> d.Message.Contains "notdefined") ""
+ Expect.exists diags (fun d -> d.Message = "This value is unused") ""
+ let! diags = doc |> Document.changeTextTo "let bar = doesnotexist"
+ Expect.hasLength diags 2 ""
+ Expect.exists diags (fun d -> d.Message.Contains "doesnotexist") ""
+ Expect.exists diags (fun d -> d.Message = "This value is unused") ""
+ let! diags = doc |> Document.changeTextTo "let baz = nope"
+ Expect.hasLength diags 2 ""
+ Expect.exists diags (fun d -> d.Message.Contains "nope") ""
+ Expect.exists diags (fun d -> d.Message = "This value is unused") ""
+ })
+ testCaseAsync
+ "can get diags for multiple lines"
+ (async {
+ let nVars = 3
+ let values i = Array.init nVars (sprintf "someValue%i%i" i)
+ let source i = values i |> Seq.mapi (sprintf "let var%i = %s") |> String.concat "\n"
+
+ let! (doc, diags) = server |> Server.createUntitledDocument (source 0)
+ use doc = doc
+ Expect.hasLength diags (nVars * 2) ""
+
+ values 0
+ |> Array.iteri (fun i name ->
+ Expect.exists diags (fun d -> d.Message.Contains name) $"No diags with name {name}"
+
+ Expect.exists
+ diags
+ (fun d -> d.Message = "This value is unused" && d.Range.Start.Line = i)
+ $"No unused value error in line {i}")
+
+ for i in 1..2 do
+ let! diags = doc |> Document.changeTextTo (source i)
+ Expect.hasLength diags (nVars * 2) ""
+
+ values i
+ |> Array.iteri (fun i name ->
+ Expect.exists diags (fun d -> d.Message.Contains name) $"No diags with name {name}"
+
+ Expect.exists
+ diags
+ (fun d -> d.Message = "This value is unused" && d.Range.Start.Line = i)
+ $"No unused value error in line {i}")
+ }) ])
+
+ let config =
+ { defaultConfigDto with
+ UnusedOpensAnalyzer = Some true
+ UnusedDeclarationsAnalyzer = Some true
+ SimplifyNameAnalyzer = Some true }
+
+ serverTestList "three analyzers" state config None (fun server ->
+ [ testCaseAsync
+ "can get nothing wrong"
+ (async {
+ let! (doc, diags) = server |> Server.createUntitledDocument ""
+ use doc = doc
+ Expect.isEmpty diags "There should be no diagnostics"
+
+ for i in 1..5 do
+ let! diags = doc |> Document.changeTextTo (string i)
+ Expect.isEmpty diags "There should be no diagnostics"
+ })
+ testCaseAsync
+ "can get all diags"
+ (async {
+ let source = "open System\nlet foo = bar\nSystem.String.Empty |> ignore"
+ let! (doc, diags) = server |> Server.createUntitledDocument source
+ use doc = doc
+
+ Expect.hasLength diags 4 ""
+ Expect.exists diags (fun d -> d.Message = "Unused open statement" && d.Range.Start.Line = 0) ""
+ Expect.exists diags (fun d -> d.Message = "This value is unused" && d.Range.Start.Line = 1) ""
+ Expect.exists diags (fun d -> d.Message.Contains "bar" && d.Range.Start.Line = 1) ""
+
+ Expect.exists
+ diags
+ (fun d -> d.Message = "This qualifier is redundant" && d.Range.Start.Line = 2)
+ ""
+
+
+ let source =
+ "open System.Collections\nlet baz = foo\nSystem.Collections.Generic.List() |> ignore"
+
+ let! diags = doc |> Document.changeTextTo source
+
+ Expect.hasLength diags 4 ""
+ Expect.exists diags (fun d -> d.Message = "Unused open statement" && d.Range.Start.Line = 0) ""
+ Expect.exists diags (fun d -> d.Message = "This value is unused" && d.Range.Start.Line = 1) ""
+ Expect.exists diags (fun d -> d.Message.Contains "foo" && d.Range.Start.Line = 1) ""
+
+ Expect.exists
+ diags
+ (fun d -> d.Message = "This qualifier is redundant" && d.Range.Start.Line = 2)
+ ""
+
+
+ let source =
+ "open System.Diagnostics\nlet bar = baz\nSystem.Diagnostics.Debugger.IsAttached"
+
+ let! diags = doc |> Document.changeTextTo source
+
+ Expect.hasLength diags 4 ""
+ Expect.exists diags (fun d -> d.Message = "Unused open statement" && d.Range.Start.Line = 0) ""
+ Expect.exists diags (fun d -> d.Message = "This value is unused" && d.Range.Start.Line = 1) ""
+ Expect.exists diags (fun d -> d.Message.Contains "baz" && d.Range.Start.Line = 1) ""
+
+ Expect.exists
+ diags
+ (fun d -> d.Message = "This qualifier is redundant" && d.Range.Start.Line = 2)
+ ""
+ }) ]) ]
+
+ testList
+ "untitled document"
+ [ serverTestList
+ "untitled counter in server for createUntitledDocument"
+ state
+ defaultConfigDto
+ None
+ (fun server ->
+ [ testCaseAsync
+ "creating document increases untitled counter"
+ (async {
+ let! actualServer = server
+ let preCounter = actualServer.UntitledCounter
+ let! (doc, _) = server |> Server.createUntitledDocument ""
+ use _doc = doc
+ let postCounter = actualServer.UntitledCounter
+
+ Expect.isGreaterThan postCounter preCounter "Untitled Counter should increase"
+ })
+ testCaseAsync
+ "creating multiple documents increases untitled counter"
+ (async {
+ let getCounter server = server |> Async.map (fun s -> s.UntitledCounter)
+
+ let! preCounter = getCounter server
+ let mutable preCounter = preCounter
+
+ for _ in 1..5 do
+ let! (doc, _) = server |> Server.createUntitledDocument ""
+ use _doc = doc
+ let! postCounter = getCounter server
+ Expect.isGreaterThan postCounter preCounter "Untitled Counter should increase"
+ preCounter <- postCounter
+ }) ])
+
+ serverTestList "document version" state defaultConfigDto None (fun server ->
+ [ testCaseAsync
+ "changing document text increases document version"
+ (async {
+ let! (doc, _) = server |> Server.createUntitledDocument ""
+ let preVersion = doc.Version
+ let! _ = doc |> Document.changeTextTo "42"
+ let postVersion = doc.Version
+
+ Expect.isGreaterThan postVersion preVersion "Document Version should increase"
+ })
+ testCaseAsync
+ "changing document text multiple times should always increase document version"
+ (async {
+ let! (doc, _) = server |> Server.createUntitledDocument ""
+ let mutable preVersion = doc.Version
+
+ for _ in 1..5 do
+ let! _ = doc |> Document.changeTextTo ""
+ let postVersion = doc.Version
+ Expect.isGreaterThan postVersion preVersion "Document Version should increase"
+ preVersion <- postVersion
+ }) ]) ] ]
+
+ testList
+ "with root path"
+ [ let inTestCases name =
+ System.IO.Path.Combine(__SOURCE_DIRECTORY__, "..", "TestCases", "ServerTests", name)
+ |> Some
+
+ let noAnalyzersConfig =
+ { defaultConfigDto with
+ UnusedOpensAnalyzer = Some false
+ UnusedDeclarationsAnalyzer = Some false
+ SimplifyNameAnalyzer = Some false }
+
+ let allAnalyzersConfig =
+ { defaultConfigDto with
+ UnusedOpensAnalyzer = Some true
+ UnusedDeclarationsAnalyzer = Some true
+ SimplifyNameAnalyzer = Some true }
+
+ serverTestList
+ "dir with just a script and no analyzers"
+ state
+ noAnalyzersConfig
+ (inTestCases "JustScript")
+ (fun server ->
+ [ testCaseAsync
+ "can load script file"
+ (async {
+ let! (doc, diags) = server |> Server.openDocument "Script.fsx"
+ use _doc = doc
+
+ Expect.hasLength diags 1 "Should be one diagnostics"
+ let diag = diags |> Array.head
+
+ Expect.stringContains
+ diag.Message
+ "The value or constructor 'bar' is not defined."
+ "Should be not defined error"
+
+ Expect.equal diag.Range.Start.Line 0 "Error should be in line 1"
+ })
+ testCaseAsync
+ "can load script file again"
+ (async {
+ let! (doc, diags) = server |> Server.openDocument "Script.fsx"
+ use _doc = doc
+
+ Expect.hasLength diags 1 "Should be one diagnostics"
+ let diag = diags |> Array.head
+
+ Expect.stringContains
+ diag.Message
+ "The value or constructor 'bar' is not defined."
+ "Should be not defined error"
+
+ Expect.equal diag.Range.Start.Line 0 "Error should be in line 1"
+ }) ])
+
+ serverTestList
+ "dir with just a script and all anaylzers"
+ state
+ allAnalyzersConfig
+ (inTestCases "JustScript")
+ (fun server ->
+ [ testCaseAsync
+ "can load script file"
+ (async {
+ let! (doc, diags) = server |> Server.openDocument "Script.fsx"
+ use _doc = doc
+
+ Expect.exists
+ diags
+ (fun diag ->
+ diag.Message.Contains "The value or constructor 'bar' is not defined."
+ && diag.Range.Start.Line = 0)
+ "Should be not defined error"
+
+ Expect.exists
+ diags
+ (fun diag -> diag.Message.Contains "This value is unused" && diag.Range.Start.Line = 0)
+ "Should be unused value"
+ })
+ testCaseAsync
+ "can load script file again"
+ (async {
+ let! (doc, diags) = server |> Server.openDocument "Script.fsx"
+ use _doc = doc
+
+ Expect.exists
+ diags
+ (fun diag ->
+ diag.Message.Contains "The value or constructor 'bar' is not defined."
+ && diag.Range.Start.Line = 0)
+ "Should be not defined error"
+
+ Expect.exists
+ diags
+ (fun diag -> diag.Message.Contains "This value is unused" && diag.Range.Start.Line = 0)
+ "Should be unused value"
+ }) ])
+
+ testSequenced
+ <| testList
+ "contesting"
+ [ let projectDir = inTestCases "Project"
+
+ serverTestList "dir with project and no analyzers" state noAnalyzersConfig projectDir (fun server ->
+ [ testCaseAsync
+ "can load file in project"
+ (async {
+ let! (doc, diags) = server |> Server.openDocument "Other.fs"
+ use _doc = doc
+
+ Expect.hasLength diags 1 "Should be one diagnostics"
+ let diag = diags |> Array.head
+
+ Expect.stringContains
+ diag.Message
+ "The value or constructor 'otherBar' is not defined."
+ "Should be not defined error"
+
+ Expect.equal diag.Range.Start.Line 5 "Error should be in line 6"
+ })
+ testCaseAsync
+ "can load file in project again"
+ (async {
+ let! (doc, diags) = server |> Server.openDocument "Other.fs"
+ use _doc = doc
+
+ Expect.hasLength diags 1 "Should be one diagnostics"
+ let diag = diags |> Array.head
+
+ Expect.stringContains
+ diag.Message
+ "The value or constructor 'otherBar' is not defined."
+ "Should be not defined error"
+
+ Expect.equal diag.Range.Start.Line 5 "Error should be in line 6"
+ })
+ testCaseAsync
+ "can load other file in project"
+ (async {
+ let! (doc, diags) = server |> Server.openDocument "Program.fs"
+ use _doc = doc
+
+ Expect.hasLength diags 1 "Should be one diagnostics"
+ let diag = diags |> Array.head
+
+ Expect.stringContains
+ diag.Message
+ "The value or constructor 'programBar' is not defined."
+ "Should be not defined error"
+
+ Expect.equal diag.Range.Start.Line 4 "Error should be in line 5"
+ }) ])
+
+ serverTestList "dir with project and all analyzers" state allAnalyzersConfig projectDir (fun server ->
+ [ testCaseAsync
+ "can load file in project"
+ (async {
+ let! (doc, diags) = server |> Server.openDocument "Other.fs"
+ use _doc = doc
+
+ Expect.hasLength diags 1 "Should be one diagnostics"
+ let diag = diags |> Array.head
+
+ Expect.stringContains
+ diag.Message
+ "The value or constructor 'otherBar' is not defined."
+ "Should be not defined error"
+
+ Expect.equal diag.Range.Start.Line 5 "Error should be in line 6"
+ })
+ testCaseAsync
+ "can load file in project again"
+ (async {
+ let! (doc, diags) = server |> Server.openDocument "Other.fs"
+ use _doc = doc
+
+ Expect.hasLength diags 1 "Should be one diagnostics"
+ let diag = diags |> Array.head
+
+ Expect.stringContains
+ diag.Message
+ "The value or constructor 'otherBar' is not defined."
+ "Should be not defined error"
+
+ Expect.equal diag.Range.Start.Line 5 "Error should be in line 6"
+ })
+ testCaseAsync
+ "can load other file in project"
+ (async {
+ let! (doc, diags) = server |> Server.openDocument "Program.fs"
+ use _doc = doc
+
+ Expect.exists
+ diags
+ (fun diag ->
+ diag.Message.Contains "The value or constructor 'programBar' is not defined."
+ && diag.Range.Start.Line = 4)
+ "Should be not defined error"
+ // `argv`
+ Expect.exists
+ diags
+ (fun diag -> diag.Message.Contains "This value is unused" && diag.Range.Start.Line = 11)
+ "Should be unused value"
+
+ Expect.exists
+ diags
+ (fun diag -> diag.Message.Contains "Unused open statement" && diag.Range.Start.Line = 2)
+ "Should be unused open"
+ }) ]) ] ]
+
+ testList
+ "Waiting for diagnostics"
+ [ let allAnalyzersConfig =
+ { defaultConfigDto with
+ UnusedOpensAnalyzer = Some true
+ UnusedDeclarationsAnalyzer = Some true
+ SimplifyNameAnalyzer = Some true }
+
+ serverTestList "waitForLatestDiagnostics" state allAnalyzersConfig None (fun server ->
+ [
+ // `Document.waitForLatestDiagnostics` is crucial for success of tests: Must wait for newest, current Diagnostics, but ignore diags from previous parses.
+ // Issues:
+ // * must ignore old events
+ // * multiple `publishDiagnostics` for each parse
+
+ // Test in here: a script with a lot of Analyzer Diagnostics:
+ // Analyzers are checked after F# Compiler Checking is done (-> already one `publishDiagnostics`)
+ // After analyzers `documentAnalyzed` gets sent. But might arrive before analyzer diags.
+
+ let genSource
+ nCompilerErrorsPerRepeat
+ nUnusedOpensPerRepeat
+ nUnusedDeclsPerRepeat
+ nSimplifyNamesPerRepeat
+ repeats
+ identifier
+ =
+ // generate source with lots of Analyzer Diagnostics (and F# compiler errors)
+ // identifier to force some textual changes
+ let nss =
+ [| "System"
+ "System.Diagnostics"
+ "System.Text"
+ "System.Text.RegularExpressions"
+ "System.Threading"
+ "System.Runtime"
+ "FSharp.Control"
+ "FSharp.Linq"
+ "FSharp.Quotations"
+ "FSharp.Reflection" |]
+
+ let tys =
+ [| "System.String"
+ "System.Index"
+ "System.Int32"
+ "System.Random"
+ "System.Guid"
+ "System.Text.RegularExpressions.Regex"
+ "System.Text.RegularExpressions.Match"
+ "System.Text.StringBuilder"
+ "System.Diagnostics.TraceLevel"
+ "System.Diagnostics.Stopwatch" |]
+
+ let lines =
+ [ $"// {identifier}"
+ for i in 1..repeats do
+ $"// Rep {i}"
+
+ for j in 1..nUnusedOpensPerRepeat do
+ let o = Array.get nss ((j - 1) % nss.Length)
+ $"open {o}"
+
+ for j in 1..nUnusedDeclsPerRepeat do
+ $"let {identifier}Rep{i}Val{j} = 0"
+
+ // note: requires at least 4 UnusedOpens (to `open ...` required for Simplify Name)
+ for j in 1..nSimplifyNamesPerRepeat do
+ let ty = Array.get tys ((j - 1) % tys.Length)
+ $"let _{identifier}Rep{i}F{j} (v: {ty}) = v"
+
+ // `let _identifier = value`:
+ // * value not defined
+ // * no unused warning because `_`
+ for j in 1..nCompilerErrorsPerRepeat do
+ $"let _{identifier}ErrorRep{i}Val{j} = valueRep{i}Val{j}"
+
+ "" ]
+
+ String.concat "\n" lines
+
+ testCaseAsync
+ "lots of diagnostics for all analyzers"
+ (async {
+ // count for each: n * repeats
+ let nCompilerErrorsPerRepeat = 3
+ let nUnusedOpensPerRepeat = 7
+ let nUnusedDeclsPerRepeat = 5
+ let nSimplifyNamesPerRepeat = 9
+
+ let calcExpected repeats =
+ {| UnusedOpens = nUnusedOpensPerRepeat * repeats
+ UnusedDecls = nUnusedDeclsPerRepeat * repeats
+ SimplifyNames = nSimplifyNamesPerRepeat * repeats
+ CompilerErrors = nCompilerErrorsPerRepeat * repeats |}
+
+ let repeats = 2
+
+ let source =
+ genSource
+ nCompilerErrorsPerRepeat
+ nUnusedOpensPerRepeat
+ nUnusedDeclsPerRepeat
+ nSimplifyNamesPerRepeat
+ repeats
+ "init"
+
+ let! (doc, diags) = server |> Server.createUntitledDocument source
+ use doc = doc
+
+ let checkDiags repeats loop diags =
+ let expected = calcExpected repeats
+
+ let groups =
+ diags
+ |> Array.map (fun d ->
+ // simplify `The value or constructor 'value' is not defined.` error (contains names and recommendations)
+ if d.Code = Some "39" then
+ "The value or constructor is not defined"
+ else
+ d.Message)
+ |> Array.countBy id
+ |> Map.ofArray
+
+ let actual =
+ {| UnusedOpens = groups.["Unused open statement"]
+ UnusedDecls = groups.["This value is unused"]
+ SimplifyNames = groups.["This qualifier is redundant"]
+ CompilerErrors = groups.["The value or constructor is not defined"] |}
+
+ // exact count isn't actually that important because each analyzers sends all its diags together.
+ // important part is just: has arrived -> `waitForLatestDiagnostics` waited long enough for all diags
+ Expect.equal actual expected $"Incorrect dags in loop {loop}"
+
+ checkDiags repeats 0 diags
+
+ for i in 1..5 do
+ let repeats = repeats + i // to get different numbers of diagnostics
+
+ let source =
+ genSource
+ nCompilerErrorsPerRepeat
+ nUnusedOpensPerRepeat
+ nUnusedDeclsPerRepeat
+ nSimplifyNamesPerRepeat
+ repeats
+ $"loop{i}"
+
+ let! diags = doc |> Document.changeTextTo source
+ checkDiags repeats i diags
+ })
+
+ testCaseAsync
+ "diagnostics for some analyzers"
+ (async {
+ let checkDiags (unusedOpen, unusedValue, simplifyName) diags =
+ let actual =
+ {| UnusedOpen = diags |> Array.exists (fun d -> d.Message = "Unused open statement")
+ UnusedDecl = diags |> Array.exists (fun d -> d.Message = "This value is unused")
+ SimplifyName = diags |> Array.exists (fun d -> d.Message = "This qualifier is redundant") |}
+
+ let expected =
+ {| UnusedOpen = unusedOpen
+ UnusedDecl = unusedValue
+ SimplifyName = simplifyName |}
+
+ Expect.equal actual expected "Should contain correct diagnostics"
+
+ let source =
+ Text.trimTripleQuotation
+ """
open System
open System.Diagnostics
open System.Text
@@ -485,39 +661,51 @@ let x = 1
let y = 2
let z = 3
"""
- let! (doc, diags) = server |> Server.createUntitledDocument source
- use doc = doc
- checkDiags (true, true, false) diags
- let source = Text.trimTripleQuotation """
+ let! (doc, diags) = server |> Server.createUntitledDocument source
+ use doc = doc
+ checkDiags (true, true, false) diags
+
+ let source =
+ Text.trimTripleQuotation
+ """
let x = 1
let y = 2
let z = 3
"""
- let! diags = doc |> Document.changeTextTo source
- checkDiags (false, true, false) diags
- let source = Text.trimTripleQuotation """
+ let! diags = doc |> Document.changeTextTo source
+ checkDiags (false, true, false) diags
+
+ let source =
+ Text.trimTripleQuotation
+ """
open System
open System.Diagnostics
open System.Text
()
"""
- let! diags = doc |> Document.changeTextTo source
- checkDiags (true, false, false) diags
- let source = Text.trimTripleQuotation """
+ let! diags = doc |> Document.changeTextTo source
+ checkDiags (true, false, false) diags
+
+ let source =
+ Text.trimTripleQuotation
+ """
open System
open System.Diagnostics
open System.Text
let _f (v: System.String) = v
"""
- let! diags = doc |> Document.changeTextTo source
- checkDiags (true, false, true) diags
- let source = Text.trimTripleQuotation """
+ let! diags = doc |> Document.changeTextTo source
+ checkDiags (true, false, true) diags
+
+ let source =
+ Text.trimTripleQuotation
+ """
open System
open System.Diagnostics
open System.Text
@@ -525,24 +713,26 @@ open System.Text
let f (v: System.String) = ()
"""
- let! diags = doc |> Document.changeTextTo source
- checkDiags (true, true, true) diags
-
- let source = "()"
- let! diags = doc |> Document.changeTextTo source
- checkDiags (false, false, false) diags
- })
- ])
- ]
-
- testList "timing" [
- let allAnalyzersConfig =
- { defaultConfigDto with
- UnusedOpensAnalyzer = Some true
- UnusedDeclarationsAnalyzer = Some true
- SimplifyNameAnalyzer = Some true
- }
- let mkSource (msg: string) = Text.trimTripleQuotation $"""
+
+ let! diags = doc |> Document.changeTextTo source
+ checkDiags (true, true, true) diags
+
+ let source = "()"
+ let! diags = doc |> Document.changeTextTo source
+ checkDiags (false, false, false) diags
+ }) ]) ]
+
+ testList
+ "timing"
+ [ let allAnalyzersConfig =
+ { defaultConfigDto with
+ UnusedOpensAnalyzer = Some true
+ UnusedDeclarationsAnalyzer = Some true
+ SimplifyNameAnalyzer = Some true }
+
+ let mkSource (msg: string) =
+ Text.trimTripleQuotation
+ $"""
open System
// {msg}
@@ -557,113 +747,147 @@ f1 foo
// {msg}
f2 "bar" |> ignore
"""
- serverTestList "server" state allAnalyzersConfig None (fun server -> [
- testList "single parse" [
- testCaseAsync "single parse" <| async {
- let! (doc, _) = server |> Server.createUntitledDocument (mkSource "single parse")
- use doc = doc
- ()
- }
- ]
- testList "parse of same document" [
- testCaseAsync "single doc" <| async {
- let! (doc, _) = server |> Server.createUntitledDocument (mkSource "0 parse")
- use doc = doc
-
- for i in 1..5 do
- let! _ = doc |> Document.changeTextTo (mkSource $"Parse {i}")
- ()
- ()
- }
- ]
- testList "parse in different documents" [
- for i in 0..5 do
- testCaseAsync $"doc {i}" <| async {
- let! (doc, _) = server |> Server.createUntitledDocument (mkSource "parse {i}")
- use doc = doc
- ()
- }
- ]
- ])
- ]
-
- testList "Document" [
- serverTestList "no root path without analyzers" state defaultConfigDto None (fun server -> [
- testCaseAsync "can create Document by absolute path without root path" <| async {
- let relativePath = "../TestCases/ServerTests/JustScript/Script.fsx"
- let absolutePath = System.IO.Path.GetFullPath(System.IO.Path.Combine(__SOURCE_DIRECTORY__, relativePath))
- let! (doc, _) = server |> Server.openDocument absolutePath
- use doc = doc
- ()
- }
-
- let mutable docState = {| Uri = ""; Version = -1; CallCounter = 0 |}
- let getDoc server = async {
- let text = Text.trimTripleQuotation """
+
+ serverTestList "server" state allAnalyzersConfig None (fun server ->
+ [ testList
+ "single parse"
+ [ testCaseAsync "single parse"
+ <| async {
+ let! (doc, _) = server |> Server.createUntitledDocument (mkSource "single parse")
+ use _doc = doc
+ ()
+ } ]
+ testList
+ "parse of same document"
+ [ testCaseAsync "single doc"
+ <| async {
+ let! (doc, _) = server |> Server.createUntitledDocument (mkSource "0 parse")
+ use doc = doc
+
+ for i in 1..5 do
+ let! _ = doc |> Document.changeTextTo (mkSource $"Parse {i}")
+ ()
+
+ ()
+ } ]
+ testList
+ "parse in different documents"
+ [ for i in 0..5 do
+ testCaseAsync $"doc {i}"
+ <| async {
+ let! (doc, _) = server |> Server.createUntitledDocument (mkSource "parse {i}")
+ use _doc = doc
+ ()
+ } ] ]) ]
+
+ testList
+ "Document"
+ [ serverTestList "no root path without analyzers" state defaultConfigDto None (fun server ->
+ [ testCaseAsync "can create Document by absolute path without root path"
+ <| async {
+ let relativePath = "../TestCases/ServerTests/JustScript/Script.fsx"
+
+ let absolutePath =
+ System.IO.Path.GetFullPath(System.IO.Path.Combine(__SOURCE_DIRECTORY__, relativePath))
+
+ let! (doc, _) = server |> Server.openDocument absolutePath
+ use _doc = doc
+ ()
+ }
+
+ let mutable docState =
+ {| Uri = ""
+ Version = -1
+ CallCounter = 0 |}
+
+ let getDoc server =
+ async {
+ let text =
+ Text.trimTripleQuotation
+ """
let bar = "hello world"
let foo = System.String.
"""
- let! (doc, diags) = server |> Server.createUntitledDocument text
- docState <- {|
- Uri = doc.Uri
- Version = doc.Version
- // tracks how often `getDoc` was called
- CallCounter = docState.CallCounter + 1
- |}
- return (doc, diags)
- }
- documentTestList "multiple actions on single document" server getDoc (fun doc -> [
- testCaseAsync "doc is doc returned from getDocument" <| async {
- let! (doc,_) = doc
- Expect.equal (doc.Uri, doc.Version) (docState.Uri, docState.Version) "Should be same doc"
- Expect.equal docState.CallCounter 1 "getDocument should only be called once"
- }
- testCaseAsync "doc stays same" <| async {
- let! (doc,_) = doc
- Expect.equal (doc.Uri, doc.Version) (docState.Uri, docState.Version) "Should be same doc"
- Expect.equal docState.CallCounter 1 "getDocument should only be called once"
- }
- let completionAt pos (doc: Document) = async {
- let ps: CompletionParams = {
- TextDocument = doc.TextDocumentIdentifier
- Position = pos
- Context = None
- }
- let! res = doc.Server.Server.TextDocumentCompletion ps
- Expect.isOk res "Should be ok result"
- return res |> Result.defaultWith (fun _ -> failtest "unreachable")
- }
- testCaseAsync "can get completions" <| async {
- let! (doc,_) = doc
- let! completions = doc |> completionAt { Line = 1; Character = 24 }
- Expect.isSome completions "Should be some completions"
- let completions = completions.Value
- Expect.isNonEmpty completions.Items "Should be completions"
- Expect.exists completions.Items (fun i -> i.Label = "IsNullOrWhiteSpace") "Should have `IsNullOrWhiteSpace` completion"
- }
- testCaseAsync "can get completions again" <| async {
- let! (doc,_) = doc
- let! completions = doc |> completionAt { Line = 1; Character = 24 }
- Expect.isSome completions "Should be some completions"
- let completions = completions.Value
- Expect.isNonEmpty completions.Items "Should be completions"
- Expect.exists completions.Items (fun i -> i.Label = "IsNullOrWhiteSpace") "Should have `IsNullOrWhiteSpace` completion"
- }
- testCaseAsync "can get signature help" <| async {
- let! (doc,_) = doc
- let ps: TextDocumentPositionParams = {
- TextDocument = doc.TextDocumentIdentifier
- Position = { Line = 0; Character = 6 }
- }
- let! res = doc.Server.Server.TextDocumentHover ps
- Expect.isOk res "Should have hover data"
- }
- testCaseAsync "doc is still same" <| async {
- let! (doc,_) = doc
- Expect.equal (doc.Uri, doc.Version) (docState.Uri, docState.Version) "Should be same doc"
- Expect.equal docState.CallCounter 1 "getDocument should only be called once"
- }
- ])
- ])
- ]
-]
+
+ let! (doc, diags) = server |> Server.createUntitledDocument text
+
+ docState <-
+ {| Uri = doc.Uri
+ Version = doc.Version
+ // tracks how often `getDoc` was called
+ CallCounter = docState.CallCounter + 1 |}
+
+ return (doc, diags)
+ }
+
+ documentTestList "multiple actions on single document" server getDoc (fun doc ->
+ [ testCaseAsync "doc is doc returned from getDocument"
+ <| async {
+ let! (doc, _) = doc
+ Expect.equal (doc.Uri, doc.Version) (docState.Uri, docState.Version) "Should be same doc"
+ Expect.equal docState.CallCounter 1 "getDocument should only be called once"
+ }
+ testCaseAsync "doc stays same"
+ <| async {
+ let! (doc, _) = doc
+ Expect.equal (doc.Uri, doc.Version) (docState.Uri, docState.Version) "Should be same doc"
+ Expect.equal docState.CallCounter 1 "getDocument should only be called once"
+ }
+ let completionAt pos (doc: Document) =
+ async {
+ let ps: CompletionParams =
+ { TextDocument = doc.TextDocumentIdentifier
+ Position = pos
+ Context = None }
+
+ let! res = doc.Server.Server.TextDocumentCompletion ps
+ Expect.isOk res "Should be ok result"
+ return res |> Result.defaultWith (fun _ -> failtest "unreachable")
+ }
+
+ testCaseAsync "can get completions"
+ <| async {
+ let! (doc, _) = doc
+ let! completions = doc |> completionAt { Line = 1; Character = 24 }
+ Expect.isSome completions "Should be some completions"
+ let completions = completions.Value
+ Expect.isNonEmpty completions.Items "Should be completions"
+
+ Expect.exists
+ completions.Items
+ (fun i -> i.Label = "IsNullOrWhiteSpace")
+ "Should have `IsNullOrWhiteSpace` completion"
+ }
+
+ testCaseAsync "can get completions again"
+ <| async {
+ let! (doc, _) = doc
+ let! completions = doc |> completionAt { Line = 1; Character = 24 }
+ Expect.isSome completions "Should be some completions"
+ let completions = completions.Value
+ Expect.isNonEmpty completions.Items "Should be completions"
+
+ Expect.exists
+ completions.Items
+ (fun i -> i.Label = "IsNullOrWhiteSpace")
+ "Should have `IsNullOrWhiteSpace` completion"
+ }
+
+ testCaseAsync "can get signature help"
+ <| async {
+ let! (doc, _) = doc
+
+ let ps: TextDocumentPositionParams =
+ { TextDocument = doc.TextDocumentIdentifier
+ Position = { Line = 0; Character = 6 } }
+
+ let! res = doc.Server.Server.TextDocumentHover ps
+ Expect.isOk res "Should have hover data"
+ }
+
+ testCaseAsync "doc is still same"
+ <| async {
+ let! (doc, _) = doc
+ Expect.equal (doc.Uri, doc.Version) (docState.Uri, docState.Version) "Should be same doc"
+ Expect.equal docState.CallCounter 1 "getDocument should only be called once"
+ } ]) ]) ] ]
diff --git a/test/FsAutoComplete.Tests.Lsp/Utils/TextEdit.Tests.fs b/test/FsAutoComplete.Tests.Lsp/Utils/TextEdit.Tests.fs
index fd9d630a2..aff5ff7a6 100644
--- a/test/FsAutoComplete.Tests.Lsp/Utils/TextEdit.Tests.fs
+++ b/test/FsAutoComplete.Tests.Lsp/Utils/TextEdit.Tests.fs
@@ -9,551 +9,701 @@ open Ionide.LanguageServerProtocol.Types
open Utils.TextEdit
open Utils.Utils
-let private logger = Expecto.Logging.Log.create (sprintf "%s.%s" (nameof Utils.Tests) (nameof Utils.TextEdit))
-let inline private pos line column: Position = { Line = line; Character = column }
-let inline private range start fin = { Start = start; End = fin}
+let private logger =
+ Expecto.Logging.Log.create (sprintf "%s.%s" (nameof Utils.Tests) (nameof Utils.TextEdit))
+
+let inline private pos line column : Position = { Line = line; Character = column }
+let inline private range start fin = { Start = start; End = fin }
let inline private posRange pos = range pos pos
let inline private (!-) text = Text.trimTripleQuotation text
module private Cursor =
open Expecto.Flip
- let private tryExtractIndexTests = testList (nameof Cursor.tryExtractIndex) [
- testList "no cursor" [
- let assertNoCursor =
- Cursor.tryExtractPosition
- >> Expect.isNone "should have found no cursor"
- testCase "empty string" <| fun _ ->
- let text = ""
- assertNoCursor text
- testCase "single line" <| fun _ ->
- let text = "Foo Bar Baz"
- assertNoCursor text
- testCase "two lines" <| fun _ ->
- let text = "Foo Bar Baz\nLorem ipsum dolor sit"
- assertNoCursor text
- testCase "multiple lines" <| fun _ ->
- let text = "Foo\nBar\nBaz\nLorem\nimpsum\ndolor\nsit"
- assertNoCursor text
- testCase "just spaces" <| fun _ ->
- let text = " "
- assertNoCursor text
- testCase "just spaces and new lines" <| fun _ ->
- let text = " \n \n\n \n\n\n\n \n \n \n"
- assertNoCursor text
- testCase "triple quoted string without processing" <| fun _ ->
- let text = """
-module Foo
-
-let a = 42
-let b =
+ let private tryExtractIndexTests =
+ testList
+ (nameof Cursor.tryExtractIndex)
+ [ testList
+ "no cursor"
+ [ let assertNoCursor =
+ Cursor.tryExtractPosition >> Expect.isNone "should have found no cursor"
+
+ testCase "empty string"
+ <| fun _ ->
+ let text = ""
+ assertNoCursor text
+
+ testCase "single line"
+ <| fun _ ->
+ let text = "Foo Bar Baz"
+ assertNoCursor text
+
+ testCase "two lines"
+ <| fun _ ->
+ let text = "Foo Bar Baz\nLorem ipsum dolor sit"
+ assertNoCursor text
+
+ testCase "multiple lines"
+ <| fun _ ->
+ let text = "Foo\nBar\nBaz\nLorem\nimpsum\ndolor\nsit"
+ assertNoCursor text
+
+ testCase "just spaces"
+ <| fun _ ->
+ let text = " "
+ assertNoCursor text
+
+ testCase "just spaces and new lines"
+ <| fun _ ->
+ let text = " \n \n\n \n\n\n\n \n \n \n"
+ assertNoCursor text
+
+ testCase "triple quoted string without processing"
+ <| fun _ ->
+ let text =
+ """
+module Foo
+
+let a = 42
+let b =
a + 5
printfn "Result=%i" b
"""
- assertNoCursor text
- testCase "triple quoted string with processing (starting new line, no indentation)" <| fun _ ->
- let text = !- """
+
+ assertNoCursor text
+
+ testCase "triple quoted string with processing (starting new line, no indentation)"
+ <| fun _ ->
+ let text =
+ !- """
module Foo
let a = 42
-let b =
+let b =
a + 5
printfn "Result=%i" b
"""
- assertNoCursor text
- testCase "triple quoted string with processing (starting new line, indentation)" <| fun _ ->
- let text = !- """
+
+ assertNoCursor text
+
+ testCase "triple quoted string with processing (starting new line, indentation)"
+ <| fun _ ->
+ let text =
+ !- """
module Foo
let a = 42
- let b =
+ let b =
a + 5
printfn "Result=%i" b
"""
- assertNoCursor text
- ]
- testList "with cursor" [
- let assertAndGetIndex =
- Text.trimTripleQuotation
- >> Cursor.tryExtractIndex
- >> Option.defaultWith (fun _ -> failtest "No cursor found")
- let assertResultIs (idx: int, text: string) =
- assertAndGetIndex
- >> Expect.equal "should be correct cursor position and text" (idx, text)
- let assertCursorAt (idx: int) =
- assertAndGetIndex
- >> fst
- >> Expect.equal "should have found cursor at correct position" idx
- let assertTextIs (text: string) =
- assertAndGetIndex
- >> snd
- >> Expect.equal "should have correct text" text
-
- testList "in normal string" [
- testCase "in empty string" <| fun _ ->
- let text = "$0"
- let expected = 0
- text |> assertCursorAt expected
- testCase "start of single line" <| fun _ ->
- let text = "$0Foo bar baz"
- let expected = 0
- text |> assertCursorAt expected
- testCase "end of single word" <| fun _ ->
- let text = "foo$0"
- // Note: out of string range: cursor is AFTER last character
- let expected = 3
- text |> assertCursorAt expected
- testCase "end of single line" <| fun _ ->
- let text = "foo bar baz$0"
- let expected = 11
- text |> assertCursorAt expected
- testCase "removes cursor marker from single line" <| fun _ ->
- let text = "foo $0bar"
- let expected = "foo bar"
- text |> assertTextIs expected
- ]
- testList "in triple quoted string" [
- testCase "in empty string unindented" <| fun _ ->
- // technically incorrect: contains `\n`
- let text = """
+
+ assertNoCursor text ]
+ testList
+ "with cursor"
+ [ let assertAndGetIndex =
+ Text.trimTripleQuotation
+ >> Cursor.tryExtractIndex
+ >> Option.defaultWith (fun _ -> failtest "No cursor found")
+
+ let _assertResultIs (idx: int, text: string) =
+ assertAndGetIndex
+ >> Expect.equal "should be correct cursor position and text" (idx, text)
+
+ let assertCursorAt (idx: int) =
+ assertAndGetIndex
+ >> fst
+ >> Expect.equal "should have found cursor at correct position" idx
+
+ let assertTextIs (text: string) = assertAndGetIndex >> snd >> Expect.equal "should have correct text" text
+
+ testList
+ "in normal string"
+ [ testCase "in empty string"
+ <| fun _ ->
+ let text = "$0"
+ let expected = 0
+ text |> assertCursorAt expected
+ testCase "start of single line"
+ <| fun _ ->
+ let text = "$0Foo bar baz"
+ let expected = 0
+ text |> assertCursorAt expected
+ testCase "end of single word"
+ <| fun _ ->
+ let text = "foo$0"
+ // Note: out of string range: cursor is AFTER last character
+ let expected = 3
+ text |> assertCursorAt expected
+ testCase "end of single line"
+ <| fun _ ->
+ let text = "foo bar baz$0"
+ let expected = 11
+ text |> assertCursorAt expected
+ testCase "removes cursor marker from single line"
+ <| fun _ ->
+ let text = "foo $0bar"
+ let expected = "foo bar"
+ text |> assertTextIs expected ]
+
+ testList
+ "in triple quoted string"
+ [ testCase "in empty string unindented"
+ <| fun _ ->
+ // technically incorrect: contains `\n`
+ let text =
+ """
$0
"""
- let expected = 0
- text |> assertCursorAt expected
- testCase "in empty string indented" <| fun _ ->
- // technically incorrect: contains `\n`
- let text = """
+
+ let expected = 0
+ text |> assertCursorAt expected
+ testCase "in empty string indented"
+ <| fun _ ->
+ // technically incorrect: contains `\n`
+ let text =
+ """
$0
"""
- let expected = 0
- text |> assertCursorAt expected
- testCase "in F# code unindented" <| fun _ ->
- let text = """
+
+ let expected = 0
+ text |> assertCursorAt expected
+ testCase "in F# code unindented"
+ <| fun _ ->
+ let text =
+ """
module Foo
let $0a = 42
-let b =
+let b =
a + 5
printfn "Result=%i" b
"""
- let expected = 16
- text |> assertCursorAt expected
- testCase "in F# code indented" <| fun _ ->
- let text = """
+
+ let expected = 16
+ text |> assertCursorAt expected
+ testCase "in F# code indented"
+ <| fun _ ->
+ let text =
+ """
module Foo
let $0a = 42
- let b =
+ let b =
a + 5
printfn "Result=%i" b
"""
- let expected = 16
- text |> assertCursorAt expected
- testCase "removes cursor in F# code unindented" <| fun _ ->
- let text = """
+
+ let expected = 16
+ text |> assertCursorAt expected
+ testCase "removes cursor in F# code unindented"
+ <| fun _ ->
+ let text =
+ """
module Foo
let $0a = 42
-let b =
+let b =
a + 5
printfn "Result=%i" b
"""
- // expected isn't trimmed in assertXXX -> do manually
- let expected = !- """
+ // expected isn't trimmed in assertXXX -> do manually
+ let expected =
+ !- """
module Foo
let a = 42
-let b =
+let b =
a + 5
printfn "Result=%i" b
"""
- text |> assertTextIs expected
- testCase "removes cursor in F# code indented" <| fun _ ->
- let text = """
+
+ text |> assertTextIs expected
+ testCase "removes cursor in F# code indented"
+ <| fun _ ->
+ let text =
+ """
module Foo
let $0a = 42
- let b =
+ let b =
a + 5
printfn "Result=%i" b
"""
- let expected = !- """
+
+ let expected =
+ !- """
module Foo
let a = 42
-let b =
+let b =
a + 5
printfn "Result=%i" b
"""
- text |> assertTextIs expected
- testCase "finds and removes only first cursor" <| fun _ ->
- let text = """
+
+ text |> assertTextIs expected
+ testCase "finds and removes only first cursor"
+ <| fun _ ->
+ let text =
+ """
module Foo
let $0a = 42
- let $0b =
+ let $0b =
a + 5
printfn "Result=%i" b
"""
- let expected = !- """
+
+ let expected =
+ !- """
module Foo
let a = 42
-let $0b =
+let $0b =
a + 5
printfn "Result=%i" b
"""
- text |> assertTextIs expected
- ]
- ]
- ]
-
- let private tryExtractPositionMarkedWithAnyOfTests = testList (nameof Cursor.tryExtractPositionMarkedWithAnyOf) [
- testCase "exact first of many cursors" <| fun _ ->
- let text = "let $Avalue$B = $C42"
- let actual =
- text
- |> Cursor.tryExtractPositionMarkedWithAnyOf [|"$B"; "$C"; "$A"|]
- let expected = Some (("$A", pos 0 4), "let value$B = $C42")
-
- actual
- |> Expect.equal "should be correct marker" expected
- ]
-
- let private tryExtractPositionTests = testList (nameof Cursor.tryExtractPosition) [
- testList "no cursor" [
- let assertNoCursor =
- Cursor.tryExtractPosition
- >> Expect.isNone "should have found no cursor"
- testCase "empty string" <| fun _ ->
- let text = ""
- assertNoCursor text
- testCase "single line" <| fun _ ->
- let text = "Foo Bar Baz"
- assertNoCursor text
- testCase "two lines" <| fun _ ->
- let text = "Foo Bar Baz\nLorem ipsum dolor sit"
- assertNoCursor text
- testCase "multiple lines" <| fun _ ->
- let text = "Foo\nBar\nBaz\nLorem\nimpsum\ndolor\nsit"
- assertNoCursor text
- testCase "just spaces" <| fun _ ->
- let text = " "
- assertNoCursor text
- testCase "just spaces and new lines" <| fun _ ->
- let text = " \n \n\n \n\n\n\n \n \n \n"
- assertNoCursor text
- testCase "triple quoted string without processing" <| fun _ ->
- let text = """
-module Foo
-
-let a = 42
-let b =
+
+ text |> assertTextIs expected ] ] ]
+
+ let private tryExtractPositionMarkedWithAnyOfTests =
+ testList
+ (nameof Cursor.tryExtractPositionMarkedWithAnyOf)
+ [ testCase "exact first of many cursors"
+ <| fun _ ->
+ let text = "let $Avalue$B = $C42"
+ let actual = text |> Cursor.tryExtractPositionMarkedWithAnyOf [| "$B"; "$C"; "$A" |]
+ let expected = Some(("$A", pos 0 4), "let value$B = $C42")
+
+ actual |> Expect.equal "should be correct marker" expected ]
+
+ let private tryExtractPositionTests =
+ testList
+ (nameof Cursor.tryExtractPosition)
+ [ testList
+ "no cursor"
+ [ let assertNoCursor =
+ Cursor.tryExtractPosition >> Expect.isNone "should have found no cursor"
+
+ testCase "empty string"
+ <| fun _ ->
+ let text = ""
+ assertNoCursor text
+
+ testCase "single line"
+ <| fun _ ->
+ let text = "Foo Bar Baz"
+ assertNoCursor text
+
+ testCase "two lines"
+ <| fun _ ->
+ let text = "Foo Bar Baz\nLorem ipsum dolor sit"
+ assertNoCursor text
+
+ testCase "multiple lines"
+ <| fun _ ->
+ let text = "Foo\nBar\nBaz\nLorem\nimpsum\ndolor\nsit"
+ assertNoCursor text
+
+ testCase "just spaces"
+ <| fun _ ->
+ let text = " "
+ assertNoCursor text
+
+ testCase "just spaces and new lines"
+ <| fun _ ->
+ let text = " \n \n\n \n\n\n\n \n \n \n"
+ assertNoCursor text
+
+ testCase "triple quoted string without processing"
+ <| fun _ ->
+ let text =
+ """
+module Foo
+
+let a = 42
+let b =
a + 5
printfn "Result=%i" b
"""
- assertNoCursor text
- testCase "triple quoted string with processing (starting new line, no indentation)" <| fun _ ->
- let text = !- """
+
+ assertNoCursor text
+
+ testCase "triple quoted string with processing (starting new line, no indentation)"
+ <| fun _ ->
+ let text =
+ !- """
module Foo
let a = 42
-let b =
+let b =
a + 5
printfn "Result=%i" b
"""
- assertNoCursor text
- testCase "triple quoted string with processing (starting new line, indentation)" <| fun _ ->
- let text = !- """
+
+ assertNoCursor text
+
+ testCase "triple quoted string with processing (starting new line, indentation)"
+ <| fun _ ->
+ let text =
+ !- """
module Foo
let a = 42
- let b =
+ let b =
a + 5
printfn "Result=%i" b
"""
- assertNoCursor text
- ]
-
- testList "with cursor" [
- let assertAndGetCursor =
- Text.trimTripleQuotation
- >> Cursor.tryExtractPosition
- >> Option.defaultWith (fun _ -> failtest "No cursor found")
- let assertCursorAt (pos: Position) =
- assertAndGetCursor
- >> fst
- >> Expect.equal "should have found cursor at correct position" pos
- let assertTextIs (text: string) =
- assertAndGetCursor
- >> snd
- >> Expect.equal "should have correct text" text
- let assertResultIs (pos: Position, text: string) =
- assertAndGetCursor
- >> Expect.equal "should be correct cursor position and text" (pos, text)
-
- testList "in normal string" [
- testCase "in empty string" <| fun _ ->
- let text = "$0"
- let expected = pos 0 0
- text |> assertCursorAt expected
- testCase "start of single line" <| fun _ ->
- let text = "$0Foo bar baz"
- let expected = pos 0 0
- text |> assertCursorAt expected
- testCase "end of single word" <| fun _ ->
- let text = "foo$0"
- // Note: out of string range: cursor is AFTER last character
- let expected = pos 0 3
- text |> assertCursorAt expected
- testCase "end of single line" <| fun _ ->
- let text = "foo bar baz$0"
- let expected = pos 0 11
- text |> assertCursorAt expected
- testCase "removes cursor marker from single line" <| fun _ ->
- let text = "foo $0bar"
- let expected = "foo bar"
- text |> assertTextIs expected
- ]
- testList "in triple quoted string" [
- testCase "in empty string unindented" <| fun _ ->
- // technically incorrect: contains `\n`
- let text = """
+
+ assertNoCursor text ]
+
+ testList
+ "with cursor"
+ [ let assertAndGetCursor =
+ Text.trimTripleQuotation
+ >> Cursor.tryExtractPosition
+ >> Option.defaultWith (fun _ -> failtest "No cursor found")
+
+ let assertCursorAt (pos: Position) =
+ assertAndGetCursor
+ >> fst
+ >> Expect.equal "should have found cursor at correct position" pos
+
+ let assertTextIs (text: string) = assertAndGetCursor >> snd >> Expect.equal "should have correct text" text
+
+ let _assertResultIs (pos: Position, text: string) =
+ assertAndGetCursor
+ >> Expect.equal "should be correct cursor position and text" (pos, text)
+
+ testList
+ "in normal string"
+ [ testCase "in empty string"
+ <| fun _ ->
+ let text = "$0"
+ let expected = pos 0 0
+ text |> assertCursorAt expected
+ testCase "start of single line"
+ <| fun _ ->
+ let text = "$0Foo bar baz"
+ let expected = pos 0 0
+ text |> assertCursorAt expected
+ testCase "end of single word"
+ <| fun _ ->
+ let text = "foo$0"
+ // Note: out of string range: cursor is AFTER last character
+ let expected = pos 0 3
+ text |> assertCursorAt expected
+ testCase "end of single line"
+ <| fun _ ->
+ let text = "foo bar baz$0"
+ let expected = pos 0 11
+ text |> assertCursorAt expected
+ testCase "removes cursor marker from single line"
+ <| fun _ ->
+ let text = "foo $0bar"
+ let expected = "foo bar"
+ text |> assertTextIs expected ]
+
+ testList
+ "in triple quoted string"
+ [ testCase "in empty string unindented"
+ <| fun _ ->
+ // technically incorrect: contains `\n`
+ let text =
+ """
$0
"""
- let expected = pos 0 0
- text |> assertCursorAt expected
- testCase "in empty string indented" <| fun _ ->
- // technically incorrect: contains `\n`
- let text = """
+
+ let expected = pos 0 0
+ text |> assertCursorAt expected
+ testCase "in empty string indented"
+ <| fun _ ->
+ // technically incorrect: contains `\n`
+ let text =
+ """
$0
"""
- let expected = pos 0 0
- text |> assertCursorAt expected
- testCase "in F# code unindented" <| fun _ ->
- let text = """
+
+ let expected = pos 0 0
+ text |> assertCursorAt expected
+ testCase "in F# code unindented"
+ <| fun _ ->
+ let text =
+ """
module Foo
let $0a = 42
-let b =
+let b =
a + 5
printfn "Result=%i" b
"""
- let expected = pos 2 4 // 0-based, first line (with `"""`) is removed
- text |> assertCursorAt expected
- testCase "in F# code indented" <| fun _ ->
- let text = """
+
+ let expected = pos 2 4 // 0-based, first line (with `"""`) is removed
+ text |> assertCursorAt expected
+ testCase "in F# code indented"
+ <| fun _ ->
+ let text =
+ """
module Foo
let $0a = 42
- let b =
+ let b =
a + 5
printfn "Result=%i" b
"""
- let expected = pos 2 4 // 0-based, first line (with `"""`) is removed, leading indentation removed
- text |> assertCursorAt expected
- testCase "removes cursor in F# code unindented" <| fun _ ->
- let text = """
+
+ let expected = pos 2 4 // 0-based, first line (with `"""`) is removed, leading indentation removed
+ text |> assertCursorAt expected
+ testCase "removes cursor in F# code unindented"
+ <| fun _ ->
+ let text =
+ """
module Foo
let $0a = 42
-let b =
+let b =
a + 5
printfn "Result=%i" b
"""
- // expected isn't trimmed in assertXXX -> do manually
- let expected = !- """
+ // expected isn't trimmed in assertXXX -> do manually
+ let expected =
+ !- """
module Foo
let a = 42
-let b =
+let b =
a + 5
printfn "Result=%i" b
"""
- text |> assertTextIs expected
- testCase "removes cursor in F# code indented" <| fun _ ->
- let text = """
+
+ text |> assertTextIs expected
+ testCase "removes cursor in F# code indented"
+ <| fun _ ->
+ let text =
+ """
module Foo
let $0a = 42
- let b =
+ let b =
a + 5
printfn "Result=%i" b
"""
- let expected = !- """
+
+ let expected =
+ !- """
module Foo
let a = 42
-let b =
+let b =
a + 5
printfn "Result=%i" b
"""
- text |> assertTextIs expected
- testCase "finds and removes only first cursor" <| fun _ ->
- let text = """
+
+ text |> assertTextIs expected
+ testCase "finds and removes only first cursor"
+ <| fun _ ->
+ let text =
+ """
module Foo
let $0a = 42
- let $0b =
+ let $0b =
a + 5
printfn "Result=%i" b
"""
- let expected = !- """
+
+ let expected =
+ !- """
module Foo
let a = 42
-let $0b =
+let $0b =
a + 5
printfn "Result=%i" b
"""
- text |> assertTextIs expected
- ]
- ]
- ]
-
- let tryExtractRangeTests = testList (nameof Cursor.tryExtractRange) [
- let assertAndGetRange =
- Text.trimTripleQuotation
- >> Cursor.tryExtractRange
- >> Option.defaultWith (fun _ -> failtest "No cursor found")
- let assertRangeIs (range: Range) =
- assertAndGetRange
- >> fst
- >> Expect.equal "should have found correct range" range
- let assertTextIs (text: string) =
- assertAndGetRange
- >> snd
- >> Expect.equal "should have correct text" text
- let assertResultIs (range: Range, text: string) =
- assertAndGetRange
- >> Expect.equal "should be correct range and text" (range, text)
- testCase "no cursor results in no range" <| fun _ ->
- let text = !- """
-module Foo
-
-let a = 42
-let b =
+
+ text |> assertTextIs expected ] ] ]
+
+ let tryExtractRangeTests =
+ testList
+ (nameof Cursor.tryExtractRange)
+ [ let assertAndGetRange =
+ Text.trimTripleQuotation
+ >> Cursor.tryExtractRange
+ >> Option.defaultWith (fun _ -> failtest "No cursor found")
+
+ let assertRangeIs (range: Range) =
+ assertAndGetRange >> fst >> Expect.equal "should have found correct range" range
+
+ let assertTextIs (text: string) = assertAndGetRange >> snd >> Expect.equal "should have correct text" text
+
+ let assertResultIs (range: Range, text: string) =
+ assertAndGetRange
+ >> Expect.equal "should be correct range and text" (range, text)
+
+ testCase "no cursor results in no range"
+ <| fun _ ->
+ let text =
+ !- """
+module Foo
+
+let a = 42
+let b =
a + 5
printfn "Result=%i" b
"""
- text
- |> Cursor.tryExtractRange
- |> Expect.isNone "should have found no cursor"
- testCase "can extract range in same line" <| fun _ ->
- let text = """
+
+ text |> Cursor.tryExtractRange |> Expect.isNone "should have found no cursor"
+
+ testCase "can extract range in same line"
+ <| fun _ ->
+ let text =
+ """
module Foo
let $0a =$0 42
-let b =
+let b =
a + 5
printfn "Result=%i" b
"""
- let expected = { Start = pos 2 4; End = pos 2 7 }
- text |> assertRangeIs expected
- testCase "can extract range over multiple lines" <| fun _ ->
- let text = """
+
+ let expected = { Start = pos 2 4; End = pos 2 7 }
+ text |> assertRangeIs expected
+
+ testCase "can extract range over multiple lines"
+ <| fun _ ->
+ let text =
+ """
module Foo
let $0a = 42
-let b =
+let b =
a + 5
printfn "$0Result=%i" b
"""
- let expected = { Start = pos 2 4; End = pos 5 9 }
- text |> assertRangeIs expected
- testCase "can extract position" <| fun _ ->
- let text = """
+
+ let expected = { Start = pos 2 4; End = pos 5 9 }
+ text |> assertRangeIs expected
+
+ testCase "can extract position"
+ <| fun _ ->
+ let text =
+ """
module Foo
let a =$0 42
-let b =
+let b =
a + 5
printfn "Result=%i" b
"""
- let expected = { Start = pos 2 7; End = pos 2 7 }
- text |> assertRangeIs expected
- testCase "removes cursor markers from line" <| fun _ ->
- let text = """
+
+ let expected = { Start = pos 2 7; End = pos 2 7 }
+ text |> assertRangeIs expected
+
+ testCase "removes cursor markers from line"
+ <| fun _ ->
+ let text =
+ """
module Foo
let $0a = 42
-let b =
+let b =
a + 5
printfn "$0Result=%i" b
"""
- let expected = !- """
+
+ let expected =
+ !- """
module Foo
let a = 42
-let b =
+let b =
a + 5
printfn "Result=%i" b
"""
- text |> assertTextIs expected
- testCase "finds and removes only first range and its two markers" <| fun _ ->
- let text = """
+
+ text |> assertTextIs expected
+
+ testCase "finds and removes only first range and its two markers"
+ <| fun _ ->
+ let text =
+ """
module $0Foo
let a = $042
-let b =
+let b =
a + $05
printfn "$0Result$0=%i$0" b$0
"""
- let expectedRange = { Start = pos 0 7; End = pos 2 8 }
- let expectedText = !- """
+
+ let expectedRange = { Start = pos 0 7; End = pos 2 8 }
+
+ let expectedText =
+ !- """
module Foo
let a = 42
-let b =
+let b =
a + $05
printfn "$0Result$0=%i$0" b$0
"""
- text |> assertResultIs (expectedRange, expectedText)
- ]
-
- let beforeIndexTests = testList (nameof Cursor.beforeIndex) [
- let assertBeforeIndex expected textWithCursor =
- let textWithCursor = Text.trimTripleQuotation textWithCursor
- let idx = textWithCursor.IndexOf(Cursor.Marker, StringComparison.Ordinal)
- Expect.isGreaterThanOrEqual "Text has no cursor" (idx, 0)
- let text = textWithCursor.Remove(idx, Cursor.Marker.Length)
-
- text
- |> Cursor.beforeIndex idx
- |> Expect.equal "Should be correct position" expected
-
-
- testList "single line" [
- testCase "empty string" <| fun _ ->
- let text = ""
- let idx = 0
- let expected = pos 0 0
-
- text
- |> Cursor.beforeIndex idx
- |> Expect.equal "Position should be at start of string" expected
-
- testCase "empty string with cursor" <| fun _ ->
- let text = "$0"
- let expected = pos 0 0
- assertBeforeIndex expected text
-
- testCase "single line string - start" <| fun _ ->
- let text = "$0let foo = 42"
- let expected = pos 0 0
- assertBeforeIndex expected text
- testCase "single line string - middle" <| fun _ ->
- let text = "let foo $0= 42"
- let expected = pos 0 8
- assertBeforeIndex expected text
- testCase "single line string - end" <| fun _ ->
- let text = "let foo = 42$0"
- let expected = pos 0 12
- assertBeforeIndex expected text
- ]
- testList "multi line" [
- testCase "start of first line" <| fun _ ->
- let text = """
+
+ text |> assertResultIs (expectedRange, expectedText) ]
+
+ let beforeIndexTests =
+ testList
+ (nameof Cursor.beforeIndex)
+ [ let assertBeforeIndex expected textWithCursor =
+ let textWithCursor = Text.trimTripleQuotation textWithCursor
+ let idx = textWithCursor.IndexOf(Cursor.Marker, StringComparison.Ordinal)
+ Expect.isGreaterThanOrEqual "Text has no cursor" (idx, 0)
+ let text = textWithCursor.Remove(idx, Cursor.Marker.Length)
+
+ text
+ |> Cursor.beforeIndex idx
+ |> Expect.equal "Should be correct position" expected
+
+
+ testList
+ "single line"
+ [ testCase "empty string"
+ <| fun _ ->
+ let text = ""
+ let idx = 0
+ let expected = pos 0 0
+
+ text
+ |> Cursor.beforeIndex idx
+ |> Expect.equal "Position should be at start of string" expected
+
+ testCase "empty string with cursor"
+ <| fun _ ->
+ let text = "$0"
+ let expected = pos 0 0
+ assertBeforeIndex expected text
+
+ testCase "single line string - start"
+ <| fun _ ->
+ let text = "$0let foo = 42"
+ let expected = pos 0 0
+ assertBeforeIndex expected text
+ testCase "single line string - middle"
+ <| fun _ ->
+ let text = "let foo $0= 42"
+ let expected = pos 0 8
+ assertBeforeIndex expected text
+ testCase "single line string - end"
+ <| fun _ ->
+ let text = "let foo = 42$0"
+ let expected = pos 0 12
+ assertBeforeIndex expected text ]
+
+ testList
+ "multi line"
+ [ testCase "start of first line"
+ <| fun _ ->
+ let text =
+ """
$0module Foo
let a = 42
@@ -561,10 +711,13 @@ let b =
a + 5
printfn "Result=%i" b
"""
- let expected = pos 0 0
- assertBeforeIndex expected text
- testCase "middle of first line" <| fun _ ->
- let text = """
+
+ let expected = pos 0 0
+ assertBeforeIndex expected text
+ testCase "middle of first line"
+ <| fun _ ->
+ let text =
+ """
module $0Foo
let a = 42
@@ -572,10 +725,13 @@ let b =
a + 5
printfn "Result=%i" b
"""
- let expected = pos 0 7
- assertBeforeIndex expected text
- testCase "end of first line" <| fun _ ->
- let text = """
+
+ let expected = pos 0 7
+ assertBeforeIndex expected text
+ testCase "end of first line"
+ <| fun _ ->
+ let text =
+ """
module Foo$0
let a = 42
@@ -583,10 +739,13 @@ let b =
a + 5
printfn "Result=%i" b
"""
- let expected = pos 0 10
- assertBeforeIndex expected text
- testCase "start of 4th line" <| fun _ ->
- let text = """
+
+ let expected = pos 0 10
+ assertBeforeIndex expected text
+ testCase "start of 4th line"
+ <| fun _ ->
+ let text =
+ """
module Foo
let a = 42
@@ -594,10 +753,13 @@ $0let b =
a + 5
printfn "Result=%i" b
"""
- let expected = pos 3 0
- assertBeforeIndex expected text
- testCase "middle of 4th line" <| fun _ ->
- let text = """
+
+ let expected = pos 3 0
+ assertBeforeIndex expected text
+ testCase "middle of 4th line"
+ <| fun _ ->
+ let text =
+ """
module Foo
let a = 42
@@ -605,10 +767,13 @@ let $0b =
a + 5
printfn "Result=%i" b
"""
- let expected = pos 3 4
- assertBeforeIndex expected text
- testCase "end of 4th line" <| fun _ ->
- let text = """
+
+ let expected = pos 3 4
+ assertBeforeIndex expected text
+ testCase "end of 4th line"
+ <| fun _ ->
+ let text =
+ """
module Foo
let a = 42
@@ -616,213 +781,279 @@ let b =$0
a + 5
printfn "Result=%i" b
"""
- let expected = pos 3 7
- assertBeforeIndex expected text
- testCase "start of last line" <| fun _ ->
- let text = """
+
+ let expected = pos 3 7
+ assertBeforeIndex expected text
+ testCase "start of last line"
+ <| fun _ ->
+ let text =
+ """
module Foo
let a = 42
let b =
a + 5
$0printfn "Result=%i" b"""
- let expected = pos 5 0
- assertBeforeIndex expected text
- testCase "middle of last line" <| fun _ ->
- let text = """
+
+ let expected = pos 5 0
+ assertBeforeIndex expected text
+ testCase "middle of last line"
+ <| fun _ ->
+ let text =
+ """
module Foo
let a = 42
let b =
a + 5
printfn "$0Result=%i" b"""
- let expected = pos 5 9
- assertBeforeIndex expected text
- testCase "end of last line" <| fun _ ->
- let text = """
+
+ let expected = pos 5 9
+ assertBeforeIndex expected text
+ testCase "end of last line"
+ <| fun _ ->
+ let text =
+ """
module Foo
let a = 42
let b =
a + 5
printfn "Result=%i" b$0"""
- let expected = pos 5 21
- assertBeforeIndex expected text
- ]
- ]
-
- let tryIndexOfTests = testList (nameof Cursor.tryIndexOf) [
- let assertAndGetTextAndCursor =
- Text.trimTripleQuotation
- >> Cursor.tryExtractPosition
- >> Option.defaultWith (fun _ -> failtest "should have found cursor")
- let indexOf =
- assertAndGetTextAndCursor
- >> fun (pos, text) -> Cursor.tryIndexOf pos text
- let assertAndGetIndexOf =
- indexOf
- >> function
- | Ok i -> i
- | Result.Error msg -> failtest $"should have found index. But was error: {msg}"
- let assertIndexOf expectedIndex =
- assertAndGetIndexOf
- >> Expect.equal "wrong index" expectedIndex
- let assertIndexOf expectedIndex textWithCursor =
- let (pos, text) = assertAndGetTextAndCursor textWithCursor
- match Cursor.tryIndexOf pos text with
- | Ok actualIndex ->
- let idxInText = textWithCursor.IndexOf(Cursor.Marker, StringComparison.Ordinal)
- let errorMsg = $"wrong index. Cursor at Postion={{Line={pos.Line};Char={pos.Character}}} or Index={idxInText}"
- Expect.equal errorMsg expectedIndex actualIndex
- | Result.Error msg -> failtest $"should have found index. But was error: {msg}"
- let assertNoIndexAt pos =
- Text.trimTripleQuotation
- >> Cursor.tryIndexOf pos
- >> function
- | Ok i -> failtest $"Expected Error, but was OK with index {i}"
- | Result.Error _ -> ()
-
- testList "empty string" [
- testCase "inside" <| fun _ ->
- let text = "$0"
- let expected = 0
- text |> assertIndexOf expected
- testCase "out of char range in empty string" <| fun _ ->
- let text = ""
- let pos = pos 0 1
- text |> assertNoIndexAt pos
- testCase "out of line range in empty string" <| fun _ ->
- let text = ""
- let pos = pos 1 0
- text |> assertNoIndexAt pos
- ]
-
- testList "single line" [
- testCase "out of char range" <| fun _ ->
- let text = "foo bar baz"
- let pos = pos 0 (11 + 1)
- text |> assertNoIndexAt pos
- testCase "out of line range" <| fun _ ->
- let text = "foo bar baz"
- let pos = pos 1 0
- text |> assertNoIndexAt pos
- testCase "start" <| fun _ ->
- let text = "$0foo bar baz"
- let expected = 0
- text |> assertIndexOf expected
- testCase "middle" <| fun _ ->
- let text = "foo b$0ar baz"
- let expected = 5
- text |> assertIndexOf expected
- testCase "end" <| fun _ ->
- let text = "foo bar baz$0"
- let expected = 11
- text |> assertIndexOf expected
- ]
-
- testList "two lines" [
- testCase "start of 1st line" <| fun _ ->
- // chars: 11 + `\n` + 17
- let text = "$0foo bar baz\nlorem ipsum dolor"
- let expected = 0
- text |> assertIndexOf expected
- testCase "middle of 1st line" <| fun _ ->
- let text = "foo b$0ar baz\nlorem ipsum dolor"
- let expected = 5
- text |> assertIndexOf expected
- testCase "end of 1st line" <| fun _ ->
- let text = "foo bar baz$0\nlorem ipsum dolor"
- let expected = 10 (*1st line; 0-based*) + 1 (*\n*) // on `\n`; 10: Index is 0-based: string with length=11 -> max index = 10
- text |> assertIndexOf expected
- testCase "start of 2nd line" <| fun _ ->
- let text = "foo bar baz\n$0lorem ipsum dolor"
- let expected = 10 (*1st line; 0-based*) + 1 (*\n*) + 0 (*2nd line*) + 1 (*index after cursor*)
- text |> assertIndexOf expected
- testCase "middle of 2nd line" <| fun _ ->
- let text = "foo bar baz\nlorem ip$0sum dolor"
- let expected = 10 (*1st line; 0-based*) + 1 (*\n*) + 8 (*2nd line*) + 1 (*index after cursor*)
- text |> assertIndexOf expected
- testCase "end of 2nd line" <| fun _ ->
- let text = "foo bar baz\nlorem ipsum dolor$0"
- let expected = 10 (*1st line; 0-based*) + 1 (*\n*) + 17 (*2nd line*) + 1 (*index afrer cursor*)
- text |> assertIndexOf expected
- testCase "out of char range in 1st line" <| fun _ ->
- let text = "foo bar baz\nlorem ipsum dolor"
- let pos = pos 0 (11 + 1)
- text |> assertNoIndexAt pos
- testCase "out of char range in 2nd line" <| fun _ ->
- let text = "foo bar baz\nlorem ipsum dolor"
- let pos = pos 1 (17 + 1)
- text |> assertNoIndexAt pos
- testCase "out of line range" <| fun _ ->
- let text = "foo bar baz\nlorem ipsum dolor"
- let pos = pos 2 0
- text |> assertNoIndexAt pos
- ]
-
- testList "F# code" [
- testCase "start of text" <| fun _ ->
- let text = """
+
+ let expected = pos 5 21
+ assertBeforeIndex expected text ] ]
+
+ let tryIndexOfTests =
+ testList
+ (nameof Cursor.tryIndexOf)
+ [ let assertAndGetTextAndCursor =
+ Text.trimTripleQuotation
+ >> Cursor.tryExtractPosition
+ >> Option.defaultWith (fun _ -> failtest "should have found cursor")
+
+ let indexOf =
+ assertAndGetTextAndCursor >> fun (pos, text) -> Cursor.tryIndexOf pos text
+
+ let assertAndGetIndexOf =
+ indexOf
+ >> function
+ | Ok i -> i
+ | Result.Error msg -> failtest $"should have found index. But was error: {msg}"
+
+ let _assertIndexOf expectedIndex = assertAndGetIndexOf >> Expect.equal "wrong index" expectedIndex
+
+ let assertIndexOf expectedIndex textWithCursor =
+ let (pos, text) = assertAndGetTextAndCursor textWithCursor
+
+ match Cursor.tryIndexOf pos text with
+ | Ok actualIndex ->
+ let idxInText = textWithCursor.IndexOf(Cursor.Marker, StringComparison.Ordinal)
+
+ let errorMsg =
+ $"wrong index. Cursor at Postion={{Line={pos.Line};Char={pos.Character}}} or Index={idxInText}"
+
+ Expect.equal errorMsg expectedIndex actualIndex
+ | Result.Error msg -> failtest $"should have found index. But was error: {msg}"
+
+ let assertNoIndexAt pos =
+ Text.trimTripleQuotation
+ >> Cursor.tryIndexOf pos
+ >> function
+ | Ok i -> failtest $"Expected Error, but was OK with index {i}"
+ | Result.Error _ -> ()
+
+ testList
+ "empty string"
+ [ testCase "inside"
+ <| fun _ ->
+ let text = "$0"
+ let expected = 0
+ text |> assertIndexOf expected
+ testCase "out of char range in empty string"
+ <| fun _ ->
+ let text = ""
+ let pos = pos 0 1
+ text |> assertNoIndexAt pos
+ testCase "out of line range in empty string"
+ <| fun _ ->
+ let text = ""
+ let pos = pos 1 0
+ text |> assertNoIndexAt pos ]
+
+ testList
+ "single line"
+ [ testCase "out of char range"
+ <| fun _ ->
+ let text = "foo bar baz"
+ let pos = pos 0 (11 + 1)
+ text |> assertNoIndexAt pos
+ testCase "out of line range"
+ <| fun _ ->
+ let text = "foo bar baz"
+ let pos = pos 1 0
+ text |> assertNoIndexAt pos
+ testCase "start"
+ <| fun _ ->
+ let text = "$0foo bar baz"
+ let expected = 0
+ text |> assertIndexOf expected
+ testCase "middle"
+ <| fun _ ->
+ let text = "foo b$0ar baz"
+ let expected = 5
+ text |> assertIndexOf expected
+ testCase "end"
+ <| fun _ ->
+ let text = "foo bar baz$0"
+ let expected = 11
+ text |> assertIndexOf expected ]
+
+ testList
+ "two lines"
+ [ testCase "start of 1st line"
+ <| fun _ ->
+ // chars: 11 + `\n` + 17
+ let text = "$0foo bar baz\nlorem ipsum dolor"
+ let expected = 0
+ text |> assertIndexOf expected
+ testCase "middle of 1st line"
+ <| fun _ ->
+ let text = "foo b$0ar baz\nlorem ipsum dolor"
+ let expected = 5
+ text |> assertIndexOf expected
+ testCase "end of 1st line"
+ <| fun _ ->
+ let text = "foo bar baz$0\nlorem ipsum dolor"
+ let expected = 10 (*1st line; 0-based*) + 1 (*\n*) // on `\n`; 10: Index is 0-based: string with length=11 -> max index = 10
+ text |> assertIndexOf expected
+ testCase "start of 2nd line"
+ <| fun _ ->
+ let text = "foo bar baz\n$0lorem ipsum dolor"
+
+ let expected =
+ 10 (*1st line; 0-based*) + 1 (*\n*) + 0 (*2nd line*) + 1 (*index after cursor*)
+
+ text |> assertIndexOf expected
+ testCase "middle of 2nd line"
+ <| fun _ ->
+ let text = "foo bar baz\nlorem ip$0sum dolor"
+
+ let expected =
+ 10 (*1st line; 0-based*) + 1 (*\n*) + 8 (*2nd line*) + 1 (*index after cursor*)
+
+ text |> assertIndexOf expected
+ testCase "end of 2nd line"
+ <| fun _ ->
+ let text = "foo bar baz\nlorem ipsum dolor$0"
+
+ let expected =
+ 10 (*1st line; 0-based*)
+ + 1 (*\n*)
+ + 17 (*2nd line*)
+ + 1 (*index afrer cursor*)
+
+ text |> assertIndexOf expected
+ testCase "out of char range in 1st line"
+ <| fun _ ->
+ let text = "foo bar baz\nlorem ipsum dolor"
+ let pos = pos 0 (11 + 1)
+ text |> assertNoIndexAt pos
+ testCase "out of char range in 2nd line"
+ <| fun _ ->
+ let text = "foo bar baz\nlorem ipsum dolor"
+ let pos = pos 1 (17 + 1)
+ text |> assertNoIndexAt pos
+ testCase "out of line range"
+ <| fun _ ->
+ let text = "foo bar baz\nlorem ipsum dolor"
+ let pos = pos 2 0
+ text |> assertNoIndexAt pos ]
+
+ testList
+ "F# code"
+ [ testCase "start of text"
+ <| fun _ ->
+ let text =
+ """
$0module Foo
let a = 42
-let b =
+let b =
a + 5
printfn "Result=%i" b
"""
- text |> assertIndexOf 0
- testCase "end of text" <| fun _ ->
- let text = """
+
+ text |> assertIndexOf 0
+ testCase "end of text"
+ <| fun _ ->
+ let text =
+ """
module Foo
let a = 42
-let b =
+let b =
a + 5
printfn "Result=%i" b
$0"""
- text |> assertIndexOf 62
- testCase "middle of 1st line" <| fun _ ->
- let text = """
+
+ text |> assertIndexOf 61
+ testCase "middle of 1st line"
+ <| fun _ ->
+ let text =
+ """
module$0 Foo
let a = 42
-let b =
+let b =
a + 5
printfn "Result=%i" b
"""
- text |> assertIndexOf 6
- testCase "end of 1st line" <| fun _ ->
- let text = """
+
+ text |> assertIndexOf 6
+ testCase "end of 1st line"
+ <| fun _ ->
+ let text =
+ """
module Foo$0
let a = 42
-let b =
+let b =
a + 5
printfn "Result=%i" b
"""
- text |> assertIndexOf 10
- testCase "start of 4th line" <| fun _ ->
- let text = """
+
+ text |> assertIndexOf 10
+ testCase "start of 4th line"
+ <| fun _ ->
+ let text =
+ """
module Foo
let a = 42
-$0let b =
+$0let b =
a + 5
printfn "Result=%i" b
"""
- text |> assertIndexOf 23
- testCase "middle of 4th line" <| fun _ ->
- let text = """
+
+ text |> assertIndexOf 23
+ testCase "middle of 4th line"
+ <| fun _ ->
+ let text =
+ """
module Foo
let a = 42
-let $0b =
+let $0b =
a + 5
printfn "Result=%i" b
"""
- text |> assertIndexOf 27
- testCase "end of 4th line" <| fun _ ->
- let text = """
+
+ text |> assertIndexOf 27
+ testCase "end of 4th line"
+ <| fun _ ->
+ let text =
+ """
module Foo
let a = 42
@@ -830,57 +1061,70 @@ let b = $0
a + 5
printfn "Result=%i" b
"""
- text |> assertIndexOf 31
- ]
- ]
-
- let identityTests = testList "identities" [
- // * idx |> beforeIndex |> tryIndexOf = idx
- // * pos |> tryIndexOf |> beforeIndex = pos
- // * tryExtractIndex >> beforeIndex = tryExtractPosition
- // * tryExtractPosition >> tryIndexOf = tryExtractIndex
-
- // assert: `lhs = rhs`
- let assertEquality lhs rhs textWithCursor =
- let textWithCursor = textWithCursor |> Text.trimTripleQuotation
- let lhs = textWithCursor |> lhs
- let rhs = textWithCursor |> rhs
- Expect.equal "Should hold: lhs = rhs (expected = actual)" lhs rhs
- let testEquality lhs rhs name textWithCursor =
- testCase name <| fun _ -> assertEquality lhs rhs textWithCursor
- let testEqualityForAllCursors lhs rhs textWithCursors =
- textWithCursors
- |> Text.trimTripleQuotation
- |> Cursors.iter
- |> List.mapi (fun i t -> testEquality lhs rhs $"Cursor {i}" t)
-
- /// assert: `value |> roundTrip = value`
- let assertThereAndBackAgain value roundTrip textWithCursor =
- let textWithCursor = textWithCursor |> Text.trimTripleQuotation
- let (value,text) = textWithCursor |> value
- let roundTripped = (value, text) ||> roundTrip
- Expect.equal "Should hold: value |> roundTrip = value (expected |> roundTrip = actual)" value roundTripped
- let testThereAndBackAgain value roundTrip name textWithCursor =
- testCase name <| fun _ -> assertThereAndBackAgain value roundTrip textWithCursor
- let testThereAndBackAgainForAllCursors value roundTrip textWithCursors =
- textWithCursors
- |> Text.trimTripleQuotation
- |> Cursors.iter
- |> List.mapi (fun i -> testThereAndBackAgain value roundTrip $"Cursor {i}")
-
- testList "idx |> beforeIndex |> tryIndexOf = idx" [
- let value (textWithCursor: string) =
- let idx = textWithCursor.IndexOf(Cursor.Marker, StringComparison.Ordinal)
- if idx < 0 then
- failtest "No cursor"
- let text = textWithCursor.Replace(Cursor.Marker, "")
- (idx, text)
- let roundTrip idx text =
- let pos = Cursor.beforeIndex idx text
- Cursor.tryIndexOf pos text
- |> Result.defaultWith (fun error -> failtest $"Error while IndexOf: {error}")
- testList "F# Code 1" (
- let text = """
+
+ text |> assertIndexOf 31 ] ]
+
+ let identityTests =
+ testList
+ "identities"
+ [
+ // * idx |> beforeIndex |> tryIndexOf = idx
+ // * pos |> tryIndexOf |> beforeIndex = pos
+ // * tryExtractIndex >> beforeIndex = tryExtractPosition
+ // * tryExtractPosition >> tryIndexOf = tryExtractIndex
+
+ // assert: `lhs = rhs`
+ let assertEquality lhs rhs textWithCursor =
+ let textWithCursor = textWithCursor |> Text.trimTripleQuotation
+ let lhs = textWithCursor |> lhs
+ let rhs = textWithCursor |> rhs
+ Expect.equal "Should hold: lhs = rhs (expected = actual)" lhs rhs
+
+ let testEquality lhs rhs name textWithCursor = testCase name <| fun _ -> assertEquality lhs rhs textWithCursor
+
+ let testEqualityForAllCursors lhs rhs textWithCursors =
+ textWithCursors
+ |> Text.trimTripleQuotation
+ |> Cursors.iter
+ |> List.mapi (fun i t -> testEquality lhs rhs $"Cursor {i}" t)
+
+ /// assert: `value |> roundTrip = value`
+ let assertThereAndBackAgain value roundTrip textWithCursor =
+ let textWithCursor = textWithCursor |> Text.trimTripleQuotation
+ let (value, text) = textWithCursor |> value
+ let roundTripped = (value, text) ||> roundTrip
+ Expect.equal "Should hold: value |> roundTrip = value (expected |> roundTrip = actual)" value roundTripped
+
+ let testThereAndBackAgain value roundTrip name textWithCursor =
+ testCase name <| fun _ -> assertThereAndBackAgain value roundTrip textWithCursor
+
+ let testThereAndBackAgainForAllCursors value roundTrip textWithCursors =
+ textWithCursors
+ |> Text.trimTripleQuotation
+ |> Cursors.iter
+ |> List.mapi (fun i -> testThereAndBackAgain value roundTrip $"Cursor {i}")
+
+ testList
+ "idx |> beforeIndex |> tryIndexOf = idx"
+ [ let value (textWithCursor: string) =
+ let idx = textWithCursor.IndexOf(Cursor.Marker, StringComparison.Ordinal)
+
+ if idx < 0 then
+ failtest "No cursor"
+
+ let text = textWithCursor.Replace(Cursor.Marker, "")
+ (idx, text)
+
+ let roundTrip idx text =
+ let pos = Cursor.beforeIndex idx text
+
+ Cursor.tryIndexOf pos text
+ |> Result.defaultWith (fun error -> failtest $"Error while IndexOf: {error}")
+
+ testList
+ "F# Code 1"
+ (let text =
+ """
$0module$0 Foo$0
$0let $0a = 42$0
@@ -888,22 +1132,28 @@ let b =
$0a $0+ 5$0
$0printfn "$0Result=%i" b$0
"""
- text |> testThereAndBackAgainForAllCursors value roundTrip
- )
- ]
- testList "pos |> tryIndexOf |> beforeIndex = pos" [
- let value (textWithCursor: string) =
- textWithCursor
- |> Cursor.tryExtractPosition
- |> Option.defaultWith (fun _ -> failtest "No cursor")
- let roundTrip pos text =
- let idx =
- text
- |> Cursor.tryIndexOf pos
- |> Result.defaultWith (fun error -> failtest $"Error while IndexOf: {error}")
- Cursor.beforeIndex idx text
- testList "F# Code 1" (
- let text = """
+
+ text |> testThereAndBackAgainForAllCursors value roundTrip) ]
+
+ testList
+ "pos |> tryIndexOf |> beforeIndex = pos"
+ [ let value (textWithCursor: string) =
+ textWithCursor
+ |> Cursor.tryExtractPosition
+ |> Option.defaultWith (fun _ -> failtest "No cursor")
+
+ let roundTrip pos text =
+ let idx =
+ text
+ |> Cursor.tryIndexOf pos
+ |> Result.defaultWith (fun error -> failtest $"Error while IndexOf: {error}")
+
+ Cursor.beforeIndex idx text
+
+ testList
+ "F# Code 1"
+ (let text =
+ """
$0module$0 Foo$0
$0let $0a = 42$0
@@ -911,20 +1161,25 @@ let b =
$0a $0+ 5$0
$0printfn "$0Result=%i" b$0
"""
- text |> testThereAndBackAgainForAllCursors value roundTrip
- )
- ]
- testList "tryExtractIndex >> beforeIndex = tryExtractPosition" [
- let lhs =
- Cursor.tryExtractIndex
- >> Option.defaultWith (fun _ -> failtest "No cursor")
- >> fun (idx, text) -> Cursor.beforeIndex idx text
- let rhs =
- Cursor.tryExtractPosition
- >> Option.defaultWith (fun _ -> failtest "No cursor")
- >> fst
- testList "F# Code 1" (
- let text = """
+
+ text |> testThereAndBackAgainForAllCursors value roundTrip) ]
+
+ testList
+ "tryExtractIndex >> beforeIndex = tryExtractPosition"
+ [ let lhs =
+ Cursor.tryExtractIndex
+ >> Option.defaultWith (fun _ -> failtest "No cursor")
+ >> fun (idx, text) -> Cursor.beforeIndex idx text
+
+ let rhs =
+ Cursor.tryExtractPosition
+ >> Option.defaultWith (fun _ -> failtest "No cursor")
+ >> fst
+
+ testList
+ "F# Code 1"
+ (let text =
+ """
$0module$0 Foo$0
$0let $0a = 42$0
@@ -932,21 +1187,26 @@ let b =
$0a $0+ 5$0
$0printfn "$0Result=%i" b$0
"""
- text |> testEqualityForAllCursors lhs rhs
- )
- ]
- testList "tryExtractPosition >> tryIndexOf = tryExtractIndex" [
- let lhs =
- Cursor.tryExtractPosition
- >> Option.defaultWith (fun _ -> failtest "No cursor")
- >> fun (pos, text) -> Cursor.tryIndexOf pos text
- >> Result.defaultWith (fun error -> failtest $"No index: {error}")
- let rhs =
- Cursor.tryExtractIndex
- >> Option.defaultWith (fun _ -> failtest "No cursor")
- >> fst
- testList "F# Code 1" (
- let text = """
+
+ text |> testEqualityForAllCursors lhs rhs) ]
+
+ testList
+ "tryExtractPosition >> tryIndexOf = tryExtractIndex"
+ [ let lhs =
+ Cursor.tryExtractPosition
+ >> Option.defaultWith (fun _ -> failtest "No cursor")
+ >> fun (pos, text) -> Cursor.tryIndexOf pos text
+ >> Result.defaultWith (fun error -> failtest $"No index: {error}")
+
+ let rhs =
+ Cursor.tryExtractIndex
+ >> Option.defaultWith (fun _ -> failtest "No cursor")
+ >> fst
+
+ testList
+ "F# Code 1"
+ (let text =
+ """
$0module$0 Foo$0
$0let $0a = 42$0
@@ -954,213 +1214,218 @@ let b =
$0a $0+ 5$0
$0printfn "$0Result=%i" b$0
"""
- text |> testEqualityForAllCursors lhs rhs
- )
- ]
- ]
-
- let afterEditsTests = testList (nameof Cursor.afterEdits) [
- testCase "doesn't move cursor when insert after cursor in different line" <| fun _ ->
- let cursor = pos 1 2
- let edits = [
- {
- Range = posRange (pos 2 5)
- NewText = "foo bar"
- }
- ]
- cursor
- |> Cursor.afterEdits edits
- |> Expect.equal "Cursor should not move" cursor
- testCase "doesn't move cursor when remove after cursor in different line" <| fun _ ->
- let cursor = pos 1 2
- let edits = [
- {
- Range = range (pos 2 5) (pos 3 4)
- NewText = ""
- }
- ]
- cursor
- |> Cursor.afterEdits edits
- |> Expect.equal "Cursor should not move" cursor
- testCase "doesn't move cursor when replace after cursor in different line" <| fun _ ->
- let cursor = pos 1 2
- let edits = [
- {
- Range = range (pos 2 5) (pos 3 4)
- NewText = "foo bar"
- }
- ]
- cursor
- |> Cursor.afterEdits edits
- |> Expect.equal "Cursor should not move" cursor
-
- testCase "doesn't move cursor when insert before cursor in different line and just inside line" <| fun _ ->
- let cursor = pos 2 2
- let edits = [
- {
- Range = posRange (pos 1 5)
- NewText = "foo bar"
- }
- ]
- cursor
- |> Cursor.afterEdits edits
- |> Expect.equal "Cursor should not move" cursor
- testCase "doesn't move cursor when remove before cursor in different line and just inside line" <| fun _ ->
- let cursor = pos 2 2
- let edits = [
- {
- Range = range (pos 1 5) (pos 1 7)
- NewText = ""
- }
- ]
- cursor
- |> Cursor.afterEdits edits
- |> Expect.equal "Cursor should not move" cursor
- testCase "doesn't move cursor when replace before cursor in different line and just inside line" <| fun _ ->
- let cursor = pos 2 2
- let edits = [
- {
- Range = range (pos 1 5) (pos 1 7)
- NewText = "foo bar"
- }
- ]
- cursor
- |> Cursor.afterEdits edits
- |> Expect.equal "Cursor should not move" cursor
-
- testCase "moves cursor down a line when inserting new line before cursor in different line" <| fun _ ->
- let cursor = pos 2 2
- let edits = [
- {
- Range = posRange (pos 1 5)
- NewText = "foo\nbar"
- }
- ]
- cursor
- |> Cursor.afterEdits edits
- |> Expect.equal "Cursor should move down a line" { cursor with Line = cursor.Line + 1 }
- testCase "moves cursor up a line when removing line before cursor in different line" <| fun _ ->
- let cursor = pos 3 2
- let edits = [
- {
- Range = range (pos 1 5) (pos 2 4)
- NewText = ""
- }
- ]
- cursor
- |> Cursor.afterEdits edits
- |> Expect.equal "Cursor should move up a line" { cursor with Line = cursor.Line - 1 }
-
- testCase "moves cursor up a line when removing a line and inserting inside line before cursor in different line" <| fun _ ->
- let cursor = pos 3 2
- let edits = [
- {
- Range = range (pos 1 5) (pos 2 4)
- NewText = "foo bar"
- }
- ]
- cursor
- |> Cursor.afterEdits edits
- |> Expect.equal "Cursor should move up a line" { cursor with Line = cursor.Line - 1 }
- testCase "doesn't move cursor when removing a line and inserting a line before cursor in different line" <| fun _ ->
- let cursor = pos 3 2
- let edits = [
- {
- Range = range (pos 1 5) (pos 2 4)
- NewText = "foo\nbar"
- }
- ]
- cursor
- |> Cursor.afterEdits edits
- |> Expect.equal "Cursor should not move" cursor
- testCase "moves cursor down when removing a line and inserting two lines before cursor in different line" <| fun _ ->
- let cursor = pos 3 2
- let edits = [
- {
- Range = range (pos 1 5) (pos 2 4)
- NewText = "foo\nbar\nbaz"
- }
- ]
- cursor
- |> Cursor.afterEdits edits
- |> Expect.equal "Cursor should move down a line" { cursor with Line = cursor.Line + 1 }
-
- testCase "moves cursor back when inserting inside same line in front of cursor" <| fun _ ->
- let cursor = pos 3 2
- let edits = [
- {
- Range = posRange (pos 3 1)
- NewText = "foo"
- }
- ]
- cursor
- |> Cursor.afterEdits edits
- |> Expect.equal "Cursor should move back" { cursor with Character = cursor.Character + 3 }
- testCase "moves cursor forward when deleting inside same line in front of cursor" <| fun _ ->
- let cursor = pos 3 7
- let edits = [
- {
- Range = range (pos 3 2) (pos 3 5)
- NewText = ""
- }
- ]
- cursor
- |> Cursor.afterEdits edits
- |> Expect.equal "Cursor should move forward" { cursor with Character = 4 }
-
- testCase "moves cursor forward and up when deleting inside and pre same line in front of cursor" <| fun _ ->
- let cursor = pos 3 7
- let edits = [
- {
- Range = range (pos 2 2) (pos 3 5)
- NewText = ""
- }
- ]
- cursor
- |> Cursor.afterEdits edits
- |> Expect.equal "Cursor should move forward and up" (pos 2 4)
-
-
- testCase "moves cursor to front of delete when cursor inside" <| fun _ ->
- let cursor = pos 3 7
- let edits = [
- {
- Range = range (pos 2 2) (pos 3 10)
- NewText = ""
- }
- ]
- cursor
- |> Cursor.afterEdits edits
- |> Expect.equal "Cursor should move to start of delete" (pos 2 2)
-
- testCase "cursor stays when insert at cursor position" <| fun _ ->
- let cursor = pos 2 5
- let edits = [
- {
- Range = posRange (pos 2 5)
- NewText = "foo bar"
- }
- ]
- cursor
- |> Cursor.afterEdits edits
- |> Expect.equal "Cursor should move to start of delete" cursor
-
- testCase "cursor moves to front when replacement with cursor inside" <| fun _ ->
- let cursor = pos 3 7
- let edits = [
- {
- Range = range (pos 2 3) (pos 5 2)
- NewText = "foo bar"
- }
- ]
- cursor
- |> Cursor.afterEdits edits
- |> Expect.equal "Cursor should move to start of delete" { Line = 2; Character = 3 }
-
- testList "multiple edits" [
- let data = lazy (
- let textWithCursors =
- """
+
+ text |> testEqualityForAllCursors lhs rhs) ] ]
+
+ let afterEditsTests =
+ testList
+ (nameof Cursor.afterEdits)
+ [ testCase "doesn't move cursor when insert after cursor in different line"
+ <| fun _ ->
+ let cursor = pos 1 2
+
+ let edits =
+ [ { Range = posRange (pos 2 5)
+ NewText = "foo bar" } ]
+
+ cursor
+ |> Cursor.afterEdits edits
+ |> Expect.equal "Cursor should not move" cursor
+ testCase "doesn't move cursor when remove after cursor in different line"
+ <| fun _ ->
+ let cursor = pos 1 2
+
+ let edits =
+ [ { Range = range (pos 2 5) (pos 3 4)
+ NewText = "" } ]
+
+ cursor
+ |> Cursor.afterEdits edits
+ |> Expect.equal "Cursor should not move" cursor
+ testCase "doesn't move cursor when replace after cursor in different line"
+ <| fun _ ->
+ let cursor = pos 1 2
+
+ let edits =
+ [ { Range = range (pos 2 5) (pos 3 4)
+ NewText = "foo bar" } ]
+
+ cursor
+ |> Cursor.afterEdits edits
+ |> Expect.equal "Cursor should not move" cursor
+
+ testCase "doesn't move cursor when insert before cursor in different line and just inside line"
+ <| fun _ ->
+ let cursor = pos 2 2
+
+ let edits =
+ [ { Range = posRange (pos 1 5)
+ NewText = "foo bar" } ]
+
+ cursor
+ |> Cursor.afterEdits edits
+ |> Expect.equal "Cursor should not move" cursor
+ testCase "doesn't move cursor when remove before cursor in different line and just inside line"
+ <| fun _ ->
+ let cursor = pos 2 2
+
+ let edits =
+ [ { Range = range (pos 1 5) (pos 1 7)
+ NewText = "" } ]
+
+ cursor
+ |> Cursor.afterEdits edits
+ |> Expect.equal "Cursor should not move" cursor
+ testCase "doesn't move cursor when replace before cursor in different line and just inside line"
+ <| fun _ ->
+ let cursor = pos 2 2
+
+ let edits =
+ [ { Range = range (pos 1 5) (pos 1 7)
+ NewText = "foo bar" } ]
+
+ cursor
+ |> Cursor.afterEdits edits
+ |> Expect.equal "Cursor should not move" cursor
+
+ testCase "moves cursor down a line when inserting new line before cursor in different line"
+ <| fun _ ->
+ let cursor = pos 2 2
+
+ let edits =
+ [ { Range = posRange (pos 1 5)
+ NewText = "foo\nbar" } ]
+
+ cursor
+ |> Cursor.afterEdits edits
+ |> Expect.equal "Cursor should move down a line" { cursor with Line = cursor.Line + 1 }
+ testCase "moves cursor up a line when removing line before cursor in different line"
+ <| fun _ ->
+ let cursor = pos 3 2
+
+ let edits =
+ [ { Range = range (pos 1 5) (pos 2 4)
+ NewText = "" } ]
+
+ cursor
+ |> Cursor.afterEdits edits
+ |> Expect.equal "Cursor should move up a line" { cursor with Line = cursor.Line - 1 }
+
+ testCase "moves cursor up a line when removing a line and inserting inside line before cursor in different line"
+ <| fun _ ->
+ let cursor = pos 3 2
+
+ let edits =
+ [ { Range = range (pos 1 5) (pos 2 4)
+ NewText = "foo bar" } ]
+
+ cursor
+ |> Cursor.afterEdits edits
+ |> Expect.equal "Cursor should move up a line" { cursor with Line = cursor.Line - 1 }
+ testCase "doesn't move cursor when removing a line and inserting a line before cursor in different line"
+ <| fun _ ->
+ let cursor = pos 3 2
+
+ let edits =
+ [ { Range = range (pos 1 5) (pos 2 4)
+ NewText = "foo\nbar" } ]
+
+ cursor
+ |> Cursor.afterEdits edits
+ |> Expect.equal "Cursor should not move" cursor
+ testCase "moves cursor down when removing a line and inserting two lines before cursor in different line"
+ <| fun _ ->
+ let cursor = pos 3 2
+
+ let edits =
+ [ { Range = range (pos 1 5) (pos 2 4)
+ NewText = "foo\nbar\nbaz" } ]
+
+ cursor
+ |> Cursor.afterEdits edits
+ |> Expect.equal "Cursor should move down a line" { cursor with Line = cursor.Line + 1 }
+
+ testCase "moves cursor back when inserting inside same line in front of cursor"
+ <| fun _ ->
+ let cursor = pos 3 2
+
+ let edits =
+ [ { Range = posRange (pos 3 1)
+ NewText = "foo" } ]
+
+ cursor
+ |> Cursor.afterEdits edits
+ |> Expect.equal
+ "Cursor should move back"
+ { cursor with
+ Character = cursor.Character + 3 }
+ testCase "moves cursor forward when deleting inside same line in front of cursor"
+ <| fun _ ->
+ let cursor = pos 3 7
+
+ let edits =
+ [ { Range = range (pos 3 2) (pos 3 5)
+ NewText = "" } ]
+
+ cursor
+ |> Cursor.afterEdits edits
+ |> Expect.equal "Cursor should move forward" { cursor with Character = 4 }
+
+ testCase "moves cursor forward and up when deleting inside and pre same line in front of cursor"
+ <| fun _ ->
+ let cursor = pos 3 7
+
+ let edits =
+ [ { Range = range (pos 2 2) (pos 3 5)
+ NewText = "" } ]
+
+ cursor
+ |> Cursor.afterEdits edits
+ |> Expect.equal "Cursor should move forward and up" (pos 2 4)
+
+
+ testCase "moves cursor to front of delete when cursor inside"
+ <| fun _ ->
+ let cursor = pos 3 7
+
+ let edits =
+ [ { Range = range (pos 2 2) (pos 3 10)
+ NewText = "" } ]
+
+ cursor
+ |> Cursor.afterEdits edits
+ |> Expect.equal "Cursor should move to start of delete" (pos 2 2)
+
+ testCase "cursor stays when insert at cursor position"
+ <| fun _ ->
+ let cursor = pos 2 5
+
+ let edits =
+ [ { Range = posRange (pos 2 5)
+ NewText = "foo bar" } ]
+
+ cursor
+ |> Cursor.afterEdits edits
+ |> Expect.equal "Cursor should move to start of delete" cursor
+
+ testCase "cursor moves to front when replacement with cursor inside"
+ <| fun _ ->
+ let cursor = pos 3 7
+
+ let edits =
+ [ { Range = range (pos 2 3) (pos 5 2)
+ NewText = "foo bar" } ]
+
+ cursor
+ |> Cursor.afterEdits edits
+ |> Expect.equal "Cursor should move to start of delete" { Line = 2; Character = 3 }
+
+ testList
+ "multiple edits"
+ [ let data =
+ lazy
+ (let textWithCursors =
+ """
let $1foo$1 = 42
let $2bar = "baz"
@@ -1171,355 +1436,327 @@ $0printfn "$0Result=%i" b$0
let $7res$7 = f (incr 4) (decr 3)
"""
- // match res with
- // | _ when res < 0 -> failwith "negative"
- // | 0 -> None
- // | i -> Some i
- // """
- |> Text.trimTripleQuotation
- let edits = [
- // doesn't change cursor
- {|
- Marker = "$1"
- NewText = "barbaz"
- |}
- // - 2 lines + 1 line
- {|
- Marker = "$2"
- NewText = "baz = 42\nlet "
- |}
- // -1 line + 2 lines
- {|
- Marker = "$3"
- NewText = "c\n b =\n (a+c-"
- |}
- // -1 line - 3 chars
- {|
- Marker = "$4"
- NewText = ""
- |}
- // +3 line -all chars + couple new chars
- {|
- Marker = "$5"
- NewText = " static\n\n\n mutable"
- |}
- // move to front of edit chars
- {|
- Marker = "$6"
- NewText = "incrementNumber"
- |}
- // doesn't change cursor
- {|
- Marker = "$7"
- NewText = "foo bar\nbaz\nlorem ipsum"
- |}
- ]
- let markers =
- edits
- |> List.map (fun e -> e.Marker)
- |> List.append ["$0"]
- |> List.toArray
-
- let (text, cursors) =
- textWithCursors
- |> Cursors.extractGroupedWith markers
- let cursor = cursors["$0"] |> List.head
-
- let edits =
- edits
- |> List.map (fun e ->
- let range =
- match cursors[e.Marker] with
- | [s;e] -> range s e
- | [pos] -> posRange pos
- | cs -> failwith $"invalid number of cursors `{e.Marker}`. Expected 1 or 2, but was {cs.Length}"
- {
- Range = range
- NewText = e.NewText
- }
- )
- |> TextEdits.sortByRange
-
- {|
- Text = text
- Cursor = cursor
- Edits = edits
- |}
- )
-
- testCase "cursor moves according to multiple edits" <| fun _ ->
- let data = data.Value
- let (text, cursor, edits) = data.Text, data.Cursor, data.Edits
-
- let textAfterEdits =
- text
- |> TextEdits.apply edits
- |> Expect.wantOk "Edits should be valid"
- // cursor should be at start of `incrementNumber`
- let expected =
- textAfterEdits
- |> Text.lines
- |> Seq.indexed
- |> Seq.choose (fun (l, line) ->
- match line.IndexOf("incrementNumber", StringComparison.Ordinal) with
- | -1 -> None
- | c -> Some (pos l c)
- )
- |> Seq.exactlyOne
-
- cursor
- |> Cursor.afterEdits edits
- |> Expect.equal "Cursor should move according to edits" expected
-
- testCase "moving cursor for all edits together is same as moving cursor for each edit" <| fun _ ->
- let data = data.Value
- let (cursor, edits) = data.Cursor, data.Edits
-
- let individually =
- edits
- |> List.rev
- |> List.fold (fun cursor edit ->
- cursor |> Cursor.afterEdits [edit]
- ) cursor
- let together =
+ // match res with
+ // | _ when res < 0 -> failwith "negative"
+ // | 0 -> None
+ // | i -> Some i
+ // """
+ |> Text.trimTripleQuotation
+
+ let edits =
+ [
+ // doesn't change cursor
+ {| Marker = "$1"; NewText = "barbaz" |}
+ // - 2 lines + 1 line
+ {| Marker = "$2"
+ NewText = "baz = 42\nlet " |}
+ // -1 line + 2 lines
+ {| Marker = "$3"
+ NewText = "c\n b =\n (a+c-" |}
+ // -1 line - 3 chars
+ {| Marker = "$4"; NewText = "" |}
+ // +3 line -all chars + couple new chars
+ {| Marker = "$5"
+ NewText = " static\n\n\n mutable" |}
+ // move to front of edit chars
+ {| Marker = "$6"
+ NewText = "incrementNumber" |}
+ // doesn't change cursor
+ {| Marker = "$7"
+ NewText = "foo bar\nbaz\nlorem ipsum" |} ]
+
+ let markers =
+ edits |> List.map (fun e -> e.Marker) |> List.append [ "$0" ] |> List.toArray
+
+ let (text, cursors) = textWithCursors |> Cursors.extractGroupedWith markers
+ let cursor = cursors["$0"] |> List.head
+
+ let edits =
+ edits
+ |> List.map (fun e ->
+ let range =
+ match cursors[e.Marker] with
+ | [ s; e ] -> range s e
+ | [ pos ] -> posRange pos
+ | cs -> failwith $"invalid number of cursors `{e.Marker}`. Expected 1 or 2, but was {cs.Length}"
+
+ { Range = range; NewText = e.NewText })
+ |> TextEdits.sortByRange
+
+ {| Text = text
+ Cursor = cursor
+ Edits = edits |})
+
+ testCase "cursor moves according to multiple edits"
+ <| fun _ ->
+ let data = data.Value
+ let (text, cursor, edits) = data.Text, data.Cursor, data.Edits
+
+ let textAfterEdits =
+ text |> TextEdits.apply edits |> Expect.wantOk "Edits should be valid"
+ // cursor should be at start of `incrementNumber`
+ let expected =
+ textAfterEdits
+ |> Text.lines
+ |> Seq.indexed
+ |> Seq.choose (fun (l, line) ->
+ match line.IndexOf("incrementNumber", StringComparison.Ordinal) with
+ | -1 -> None
+ | c -> Some(pos l c))
+ |> Seq.exactlyOne
+
+ cursor
+ |> Cursor.afterEdits edits
+ |> Expect.equal "Cursor should move according to edits" expected
+
+ testCase "moving cursor for all edits together is same as moving cursor for each edit"
+ <| fun _ ->
+ let data = data.Value
+ let (cursor, edits) = data.Cursor, data.Edits
+
+ let individually =
+ edits
+ |> List.rev
+ |> List.fold (fun cursor edit -> cursor |> Cursor.afterEdits [ edit ]) cursor
+
+ let together = cursor |> Cursor.afterEdits edits
+
+ Expecto.Expect.equal
+ together
+ individually
+ "Moving cursor for all edits together should be same as moving cursor for each edit" ]
+
+ testCase "Can add type annotation with parens while cursor stays at end of identifier"
+ <| fun _ ->
+ // `let foo$0 = 42`
+ let cursor = pos 0 7
+
+ let edits =
+ [ { Range = posRange (pos 0 4)
+ NewText = "(" }
+ { Range = posRange (pos 0 7)
+ NewText = ": int" }
+ { Range = posRange (pos 0 7)
+ NewText = ")" } ]
+
cursor
|> Cursor.afterEdits edits
-
- Expecto.Expect.equal together individually "Moving cursor for all edits together should be same as moving cursor for each edit"
- ]
-
- testCase "Can add type annotation with parens while cursor stays at end of identifier" <| fun _ ->
- // `let foo$0 = 42`
- let cursor = pos 0 7
- let edits = [
- {
- Range = posRange (pos 0 4)
- NewText = "("
- }
- {
- Range = posRange (pos 0 7)
- NewText = ": int"
- }
- {
- Range = posRange (pos 0 7)
- NewText = ")"
- }
- ]
- cursor
- |> Cursor.afterEdits edits
- |> Expect.equal "Cursor should move to end of identifier" { cursor with Character = cursor.Character + 1 }
- ]
-
- let tests = testList (nameof Cursor) [
- tryExtractIndexTests
- tryExtractPositionMarkedWithAnyOfTests
- tryExtractPositionTests
- tryExtractRangeTests
- beforeIndexTests
- tryIndexOfTests
- identityTests
- afterEditsTests
- ]
+ |> Expect.equal
+ "Cursor should move to end of identifier"
+ { cursor with
+ Character = cursor.Character + 1 } ]
+
+ let tests =
+ testList
+ (nameof Cursor)
+ [ tryExtractIndexTests
+ tryExtractPositionMarkedWithAnyOfTests
+ tryExtractPositionTests
+ tryExtractRangeTests
+ beforeIndexTests
+ tryIndexOfTests
+ identityTests
+ afterEditsTests ]
module private Cursors =
open Expecto.Flip
- let private iterTests = testList (nameof Cursors.iter) [
- testCase "no cursor" <| fun _ ->
- let text = "foo bar baz"
- let expected = []
- text
- |> Cursors.iter
- |> Expect.equal "should be empty because no cursors" expected
- testCase "one cursor" <| fun _ ->
- let text = "foo $0bar baz"
- let expected = [text]
- text
- |> Cursors.iter
- |> Expect.equal "should have returned one strings with cursor" expected
- testCase "two cursors" <| fun _ ->
- let text = "foo $0bar baz$0"
- let expected = [
- "foo $0bar baz"
- "foo bar baz$0"
- ]
- text
- |> Cursors.iter
- |> Expect.equal "should have returned two strings with cursor" expected
- testCase "three cursors" <| fun _ ->
- let text = "$0foo $0bar baz$0"
- let expected = [
- "$0foo bar baz"
- "foo $0bar baz"
- "foo bar baz$0"
- ]
- text
- |> Cursors.iter
- |> Expect.equal "should have returned three strings with cursor" expected
- testCase "four cursors" <| fun _ ->
- let text = "$0foo $0ba$0r baz$0"
- let expected = [
- "$0foo bar baz"
- "foo $0bar baz"
- "foo ba$0r baz"
- "foo bar baz$0"
- ]
- text
- |> Cursors.iter
- |> Expect.equal "should have returned three strings with cursor" expected
- testCase "cursors in triple quoted string" <| fun _ ->
- let text = !- """
+ let private iterTests =
+ testList
+ (nameof Cursors.iter)
+ [ testCase "no cursor"
+ <| fun _ ->
+ let text = "foo bar baz"
+ let expected = []
+
+ text
+ |> Cursors.iter
+ |> Expect.equal "should be empty because no cursors" expected
+ testCase "one cursor"
+ <| fun _ ->
+ let text = "foo $0bar baz"
+ let expected = [ text ]
+
+ text
+ |> Cursors.iter
+ |> Expect.equal "should have returned one strings with cursor" expected
+ testCase "two cursors"
+ <| fun _ ->
+ let text = "foo $0bar baz$0"
+ let expected = [ "foo $0bar baz"; "foo bar baz$0" ]
+
+ text
+ |> Cursors.iter
+ |> Expect.equal "should have returned two strings with cursor" expected
+ testCase "three cursors"
+ <| fun _ ->
+ let text = "$0foo $0bar baz$0"
+ let expected = [ "$0foo bar baz"; "foo $0bar baz"; "foo bar baz$0" ]
+
+ text
+ |> Cursors.iter
+ |> Expect.equal "should have returned three strings with cursor" expected
+ testCase "four cursors"
+ <| fun _ ->
+ let text = "$0foo $0ba$0r baz$0"
+
+ let expected =
+ [ "$0foo bar baz"; "foo $0bar baz"; "foo ba$0r baz"; "foo bar baz$0" ]
+
+ text
+ |> Cursors.iter
+ |> Expect.equal "should have returned three strings with cursor" expected
+ testCase "cursors in triple quoted string"
+ <| fun _ ->
+ let text =
+ !- """
module $0Foo
let a = 42
-$0let b =
+$0let b =
a + 5$0
printfn "Result=%i$0" b$0
"""
- let expected = [
- !- """
+
+ let expected =
+ [ !- """
module $0Foo
let a = 42
-let b =
+let b =
a + 5
printfn "Result=%i" b
"""
- !- """
+ !- """
module Foo
let a = 42
-$0let b =
+$0let b =
a + 5
printfn "Result=%i" b
"""
- !- """
+ !- """
module Foo
let a = 42
-let b =
+let b =
a + 5$0
printfn "Result=%i" b
"""
- !- """
+ !- """
module Foo
let a = 42
-let b =
+let b =
a + 5
printfn "Result=%i$0" b
"""
- !- """
+ !- """
module Foo
let a = 42
-let b =
+let b =
a + 5
printfn "Result=%i" b$0
- """
- ]
- text
- |> Cursors.iter
- |> Expect.equal "should have returned all strings with single cursor" expected
- ]
-
- let private extractWithTests = testList (nameof Cursors.extractWith) [
- testCase "can extract all cursors" <| fun _ ->
- let text = !- """
+ """ ]
+
+ text
+ |> Cursors.iter
+ |> Expect.equal "should have returned all strings with single cursor" expected ]
+
+ let private extractWithTests =
+ testList
+ (nameof Cursors.extractWith)
+ [ testCase "can extract all cursors"
+ <| fun _ ->
+ let text =
+ !- """
let $Ff a b = a + b
let $Vvalue = 42
let $0res = $Ff $Vvalue 3
()
"""
- let actual =
- text
- |> Cursors.extractWith [|"$F"; "$V"; "$0" |]
- let expectedText = !- """
+ let actual = text |> Cursors.extractWith [| "$F"; "$V"; "$0" |]
+
+ let expectedText =
+ !- """
let f a b = a + b
let value = 42
let res = f value 3
()
"""
- let expectedPoss = [
- ("$F", pos 0 4)
- ("$V", pos 1 4)
- ("$0", pos 2 4)
- ("$F", pos 2 10)
- ("$V", pos 2 12)
- ]
- let expected = (expectedText, expectedPoss)
-
- actual
- |> Expect.equal "markers should match" expected
- ]
-
- let tests = testList (nameof Cursors) [
- iterTests
- extractWithTests
- ]
+
+ let expectedPoss =
+ [ ("$F", pos 0 4)
+ ("$V", pos 1 4)
+ ("$0", pos 2 4)
+ ("$F", pos 2 10)
+ ("$V", pos 2 12) ]
+
+ let expected = (expectedText, expectedPoss)
+
+ actual |> Expect.equal "markers should match" expected ]
+
+ let tests = testList (nameof Cursors) [ iterTests; extractWithTests ]
module private Text =
open Expecto.Flip
- let private removeTests = testList (nameof Text.remove) [
- testList "start=end should remove nothing" [
- let assertNothingChanged textWithCursor =
- let (range, text) =
- textWithCursor
- |> Text.trimTripleQuotation
- |> Cursor.tryExtractRange
- |> Option.defaultWith (fun _ -> failtest $"no cursor found")
- let expected = Ok text
- text
- |> Text.remove range
- |> Expect.equal "shouldn't have changed input string" expected
-
- let just pos = range pos pos
- testCase "empty string" <| fun _ ->
- let text = ""
- let range = just <| pos 0 0
- let expected = ""
- text
- |> Text.remove range
- |> Expect.equal "shouldn't have change input string" (Ok expected)
- testCase "empty string with two cursors" <| fun _ ->
- "$0$0"
- |> assertNothingChanged
- testList "single line string" [
- testCase "start" <| fun _ ->
- "$0foo bar baz"
- |> assertNothingChanged
- testCase "middle" <| fun _ ->
- "foo b$0ar baz"
- |> assertNothingChanged
- testCase "end" <| fun _ ->
- "foo bar baz$0"
- |> assertNothingChanged
- testCase "two cursors in middle" <| fun _ ->
- "foo $0$0bar baz"
- |> assertNothingChanged
- ]
- testList "two line string" [
- testCase "start" <| fun _ ->
- "$0foo bar\n baz"
- |> assertNothingChanged
- testCase "end 1st line" <| fun _ ->
- "foo bar$0\n baz"
- |> assertNothingChanged
- testCase "start 2nd line" <| fun _ ->
- "foo bar\n$0 baz"
- |> assertNothingChanged
- testCase "middle 2nd line" <| fun _ ->
- "foo bar\n ba$0z"
- |> assertNothingChanged
- testCase "end" <| fun _ ->
- "foo bar\n baz$0"
- |> assertNothingChanged
- ]
- testList "F# Code" (
- let text = """
+ let private removeTests =
+ testList
+ (nameof Text.remove)
+ [ testList
+ "start=end should remove nothing"
+ [ let assertNothingChanged textWithCursor =
+ let (range, text) =
+ textWithCursor
+ |> Text.trimTripleQuotation
+ |> Cursor.tryExtractRange
+ |> Option.defaultWith (fun _ -> failtest $"no cursor found")
+
+ let expected = Ok text
+
+ text
+ |> Text.remove range
+ |> Expect.equal "shouldn't have changed input string" expected
+
+ let just pos = range pos pos
+
+ testCase "empty string"
+ <| fun _ ->
+ let text = ""
+ let range = just <| pos 0 0
+ let expected = ""
+
+ text
+ |> Text.remove range
+ |> Expect.equal "shouldn't have change input string" (Ok expected)
+
+ testCase "empty string with two cursors"
+ <| fun _ -> "$0$0" |> assertNothingChanged
+
+ testList
+ "single line string"
+ [ testCase "start" <| fun _ -> "$0foo bar baz" |> assertNothingChanged
+ testCase "middle" <| fun _ -> "foo b$0ar baz" |> assertNothingChanged
+ testCase "end" <| fun _ -> "foo bar baz$0" |> assertNothingChanged
+ testCase "two cursors in middle"
+ <| fun _ -> "foo $0$0bar baz" |> assertNothingChanged ]
+
+ testList
+ "two line string"
+ [ testCase "start" <| fun _ -> "$0foo bar\n baz" |> assertNothingChanged
+ testCase "end 1st line" <| fun _ -> "foo bar$0\n baz" |> assertNothingChanged
+ testCase "start 2nd line" <| fun _ -> "foo bar\n$0 baz" |> assertNothingChanged
+ testCase "middle 2nd line" <| fun _ -> "foo bar\n ba$0z" |> assertNothingChanged
+ testCase "end" <| fun _ -> "foo bar\n baz$0" |> assertNothingChanged ]
+
+ testList
+ "F# Code"
+ (let text =
+ """
$0module$0 Foo$0
$0let $0a = 42$0
@@ -1527,89 +1764,104 @@ let b =
$0a $0+ 5$0
$0printfn "$0Result=%i" b$0
"""
- text
- |> Cursors.iter
- |> List.mapi (fun i t ->
- testCase $"Cursor {i}" <| fun _ ->
- t |> assertNothingChanged
- )
- )
- ]
-
- let assertRemoveRange range expected text =
- text
- |> Text.remove range
- |> Expect.equal "incorrect string after removing" (Ok expected)
- let assertAfterRemovingIs expected textWithRangeCursors =
- let (range, text) =
- textWithRangeCursors
- |> Text.trimTripleQuotation
- |> Cursor.tryExtractRange
- |> Option.defaultWith (fun _ -> failtest "No cursors")
- assertRemoveRange range expected text
- testList "remove inside single line" [
- testList "single line string" [
- testCase "remove everything" <| fun _ ->
- let text = "$0foo bar baz$0"
- let expected = ""
- text |> assertAfterRemovingIs expected
- testCase "remove start to end of first word" <| fun _ ->
- let text = "$0foo$0 bar baz"
- let expected = " bar baz"
- text |> assertAfterRemovingIs expected
- testCase "remove last word to end of string" <| fun _ ->
- let text = "foo bar $0baz$0"
- let expected = "foo bar "
- text |> assertAfterRemovingIs expected
- testCase "remove word in middle" <| fun _ ->
- let text = "foo $0bar$0 baz"
- let expected = "foo baz"
- text |> assertAfterRemovingIs expected
- testCase "remove a lot in middle" <| fun _ ->
- let text = "f$0oo bar ba$0z"
- let expected = "fz"
- text |> assertAfterRemovingIs expected
- ]
- testList "three line string" [
- testCase "remove everything" <| fun _ ->
- let text = "$0foo bar\nbaz\nlorem ipsum$0"
- let expected = ""
- text |> assertAfterRemovingIs expected
- testCase "remove first line without line break" <| fun _ ->
- // let text = "$0foo bar$0\nbaz\nlorem ipsum"
- let expected = "\nbaz\nlorem ipsum"
- // text |> assertAfterRemovingIs expected
- let text = "foo bar\nbaz\nlorem ipsum"
- let range = range (pos 0 0) (pos 0 7)
- text |> assertRemoveRange range expected
- testCase "remove first line with line break" <| fun _ ->
- // strictly speaking this removes over two lines...
- // let text = "$0foo bar\n$0baz\nlorem ipsum"
- let expected = "baz\nlorem ipsum"
- // text |> assertAfterRemovingIs expected
- let text = "foo bar\nbaz\nlorem ipsum"
- let range = range (pos 0 0) (pos 1 0)
- text |> assertRemoveRange range expected
- testCase "remove 2nd line without line breaks" <| fun _ ->
- let text = "foo bar\n$0baz$0\nlorem ipsum"
- let expected = "foo bar\n\nlorem ipsum"
- text |> assertAfterRemovingIs expected
- testCase "remove 2nd line with line breaks" <| fun _ ->
- let text = "foo bar$0\nbaz\n$0lorem ipsum"
- let expected = "foo barlorem ipsum"
- text |> assertAfterRemovingIs expected
- testCase "remove 3rd line without line break" <| fun _ ->
- let text = "foo bar\nbaz\n$0lorem ipsum$0"
- let expected = "foo bar\nbaz\n"
- text |> assertAfterRemovingIs expected
- testCase "remove 3rd line with line break" <| fun _ ->
- let text = "foo bar\nbaz$0\nlorem ipsum$0"
- let expected = "foo bar\nbaz"
- text |> assertAfterRemovingIs expected
- ]
- testList "F# Code" [
- testCase "Remove empty line" <| fun _ ->
- let text = """
+
+ text
+ |> Cursors.iter
+ |> List.mapi (fun i t -> testCase $"Cursor {i}" <| fun _ -> t |> assertNothingChanged)) ]
+
+ let assertRemoveRange range expected text =
+ text
+ |> Text.remove range
+ |> Expect.equal "incorrect string after removing" (Ok expected)
+
+ let assertAfterRemovingIs expected textWithRangeCursors =
+ let (range, text) =
+ textWithRangeCursors
+ |> Text.trimTripleQuotation
+ |> Cursor.tryExtractRange
+ |> Option.defaultWith (fun _ -> failtest "No cursors")
+
+ assertRemoveRange range expected text
+
+ testList
+ "remove inside single line"
+ [ testList
+ "single line string"
+ [ testCase "remove everything"
+ <| fun _ ->
+ let text = "$0foo bar baz$0"
+ let expected = ""
+ text |> assertAfterRemovingIs expected
+ testCase "remove start to end of first word"
+ <| fun _ ->
+ let text = "$0foo$0 bar baz"
+ let expected = " bar baz"
+ text |> assertAfterRemovingIs expected
+ testCase "remove last word to end of string"
+ <| fun _ ->
+ let text = "foo bar $0baz$0"
+ let expected = "foo bar "
+ text |> assertAfterRemovingIs expected
+ testCase "remove word in middle"
+ <| fun _ ->
+ let text = "foo $0bar$0 baz"
+ let expected = "foo baz"
+ text |> assertAfterRemovingIs expected
+ testCase "remove a lot in middle"
+ <| fun _ ->
+ let text = "f$0oo bar ba$0z"
+ let expected = "fz"
+ text |> assertAfterRemovingIs expected ]
+ testList
+ "three line string"
+ [ testCase "remove everything"
+ <| fun _ ->
+ let text = "$0foo bar\nbaz\nlorem ipsum$0"
+ let expected = ""
+ text |> assertAfterRemovingIs expected
+ testCase "remove first line without line break"
+ <| fun _ ->
+ // let text = "$0foo bar$0\nbaz\nlorem ipsum"
+ let expected = "\nbaz\nlorem ipsum"
+ // text |> assertAfterRemovingIs expected
+ let text = "foo bar\nbaz\nlorem ipsum"
+ let range = range (pos 0 0) (pos 0 7)
+ text |> assertRemoveRange range expected
+ testCase "remove first line with line break"
+ <| fun _ ->
+ // strictly speaking this removes over two lines...
+ // let text = "$0foo bar\n$0baz\nlorem ipsum"
+ let expected = "baz\nlorem ipsum"
+ // text |> assertAfterRemovingIs expected
+ let text = "foo bar\nbaz\nlorem ipsum"
+ let range = range (pos 0 0) (pos 1 0)
+ text |> assertRemoveRange range expected
+ testCase "remove 2nd line without line breaks"
+ <| fun _ ->
+ let text = "foo bar\n$0baz$0\nlorem ipsum"
+ let expected = "foo bar\n\nlorem ipsum"
+ text |> assertAfterRemovingIs expected
+ testCase "remove 2nd line with line breaks"
+ <| fun _ ->
+ let text = "foo bar$0\nbaz\n$0lorem ipsum"
+ let expected = "foo barlorem ipsum"
+ text |> assertAfterRemovingIs expected
+ testCase "remove 3rd line without line break"
+ <| fun _ ->
+ let text = "foo bar\nbaz\n$0lorem ipsum$0"
+ let expected = "foo bar\nbaz\n"
+ text |> assertAfterRemovingIs expected
+ testCase "remove 3rd line with line break"
+ <| fun _ ->
+ let text = "foo bar\nbaz$0\nlorem ipsum$0"
+ let expected = "foo bar\nbaz"
+ text |> assertAfterRemovingIs expected ]
+ testList
+ "F# Code"
+ [ testCase "Remove empty line"
+ <| fun _ ->
+ let text =
+ """
module Foo
$0
$0let a = 42
@@ -1617,16 +1869,21 @@ let b =
a + 5
printfn "Result=%i" b
"""
- let expected = !- """
+
+ let expected =
+ !- """
module Foo
let a = 42
let b =
a + 5
printfn "Result=%i" b
"""
- text |> assertAfterRemovingIs expected
- testCase "remove word" <| fun _ ->
- let text = """
+
+ text |> assertAfterRemovingIs expected
+ testCase "remove word"
+ <| fun _ ->
+ let text =
+ """
module Foo
let a = 42
@@ -1634,7 +1891,9 @@ let $0b$0 =
a + 5
printfn "Result=%i" b
"""
- let expected = !- """
+
+ let expected =
+ !- """
module Foo
let a = 42
@@ -1642,9 +1901,12 @@ let =
a + 5
printfn "Result=%i" b
"""
- text |> assertAfterRemovingIs expected
- testCase "remove end" <| fun _ ->
- let text = """
+
+ text |> assertAfterRemovingIs expected
+ testCase "remove end"
+ <| fun _ ->
+ let text =
+ """
module Foo
let a = 42
@@ -1652,7 +1914,9 @@ let b =
a + 5
printfn "$0Result=%i" b$0
"""
- let expected = !- """
+
+ let expected =
+ !- """
module Foo
let a = 42
@@ -1660,9 +1924,12 @@ let b =
a + 5
printfn "
"""
- text |> assertAfterRemovingIs expected
- testCase "remove start" <| fun _ ->
- let text = """
+
+ text |> assertAfterRemovingIs expected
+ testCase "remove start"
+ <| fun _ ->
+ let text =
+ """
$0module $0Foo
let a = 42
@@ -1670,7 +1937,9 @@ let b =
a + 5
printfn "Result=%i" b
"""
- let expected = !- """
+
+ let expected =
+ !- """
Foo
let a = 42
@@ -1678,23 +1947,30 @@ let b =
a + 5
printfn "Result=%i" b
"""
- text |> assertAfterRemovingIs expected
- ]
- ]
- testList "remove over multiple lines" [
- testList "F# Code" [
- testCase "remove everything" <| fun _ ->
- let text = """
+
+ text |> assertAfterRemovingIs expected ] ]
+
+ testList
+ "remove over multiple lines"
+ [ testList
+ "F# Code"
+ [ testCase "remove everything"
+ <| fun _ ->
+ let text =
+ """
$0module Foo
let a = 42
let b =
a + 5
printfn "Result=%i" b$0"""
- let expected = ""
- text |> assertAfterRemovingIs expected
- testCase "remove everything except last line break" <| fun _ ->
- let text = """
+
+ let expected = ""
+ text |> assertAfterRemovingIs expected
+ testCase "remove everything except last line break"
+ <| fun _ ->
+ let text =
+ """
$0module Foo
let a = 42
@@ -1702,10 +1978,13 @@ let b =
a + 5
printfn "Result=%i" b$0
"""
- let expected = "\n"
- text |> assertAfterRemovingIs expected
- testCase "remove lines 3-5" <| fun _ ->
- let text = """
+
+ let expected = "\n"
+ text |> assertAfterRemovingIs expected
+ testCase "remove lines 3-5"
+ <| fun _ ->
+ let text =
+ """
module Foo
$0let a = 42
@@ -1713,14 +1992,19 @@ let b =
a + 5
$0printfn "Result=%i" b
"""
- let expected = !-"""
+
+ let expected =
+ !- """
module Foo
printfn "Result=%i" b
"""
- text |> assertAfterRemovingIs expected
- testCase "remove lines from inside line 3 to inside line 5" <| fun _ ->
- let text = """
+
+ text |> assertAfterRemovingIs expected
+ testCase "remove lines from inside line 3 to inside line 5"
+ <| fun _ ->
+ let text =
+ """
module Foo
let a = $042
@@ -1728,15 +2012,20 @@ let b =
a + $05
printfn "Result=%i" b
"""
- let expected = !-"""
+
+ let expected =
+ !- """
module Foo
let a = 5
printfn "Result=%i" b
"""
- text |> assertAfterRemovingIs expected
- testCase "remove start to inside line 4" <| fun _ ->
- let text = """
+
+ text |> assertAfterRemovingIs expected
+ testCase "remove start to inside line 4"
+ <| fun _ ->
+ let text =
+ """
$0module Foo
let a = 42
@@ -1744,76 +2033,73 @@ let b $0=
a + 5
printfn "Result=%i" b
"""
- let expected = !-"""
+
+ let expected =
+ !- """
=
a + 5
printfn "Result=%i" b
"""
- text |> assertAfterRemovingIs expected
- testCase "remove inside line 4 to end" <| fun _ ->
- let text = """
+
+ text |> assertAfterRemovingIs expected
+ testCase "remove inside line 4 to end"
+ <| fun _ ->
+ let text =
+ """
module Foo
let a = 42
let b $0=
a + 5
printfn "Result=%i" b$0"""
- let expected = !-"""
+
+ let expected =
+ !- """
module Foo
let a = 42
let b """
- text |> assertAfterRemovingIs expected
- ]
- ]
- ]
-
- let private insertTests = testList (nameof Text.insert) [
- testList "empty insert should insert nothing" [
- let assertNothingChanged textWithCursor =
- let (pos, text) =
- textWithCursor
- |> Text.trimTripleQuotation
- |> Cursor.tryExtractPosition
- |> Option.defaultWith (fun _ -> failtest $"no cursor found")
- let expected = Ok text
- text
- |> Text.insert pos ""
- |> Expect.equal "shouldn't have changed input string" expected
-
- testCase "into empty string" <| fun _ ->
- "$0"
- |> assertNothingChanged
- testList "into single line" [
- testCase "start" <| fun _ ->
- "$0foo bar baz"
- |> assertNothingChanged
- testCase "middle" <| fun _ ->
- "foo ba$0r baz"
- |> assertNothingChanged
- testCase "end" <| fun _ ->
- "foo bar baz$0"
- |> assertNothingChanged
- ]
- testList "into three lines" [
- testCase "start" <| fun _ ->
- "$0foo\nbar\nbaz"
- |> assertNothingChanged
- testCase "start 2nd line" <| fun _ ->
- "foo\n$0bar\nbaz"
- |> assertNothingChanged
- testCase "middle 2nd line" <| fun _ ->
- "foo\nba$0r\nbaz"
- |> assertNothingChanged
- testCase "end 2nd line" <| fun _ ->
- "foo\nbar$0\nbaz"
- |> assertNothingChanged
- testCase "end" <| fun _ ->
- "foo\nbar\nbaz$0"
- |> assertNothingChanged
- ]
- testList "into F# Code" (
- let text = """
+
+ text |> assertAfterRemovingIs expected ] ] ]
+
+ let private insertTests =
+ testList
+ (nameof Text.insert)
+ [ testList
+ "empty insert should insert nothing"
+ [ let assertNothingChanged textWithCursor =
+ let (pos, text) =
+ textWithCursor
+ |> Text.trimTripleQuotation
+ |> Cursor.tryExtractPosition
+ |> Option.defaultWith (fun _ -> failtest $"no cursor found")
+
+ let expected = Ok text
+
+ text
+ |> Text.insert pos ""
+ |> Expect.equal "shouldn't have changed input string" expected
+
+ testCase "into empty string" <| fun _ -> "$0" |> assertNothingChanged
+
+ testList
+ "into single line"
+ [ testCase "start" <| fun _ -> "$0foo bar baz" |> assertNothingChanged
+ testCase "middle" <| fun _ -> "foo ba$0r baz" |> assertNothingChanged
+ testCase "end" <| fun _ -> "foo bar baz$0" |> assertNothingChanged ]
+
+ testList
+ "into three lines"
+ [ testCase "start" <| fun _ -> "$0foo\nbar\nbaz" |> assertNothingChanged
+ testCase "start 2nd line" <| fun _ -> "foo\n$0bar\nbaz" |> assertNothingChanged
+ testCase "middle 2nd line" <| fun _ -> "foo\nba$0r\nbaz" |> assertNothingChanged
+ testCase "end 2nd line" <| fun _ -> "foo\nbar$0\nbaz" |> assertNothingChanged
+ testCase "end" <| fun _ -> "foo\nbar\nbaz$0" |> assertNothingChanged ]
+
+ testList
+ "into F# Code"
+ (let text =
+ """
$0module$0 Foo$0
$0let $0a = 42$0
@@ -1821,54 +2107,56 @@ let b =
$0a $0+ 5$0
$0printfn "$0Result=%i" b$0
"""
- text
- |> Cursors.iter
- |> List.mapi (fun i t ->
- testCase $"Cursor {i}" <| fun _ ->
- t |> assertNothingChanged
- )
- )
- ]
-
- let assertAfterInsertingIs expected (textWithCursor, insert) =
- let (pos, text) =
- textWithCursor
- |> Text.trimTripleQuotation
- |> Cursor.tryExtractPosition
- |> Option.defaultWith (fun _ -> failtest "No cursor")
- text
- |> Text.insert pos insert
- |> Expect.equal "incorrect string after inserting" (Ok expected)
- testList "insert without linebreak" [
- testCase "into empty string" <| fun _ ->
- let text = "$0"
- let insert = "some text"
- let expected = insert
- (text,insert)
- |> assertAfterInsertingIs expected
- testList "into single line string" [
- testCase "start" <| fun _ ->
- let text = "$0foo bar baz"
- let insert = "some text"
- let expected = $"{insert}foo bar baz"
- (text,insert)
- |> assertAfterInsertingIs expected
- testCase "middle" <| fun _ ->
- let text = "foo b$0ar baz"
- let insert = "some text"
- let expected = "foo bsome textar baz"
- (text,insert)
- |> assertAfterInsertingIs expected
- testCase "end" <| fun _ ->
- let text = "foo bar baz$0"
- let insert = "some text"
- let expected = $"foo bar baz{insert}"
- (text,insert)
- |> assertAfterInsertingIs expected
- ]
- testList "into F# Code" [
- testCase "start of 4th line" <| fun _ ->
- let text = """
+
+ text
+ |> Cursors.iter
+ |> List.mapi (fun i t -> testCase $"Cursor {i}" <| fun _ -> t |> assertNothingChanged)) ]
+
+ let assertAfterInsertingIs expected (textWithCursor, insert) =
+ let (pos, text) =
+ textWithCursor
+ |> Text.trimTripleQuotation
+ |> Cursor.tryExtractPosition
+ |> Option.defaultWith (fun _ -> failtest "No cursor")
+
+ text
+ |> Text.insert pos insert
+ |> Expect.equal "incorrect string after inserting" (Ok expected)
+
+ testList
+ "insert without linebreak"
+ [ testCase "into empty string"
+ <| fun _ ->
+ let text = "$0"
+ let insert = "some text"
+ let expected = insert
+ (text, insert) |> assertAfterInsertingIs expected
+ testList
+ "into single line string"
+ [ testCase "start"
+ <| fun _ ->
+ let text = "$0foo bar baz"
+ let insert = "some text"
+ let expected = $"{insert}foo bar baz"
+ (text, insert) |> assertAfterInsertingIs expected
+ testCase "middle"
+ <| fun _ ->
+ let text = "foo b$0ar baz"
+ let insert = "some text"
+ let expected = "foo bsome textar baz"
+ (text, insert) |> assertAfterInsertingIs expected
+ testCase "end"
+ <| fun _ ->
+ let text = "foo bar baz$0"
+ let insert = "some text"
+ let expected = $"foo bar baz{insert}"
+ (text, insert) |> assertAfterInsertingIs expected ]
+ testList
+ "into F# Code"
+ [ testCase "start of 4th line"
+ <| fun _ ->
+ let text =
+ """
module Foo
let a = 42
@@ -1876,8 +2164,11 @@ $0let b =
a + 5
printfn "Result=%i" b
"""
- let insert = "some text"
- let expected = !- """
+
+ let insert = "some text"
+
+ let expected =
+ !- """
module Foo
let a = 42
@@ -1885,10 +2176,12 @@ some textlet b =
a + 5
printfn "Result=%i" b
"""
- (text, insert)
- |> assertAfterInsertingIs expected
- testCase "middle of 4th line" <| fun _ ->
- let text = """
+
+ (text, insert) |> assertAfterInsertingIs expected
+ testCase "middle of 4th line"
+ <| fun _ ->
+ let text =
+ """
module Foo
let a = 42
@@ -1896,8 +2189,11 @@ let b$0 =
a + 5
printfn "Result=%i" b
"""
- let insert = "some text"
- let expected = !- """
+
+ let insert = "some text"
+
+ let expected =
+ !- """
module Foo
let a = 42
@@ -1905,10 +2201,12 @@ let bsome text =
a + 5
printfn "Result=%i" b
"""
- (text, insert)
- |> assertAfterInsertingIs expected
- testCase "end of 4th line" <| fun _ ->
- let text = """
+
+ (text, insert) |> assertAfterInsertingIs expected
+ testCase "end of 4th line"
+ <| fun _ ->
+ let text =
+ """
module Foo
let a = 42
@@ -1916,8 +2214,11 @@ let b =$0
a + 5
printfn "Result=%i" b
"""
- let insert = "some text"
- let expected = !- """
+
+ let insert = "some text"
+
+ let expected =
+ !- """
module Foo
let a = 42
@@ -1925,41 +2226,43 @@ let b =some text
a + 5
printfn "Result=%i" b
"""
- (text, insert)
- |> assertAfterInsertingIs expected
- ]
- ]
-
- testList "insert with line break" [
- testCase "into empty string" <| fun _ ->
- let text = "$0"
- let insert = "lorem\nipsum"
- let expected = insert
- (text, insert)
- |> assertAfterInsertingIs expected
- testList "into single line string" [
- testCase "start" <| fun _ ->
- let text = "$0foo bar baz"
- let insert = "lorem\nipsum"
- let expected = "lorem\nipsumfoo bar baz"
- (text, insert)
- |> assertAfterInsertingIs expected
- testCase "middle" <| fun _ ->
- let text = "foo b$0ar baz"
- let insert = "lorem\nipsum"
- let expected = "foo blorem\nipsumar baz"
- (text, insert)
- |> assertAfterInsertingIs expected
- testCase "end" <| fun _ ->
- let text = "foo bar baz$0"
- let insert = "lorem\nipsum"
- let expected = "foo bar bazlorem\nipsum"
- (text, insert)
- |> assertAfterInsertingIs expected
- ]
- testList "into F# Code" [
- testCase "start" <| fun _ ->
- let text = """
+
+ (text, insert) |> assertAfterInsertingIs expected ] ]
+
+ testList
+ "insert with line break"
+ [ testCase "into empty string"
+ <| fun _ ->
+ let text = "$0"
+ let insert = "lorem\nipsum"
+ let expected = insert
+ (text, insert) |> assertAfterInsertingIs expected
+ testList
+ "into single line string"
+ [ testCase "start"
+ <| fun _ ->
+ let text = "$0foo bar baz"
+ let insert = "lorem\nipsum"
+ let expected = "lorem\nipsumfoo bar baz"
+ (text, insert) |> assertAfterInsertingIs expected
+ testCase "middle"
+ <| fun _ ->
+ let text = "foo b$0ar baz"
+ let insert = "lorem\nipsum"
+ let expected = "foo blorem\nipsumar baz"
+ (text, insert) |> assertAfterInsertingIs expected
+ testCase "end"
+ <| fun _ ->
+ let text = "foo bar baz$0"
+ let insert = "lorem\nipsum"
+ let expected = "foo bar bazlorem\nipsum"
+ (text, insert) |> assertAfterInsertingIs expected ]
+ testList
+ "into F# Code"
+ [ testCase "start"
+ <| fun _ ->
+ let text =
+ """
$0module Foo
let a = 42
@@ -1967,8 +2270,11 @@ let b =
a + 5
printfn "Result=%i" b
"""
- let insert = "lorem\nipsum"
- let expected = !- """
+
+ let insert = "lorem\nipsum"
+
+ let expected =
+ !- """
lorem
ipsummodule Foo
@@ -1977,18 +2283,23 @@ let b =
a + 5
printfn "Result=%i" b
"""
- (text, insert)
- |> assertAfterInsertingIs expected
- testCase "end" <| fun _ ->
- let text = """
+
+ (text, insert) |> assertAfterInsertingIs expected
+ testCase "end"
+ <| fun _ ->
+ let text =
+ """
module Foo
let a = 42
let b =
a + 5
printfn "Result=%i" b$0"""
- let insert = "lorem\nipsum"
- let expected = !- """
+
+ let insert = "lorem\nipsum"
+
+ let expected =
+ !- """
module Foo
let a = 42
@@ -1996,10 +2307,12 @@ let b =
a + 5
printfn "Result=%i" blorem
ipsum"""
- (text, insert)
- |> assertAfterInsertingIs expected
- testCase "end before line break" <| fun _ ->
- let text = """
+
+ (text, insert) |> assertAfterInsertingIs expected
+ testCase "end before line break"
+ <| fun _ ->
+ let text =
+ """
module Foo
let a = 42
@@ -2007,8 +2320,11 @@ let b =
a + 5
printfn "Result=%i" b$0
"""
- let insert = "lorem\nipsum"
- let expected = !- """
+
+ let insert = "lorem\nipsum"
+
+ let expected =
+ !- """
module Foo
let a = 42
@@ -2017,10 +2333,12 @@ let b =
printfn "Result=%i" blorem
ipsum
"""
- (text, insert)
- |> assertAfterInsertingIs expected
- testCase "start of 4th line" <| fun _ ->
- let text = """
+
+ (text, insert) |> assertAfterInsertingIs expected
+ testCase "start of 4th line"
+ <| fun _ ->
+ let text =
+ """
module Foo
let a = 42
@@ -2028,8 +2346,11 @@ $0let b =
a + 5
printfn "Result=%i" b
"""
- let insert = "lorem\nipsum"
- let expected = !- """
+
+ let insert = "lorem\nipsum"
+
+ let expected =
+ !- """
module Foo
let a = 42
@@ -2038,10 +2359,12 @@ ipsumlet b =
a + 5
printfn "Result=%i" b
"""
- (text, insert)
- |> assertAfterInsertingIs expected
- testCase "middle of 4th line" <| fun _ ->
- let text = """
+
+ (text, insert) |> assertAfterInsertingIs expected
+ testCase "middle of 4th line"
+ <| fun _ ->
+ let text =
+ """
module Foo
let a = 42
@@ -2049,8 +2372,11 @@ let b$0 =
a + 5
printfn "Result=%i" b
"""
- let insert = "lorem\nipsum"
- let expected = !- """
+
+ let insert = "lorem\nipsum"
+
+ let expected =
+ !- """
module Foo
let a = 42
@@ -2059,10 +2385,12 @@ ipsum =
a + 5
printfn "Result=%i" b
"""
- (text, insert)
- |> assertAfterInsertingIs expected
- testCase "end of 4th line" <| fun _ ->
- let text = """
+
+ (text, insert) |> assertAfterInsertingIs expected
+ testCase "end of 4th line"
+ <| fun _ ->
+ let text =
+ """
module Foo
let a = 42
@@ -2070,8 +2398,11 @@ let b =$0
a + 5
printfn "Result=%i" b
"""
- let insert = "lorem\nipsum"
- let expected = !- """
+
+ let insert = "lorem\nipsum"
+
+ let expected =
+ !- """
module Foo
let a = 42
@@ -2080,47 +2411,57 @@ ipsum
a + 5
printfn "Result=%i" b
"""
- (text, insert)
- |> assertAfterInsertingIs expected
- ]
- ]
- ]
- let private replaceTests = testList (nameof Text.replace) [
- testList "neither change nor insert" [
- let assertNothingChanged (textWithCursors, replacement) =
- let (range, text) =
- textWithCursors
- |> Text.trimTripleQuotation
- |> Cursor.tryExtractRange
- |> Option.defaultWith (fun _ -> failtest $"no cursor(s) found")
- let expected = Ok text
- text
- |> Text.replace range replacement
- |> Expect.equal "shouldn't have changed input string" expected
- testCase "insert empty string into empty string" <| fun _ ->
- let text = "$0"
- let replacement = ""
- (text, replacement)
- |> assertNothingChanged
- testCase "replace single line text with same text" <| fun _ ->
- let text = "$0foo bar baz$0"
- let replacement = "foo bar baz"
- (text, replacement)
- |> assertNothingChanged
- testCase "replace inside single line text with same text" <| fun _ ->
- let text = "foo$0 bar$0 baz"
- let replacement = " bar"
- (text, replacement)
- |> assertNothingChanged
- testCase "insert empty string into single line string" <| fun _ ->
- let text = "foo $0bar baz"
- let replacement = ""
- (text, replacement)
- |> assertNothingChanged
- testList "F# Code" [
- testCase "insert empty string" <| fun _ ->
- let text = """
+ (text, insert) |> assertAfterInsertingIs expected ] ] ]
+
+ let private replaceTests =
+ testList
+ (nameof Text.replace)
+ [ testList
+ "neither change nor insert"
+ [ let assertNothingChanged (textWithCursors, replacement) =
+ let (range, text) =
+ textWithCursors
+ |> Text.trimTripleQuotation
+ |> Cursor.tryExtractRange
+ |> Option.defaultWith (fun _ -> failtest $"no cursor(s) found")
+
+ let expected = Ok text
+
+ text
+ |> Text.replace range replacement
+ |> Expect.equal "shouldn't have changed input string" expected
+
+ testCase "insert empty string into empty string"
+ <| fun _ ->
+ let text = "$0"
+ let replacement = ""
+ (text, replacement) |> assertNothingChanged
+
+ testCase "replace single line text with same text"
+ <| fun _ ->
+ let text = "$0foo bar baz$0"
+ let replacement = "foo bar baz"
+ (text, replacement) |> assertNothingChanged
+
+ testCase "replace inside single line text with same text"
+ <| fun _ ->
+ let text = "foo$0 bar$0 baz"
+ let replacement = " bar"
+ (text, replacement) |> assertNothingChanged
+
+ testCase "insert empty string into single line string"
+ <| fun _ ->
+ let text = "foo $0bar baz"
+ let replacement = ""
+ (text, replacement) |> assertNothingChanged
+
+ testList
+ "F# Code"
+ [ testCase "insert empty string"
+ <| fun _ ->
+ let text =
+ """
module Foo
let a = 42
@@ -2128,28 +2469,34 @@ let b$0 =
a + 5
printfn "Result=%i" b
"""
- let replacement = ""
- (text, replacement)
- |> assertNothingChanged
- testCase "replace everything with itself" <| fun _ ->
- let text = """
+
+ let replacement = ""
+ (text, replacement) |> assertNothingChanged
+ testCase "replace everything with itself"
+ <| fun _ ->
+ let text =
+ """
$0module Foo
let a = 42
let b =
a + 5
printfn "Result=%i" b$0"""
- let replacement = !- """
+
+ let replacement =
+ !- """
module Foo
let a = 42
let b =
a + 5
printfn "Result=%i" b"""
- (text, replacement)
- |> assertNothingChanged
- testCase "replace inside with same string" <| fun _ ->
- let text = """
+
+ (text, replacement) |> assertNothingChanged
+ testCase "replace inside with same string"
+ <| fun _ ->
+ let text =
+ """
module Foo
let a = 42
@@ -2157,56 +2504,65 @@ let $0b =
a +$0 5
printfn "Result=%i" b
"""
- let replacement = "b =\n a +"
- (text, replacement)
- |> assertNothingChanged
- ]
- ]
- let assertAfterChangingIs expected (textWithCursors, replacement) =
- let (range, text) =
- textWithCursors
- |> Text.trimTripleQuotation
- |> Cursor.tryExtractRange
- |> Option.defaultWith (fun _ -> failtest $"no cursor(s) found")
- let expected = Ok expected
- text
- |> Text.replace range replacement
- |> Expect.equal "unexpected change" expected
-
- testList "replace in F# Code" [
- testCase "delete everything" <| fun _ ->
- let text = """
+
+ let replacement = "b =\n a +"
+ (text, replacement) |> assertNothingChanged ] ]
+ let assertAfterChangingIs expected (textWithCursors, replacement) =
+ let (range, text) =
+ textWithCursors
+ |> Text.trimTripleQuotation
+ |> Cursor.tryExtractRange
+ |> Option.defaultWith (fun _ -> failtest $"no cursor(s) found")
+
+ let expected = Ok expected
+
+ text
+ |> Text.replace range replacement
+ |> Expect.equal "unexpected change" expected
+
+ testList
+ "replace in F# Code"
+ [ testCase "delete everything"
+ <| fun _ ->
+ let text =
+ """
$0module Foo
let a = 42
let b =
a + 5
printfn "Result=%i" b$0"""
- let replacement = ""
- let expected = ""
- (text, replacement)
- |> assertAfterChangingIs expected
- testCase "replace everything" <| fun _ ->
- let text = """
+
+ let replacement = ""
+ let expected = ""
+ (text, replacement) |> assertAfterChangingIs expected
+ testCase "replace everything"
+ <| fun _ ->
+ let text =
+ """
$0module Foo
let a = 42
let b =
a + 5
printfn "Result=%i" b$0"""
- let replacement = !- """
+
+ let replacement =
+ !- """
module Blub
42
|> (+) 5
|> printfn "Result = %i"
"""
- let expected = replacement
- (text, replacement)
- |> assertAfterChangingIs expected
- testCase "insert some lines" <| fun _ ->
- let text = """
+ let expected = replacement
+ (text, replacement) |> assertAfterChangingIs expected
+
+ testCase "insert some lines"
+ <| fun _ ->
+ let text =
+ """
module Foo
$0let a = 42
@@ -2214,11 +2570,15 @@ let b =
a + 5
printfn "Result=%i" b
"""
- let replacement = !- """
+
+ let replacement =
+ !- """
let pi = 3.14
let pi2 = pi * pi
"""
- let expected = !- """
+
+ let expected =
+ !- """
module Foo
let pi = 3.14
@@ -2228,480 +2588,456 @@ let b =
a + 5
printfn "Result=%i" b
"""
- (text, replacement)
- |> assertAfterChangingIs expected
- ]
- ]
- let tests = testList (nameof Text) [
- removeTests
- insertTests
- replaceTests
- ]
+ (text, replacement) |> assertAfterChangingIs expected ] ]
+
+ let tests = testList (nameof Text) [ removeTests; insertTests; replaceTests ]
module private TextEdit =
/// FSAC might return TextEdits with NewLine matching the OS
/// but tests here only handle `\n` not `\r`
/// -> test `TextEdit.apply` replaces/removes `\r`
- let private eolTests = testList "EOL" [
- let testEOLs baseName textWithRange (newTextWithN: string) expected =
- testList baseName [
- let expected = !- expected
- for eol in ["\n"; "\r\n"; "\r"] do
- let eolStr = System.Text.RegularExpressions.Regex.Escape eol
- testCase $"with {eolStr}" <| fun _ ->
- let (range, text) = Cursor.assertExtractRange <| !- textWithRange
- let newText = newTextWithN.Replace("\n", eol)
- let edit: TextEdit = {
- NewText = newText
- Range = range
- }
-
- let actual =
- text
- |> TextEdit.apply edit
- |> Flip.Expect.wantOk "Apply should not fail"
-
- Expect.equal actual expected "Apply should produce correct text"
- Expect.isFalse (actual.Contains '\r') "Should not contain \\r"
- ]
-
- testEOLs
- "can apply insert edit"
- """
+ let private eolTests =
+ testList
+ "EOL"
+ [ let testEOLs baseName textWithRange (newTextWithN: string) expected =
+ testList
+ baseName
+ [ let expected = !-expected
+
+ for eol in [ "\n"; "\r\n"; "\r" ] do
+ let eolStr = System.Text.RegularExpressions.Regex.Escape eol
+
+ testCase $"with {eolStr}"
+ <| fun _ ->
+ let (range, text) = Cursor.assertExtractRange <| !-textWithRange
+ let newText = newTextWithN.Replace("\n", eol)
+ let edit: TextEdit = { NewText = newText; Range = range }
+
+ let actual =
+ text |> TextEdit.apply edit |> Flip.Expect.wantOk "Apply should not fail"
+
+ Expect.equal actual expected "Apply should produce correct text"
+ Expect.isFalse (actual.Contains '\r') "Should not contain \\r" ]
+
+ testEOLs
+ "can apply insert edit"
+ """
let foo = 42$0
let bar = 2
"""
- "\nlet baz = 4"
- """
+ "\nlet baz = 4"
+ """
let foo = 42
let baz = 4
let bar = 2
"""
- testEOLs
- "can apply delete edit"
- """
+
+ testEOLs
+ "can apply delete edit"
+ """
let foo = $042
let bar = $02
"""
- "" // kinda pointless: no new line in insert -> no new lines to replace...
- """
+ "" // kinda pointless: no new line in insert -> no new lines to replace...
+ """
let foo = 2
"""
- testEOLs
- "can apply replace edit"
- """
+
+ testEOLs
+ "can apply replace edit"
+ """
let foo = $042
let bar =$0 2
"""
- "3\nlet a = 1\nlet baz ="
- """
+ "3\nlet a = 1\nlet baz ="
+ """
let foo = 3
let a = 1
let baz = 2
- """
- ]
- let private applyTests = testList (nameof TextEdit.apply) [
- eolTests
- ]
-
- let private tryFindErrorTests = testList (nameof TextEdit.tryFindError) [
- testCase "valid delete edit should should be ok" <| fun _ ->
- {
- Range = { Start = pos 2 2; End = pos 3 3 }
- NewText = ""
- }
- |> TextEdit.tryFindError
- |> Flip.Expect.isNone "Valid delete should be ok"
- testCase "valid insert edit should should be ok" <| fun _ ->
- {
- Range = { Start = pos 2 3; End = pos 2 3 }
- NewText = "foo"
- }
- |> TextEdit.tryFindError
- |> Flip.Expect.isNone "Valid delete should be ok"
- testCase "valid replace edit should should be ok" <| fun _ ->
- {
- Range = { Start = pos 2 3; End = pos 4 9 }
- NewText = "foo"
- }
- |> TextEdit.tryFindError
- |> Flip.Expect.isNone "Valid delete should be ok"
- testCase "empty edit should fail" <| fun _ ->
- {
- Range = { Start = pos 2 4; End = pos 2 4 }
- NewText = ""
- }
- |> TextEdit.tryFindError
- |> Flip.Expect.isSome "Empty edit should fail"
- testCase "edit with End before Start should fail" <| fun _ ->
- {
- Range = { Start = pos 3 4; End = pos 2 2 }
- NewText = ""
- }
- |> TextEdit.tryFindError
- |> Flip.Expect.isSome "End before Start should fail"
- ]
-
- let tests = testList (nameof TextEdit) [
- applyTests
- tryFindErrorTests
- ]
+ """ ]
+
+ let private applyTests = testList (nameof TextEdit.apply) [ eolTests ]
+
+ let private tryFindErrorTests =
+ testList
+ (nameof TextEdit.tryFindError)
+ [ testCase "valid delete edit should should be ok"
+ <| fun _ ->
+ { Range = { Start = pos 2 2; End = pos 3 3 }
+ NewText = "" }
+ |> TextEdit.tryFindError
+ |> Flip.Expect.isNone "Valid delete should be ok"
+ testCase "valid insert edit should should be ok"
+ <| fun _ ->
+ { Range = { Start = pos 2 3; End = pos 2 3 }
+ NewText = "foo" }
+ |> TextEdit.tryFindError
+ |> Flip.Expect.isNone "Valid delete should be ok"
+ testCase "valid replace edit should should be ok"
+ <| fun _ ->
+ { Range = { Start = pos 2 3; End = pos 4 9 }
+ NewText = "foo" }
+ |> TextEdit.tryFindError
+ |> Flip.Expect.isNone "Valid delete should be ok"
+ testCase "empty edit should fail"
+ <| fun _ ->
+ { Range = { Start = pos 2 4; End = pos 2 4 }
+ NewText = "" }
+ |> TextEdit.tryFindError
+ |> Flip.Expect.isSome "Empty edit should fail"
+ testCase "edit with End before Start should fail"
+ <| fun _ ->
+ { Range = { Start = pos 3 4; End = pos 2 2 }
+ NewText = "" }
+ |> TextEdit.tryFindError
+ |> Flip.Expect.isSome "End before Start should fail" ]
+
+ let tests = testList (nameof TextEdit) [ applyTests; tryFindErrorTests ]
module private TextEdits =
- let sortByRangeTests = testList (nameof TextEdits.sortByRange) [
- let test (edits: TextEdit list) =
- let sorted = edits |> TextEdits.sortByRange
- Expect.equal (sorted.Length) (edits.Length) "Sorted edits should have same length as input edits"
-
- // must hold for all in sorted:
- // * r <= succ(r)
- // -> sorted
- // * r = succ(r) -> Index(edits, r) < Index(edits, succ(r))
- // -> preserve order
- let unsorted =
- sorted
- |> List.pairwise
- |> List.filter (fun (r, succ) -> not <| Position.leq r.Range.Start succ.Range.Start)
- // isEmpty doesn't print list when failure...
- if not (unsorted |> List.isEmpty) then
- logger.error (
- eventX "Unsorted: {list}"
- >> setField "list" unsorted
- )
- Expect.isEmpty unsorted "All edits should be sorted"
-
- // Note: for this to work edits must be different (-> different NewText)
- let idxInEdits (edit: TextEdit) =
- edits |> List.findIndex ((=) edit)
-
- let unordered =
- sorted
- |> List.indexed
- |> List.pairwise
- |> List.filter (fun ((_, r), (_, succ)) -> r.Range.Start = succ.Range.Start)
- |> List.choose (fun ((i1, e1), (i2, e2)) ->
- let iSrc1, iSrc2 = (idxInEdits e1, idxInEdits e2)
- assert(iSrc1 <> iSrc2)
- if iSrc1 < iSrc2 then
- None
- else
- {|
- Edits = (e1, e2)
- SourceIndicies = (iSrc1, iSrc2)
- SortedIndices = (i1, i2)
- |}
- |> Some
- )
- // isEmpty doesn't print list when failure...
- if not (unordered |> List.isEmpty) then
- logger.error (
- eventX "Unordered: {list}"
- >> setField "list" unordered
- )
- Expect.isEmpty unordered "Edits with same start should keep order"
-
- testCase "can sort distinct ranges" <| fun _ ->
- [
- (1,5)
- (1,1)
- (3, 2)
- (8, 5)
- (5, 4)
- (5, 6)
- (4, 11)
- (1,7)
- ]
- |> List.mapi (fun i (l,c) ->
- // end doesn't really matter (no overlap allowed)
- let start = { Line = l; Character = c }
- {
- Range = { Start = start; End = start }
- NewText = $"{i}=({l},{c})"
- }
- )
- |> test
-
- testCase "can sort all same position ranges" <| fun _ ->
- List.replicate 10 (2,4)
- |> List.mapi (fun i (l,c) ->
- // end doesn't really matter (no overlap allowed)
- let start = { Line = l; Character = c }
- {
- Range = { Start = start; End = start }
- NewText = $"{i}=({l},{c})"
- }
- )
- |> test
-
- testCase "can sort mix of same and different positions" <| fun _ ->
- [
- (1,5)
- (1,1)
- (3, 2)
- (5, 4)
- (1,5)
- (8, 5)
- (5, 4)
- (5, 6)
- (4, 11)
- (4, 11)
- (1,7)
- ]
- |> List.mapi (fun i (l,c) ->
- // end doesn't really matter (no overlap allowed)
- let start = { Line = l; Character = c }
- {
- Range = { Start = start; End = start }
- NewText = $"{i}=({l},{c})"
- }
- )
- |> test
- ]
-
- let tryFindErrorTests = testList (nameof TextEdits.tryFindError) [
- testCase "valid single edit should succeed" <| fun _ ->
- [
- { NewText = "foo"; Range = { Start = pos 1 2; End = pos 1 5 } }
- ]
- |> TextEdits.tryFindError
- |> Flip.Expect.isNone "valid single edit should succeed"
- testCase "valid multiple edits should succeed" <| fun _ ->
- [
- { NewText = "foo"; Range = { Start = pos 1 2; End = pos 1 5 } }
- { NewText = "bar"; Range = { Start = pos 5 2; End = pos 5 2 } }
- { NewText = "baz"; Range = { Start = pos 2 2; End = pos 3 3 } }
- ]
- |> TextEdits.tryFindError
- |> Flip.Expect.isNone "valid multiple edits should succeed"
- testCase "no edit should fail" <| fun _ ->
- TextEdits.tryFindError []
- |> Flip.Expect.isSome "No edit should fail"
- let replace (start, fin) text : TextEdit = {
- NewText = text
- Range = { Start = start; End = fin }
- }
- let delete (start, fin) = replace (start, fin) ""
- let insert pos text = replace (pos, pos) text
- let empty pos = insert pos ""
- /// used to mark edits that aren't main parts of the test, but instead are just used as 'surrounding'
- /// -> `filler` used for tagging
- let inline filler v = v
- testCase "single empty edit should fail" <| fun _ ->
- TextEdits.tryFindError [empty (pos 2 3)]
- |> Flip.Expect.isSome "Empty edit should fail"
- testCase "multiple empty edits should fail" <| fun _ ->
- TextEdits.tryFindError [empty (pos 2 3); empty (pos 3 5); empty (pos 1 1)]
- |> Flip.Expect.isSome "Empty edit should fail"
- testCase "empty edit in list with valid edits should fail" <| fun _ ->
- [
- filler <| replace (pos 1 2, pos 1 5) "0"
- filler <| replace (pos 5 2, pos 5 2) "1"
- empty (pos 1 7)
- filler <| replace (pos 2 2, pos 3 3) "1"
- ]
- |> TextEdits.tryFindError
- |> Flip.Expect.isSome "Empty edit should fail"
- testCase "two overlapping edits (Back/Front) on one line should fail" <| fun _ ->
- [
- replace (pos 1 2, pos 1 5) "front overlap"
- replace (pos 1 3, pos 1 7) "back overlap"
- ]
- |> TextEdits.tryFindError
- |> Flip.Expect.isSome "Overlapping edits should fail"
- testCase "two overlapping edits (Front/Back) on one line should fail" <| fun _ ->
- [
- replace (pos 1 3, pos 1 7) "back overlap"
- replace (pos 1 2, pos 1 5) "front overlap"
- ]
- |> TextEdits.tryFindError
- |> Flip.Expect.isSome "Overlapping edits should fail"
- testCase "two overlapping edits (Back/Front) over multiple lines should fail" <| fun _ ->
- [
- replace (pos 1 2, pos 3 5) "front overlap"
- replace (pos 2 3, pos 5 7) "back overlap"
- ]
- |> TextEdits.tryFindError
- |> Flip.Expect.isSome "Overlapping edits should fail"
- testCase "two touching edits should succeed" <| fun _ ->
- // valid because: cursor is between characters
- // -> replace prior to (3,5); replace after (3,5)
- // -> do not interfere with each other
- [
- replace (pos 1 2, pos 3 5) "front"
- replace (pos 3 5, pos 5 7) "back"
- ]
- |> TextEdits.tryFindError
- |> Flip.Expect.isNone "Touching edits should succeed"
- testCase "two overlapping edits (Front/Back) over multiple lines should fail" <| fun _ ->
- [
- replace (pos 2 3, pos 5 7) "back overlap"
- replace (pos 1 2, pos 3 5) "front overlap"
- ]
- |> TextEdits.tryFindError
- |> Flip.Expect.isSome "Overlapping edits should fail"
- testCase "overlapping edits (Back/Front) in list with valid edits should fail" <| fun _ ->
- [
- filler <| replace (pos 1 1, pos 1 1) "0"
- filler <| replace (pos 17 8, pos 19 8) "1"
- replace (pos 1 2, pos 3 5) "front overlap"
- filler <| replace (pos 7 5, pos 8 9) "2"
- replace (pos 2 3, pos 5 7) "back overlap"
- filler <| replace (pos 11 1, pos 15 9) "3"
- ]
- |> TextEdits.tryFindError
- |> Flip.Expect.isSome "Overlapping edits should fail"
- testCase "replace inside another replace should fail" <| fun _ ->
- [
- replace (pos 2 3, pos 4 1) "inside"
- replace (pos 1 2, pos 5 7) "outside"
- ]
- |> TextEdits.tryFindError
- |> Flip.Expect.isSome "Inside edits should fail"
- testCase "replace with another replace inside should fail" <| fun _ ->
- [
- replace (pos 1 2, pos 5 7) "outside"
- replace (pos 2 3, pos 4 1) "inside"
- ]
- |> TextEdits.tryFindError
- |> Flip.Expect.isSome "Inside edits should fail"
- testCase "inserts with same position should succeed" <| fun _ ->
- [
- insert (pos 2 4) "insert 1"
- insert (pos 2 4) "insert 2"
- ]
- |> TextEdits.tryFindError
- |> Flip.Expect.isNone "Same position inserts should succeed"
- testCase "inserts with same position followed by replace starting at same position should succeed" <| fun _ ->
- [
- insert (pos 2 4) "insert 1"
- insert (pos 2 4) "insert 2"
- replace (pos 2 4, pos 4 7) "replace"
- ]
- |> TextEdits.tryFindError
- |> Flip.Expect.isNone "Same position inserts followed by replace should succeed"
- testCase "replace before insert on same position should fail" <| fun _ ->
- [
- replace (pos 2 4, pos 4 7) "replace"
- insert (pos 2 4) "a"
- ]
- |> TextEdits.tryFindError
- |> Flip.Expect.isSome "Replace before insert on same position should fail"
- testCase "inserts with same position followed by replace at same position intermingled with other valid edits should succeed" <| fun _ ->
- [
- filler <| replace (pos 6 7, pos 7 9) "0"
- insert (pos 2 4) "insert 1"
- filler <| replace (pos 1 4, pos 2 1) "1"
- filler <| replace (pos 11 17, pos 18 19) "2"
- insert (pos 2 4) "insert 2"
- filler <| replace (pos 6 1, pos 6 2) "3"
- replace (pos 2 4, pos 4 7) "replace"
- filler <| replace (pos 9 2, pos 9 7) "4"
- ]
- |> TextEdits.tryFindError
- |> Flip.Expect.isNone "Same position inserts followed by replace should succeed"
- testCase "replace before insert on same position intermingled with other valid edits should fail" <| fun _ ->
- [
- filler <| replace (pos 6 7, pos 7 9) "0"
- insert (pos 2 4) "insert 1"
- filler <| replace (pos 1 4, pos 2 1) "1"
- filler <| replace (pos 11 17, pos 18 19) "2"
- replace (pos 2 4, pos 4 7) "replace"
- filler <| replace (pos 6 1, pos 6 2) "3"
- insert (pos 2 4) "insert 2"
- filler <| replace (pos 9 2, pos 9 7) "4"
- ]
- |> TextEdits.tryFindError
- |> Flip.Expect.isSome "Replace before insert on same position should fail"
- testCase "two replaces in same position should fail" <| fun _ ->
- [
- replace (pos 2 4, pos 5 9) "replace 1"
- replace (pos 2 4, pos 4 7) "replace 2"
- ]
- |> TextEdits.tryFindError
- |> Flip.Expect.isSome "Two replaces in same position should fail"
- testCase "two replaces in same position intermingled with other valid edits should fail should fail" <| fun _ ->
- [
- filler <| replace (pos 6 7, pos 7 9) "0"
- replace (pos 2 4, pos 5 9) "replace 1"
- filler <| replace (pos 1 4, pos 2 1) "1"
- replace (pos 2 4, pos 4 7) "replace 2"
- filler <| replace (pos 6 1, pos 6 2) "2"
- ]
- |> TextEdits.tryFindError
- |> Flip.Expect.isSome "Two replaces in same position should fail"
- ]
-
- let applyTests = testList (nameof TextEdits.apply) [
- testList "single edit" [
- testCase "insert" <| fun _ ->
- let (range, text) = Cursor.assertExtractRange !- """
+ let sortByRangeTests =
+ testList
+ (nameof TextEdits.sortByRange)
+ [ let test (edits: TextEdit list) =
+ let sorted = edits |> TextEdits.sortByRange
+ Expect.equal (sorted.Length) (edits.Length) "Sorted edits should have same length as input edits"
+
+ // must hold for all in sorted:
+ // * r <= succ(r)
+ // -> sorted
+ // * r = succ(r) -> Index(edits, r) < Index(edits, succ(r))
+ // -> preserve order
+ let unsorted =
+ sorted
+ |> List.pairwise
+ |> List.filter (fun (r, succ) -> not <| Position.leq r.Range.Start succ.Range.Start)
+ // isEmpty doesn't print list when failure...
+ if not (unsorted |> List.isEmpty) then
+ logger.error (eventX "Unsorted: {list}" >> setField "list" unsorted)
+
+ Expect.isEmpty unsorted "All edits should be sorted"
+
+ // Note: for this to work edits must be different (-> different NewText)
+ let idxInEdits (edit: TextEdit) = edits |> List.findIndex ((=) edit)
+
+ let unordered =
+ sorted
+ |> List.indexed
+ |> List.pairwise
+ |> List.filter (fun ((_, r), (_, succ)) -> r.Range.Start = succ.Range.Start)
+ |> List.choose (fun ((i1, e1), (i2, e2)) ->
+ let iSrc1, iSrc2 = (idxInEdits e1, idxInEdits e2)
+ assert (iSrc1 <> iSrc2)
+
+ if iSrc1 < iSrc2 then
+ None
+ else
+ {| Edits = (e1, e2)
+ SourceIndicies = (iSrc1, iSrc2)
+ SortedIndices = (i1, i2) |}
+ |> Some)
+ // isEmpty doesn't print list when failure...
+ if not (unordered |> List.isEmpty) then
+ logger.error (eventX "Unordered: {list}" >> setField "list" unordered)
+
+ Expect.isEmpty unordered "Edits with same start should keep order"
+
+ testCase "can sort distinct ranges"
+ <| fun _ ->
+ [ (1, 5); (1, 1); (3, 2); (8, 5); (5, 4); (5, 6); (4, 11); (1, 7) ]
+ |> List.mapi (fun i (l, c) ->
+ // end doesn't really matter (no overlap allowed)
+ let start = { Line = l; Character = c }
+
+ { Range = { Start = start; End = start }
+ NewText = $"{i}=({l},{c})" })
+ |> test
+
+ testCase "can sort all same position ranges"
+ <| fun _ ->
+ List.replicate 10 (2, 4)
+ |> List.mapi (fun i (l, c) ->
+ // end doesn't really matter (no overlap allowed)
+ let start = { Line = l; Character = c }
+
+ { Range = { Start = start; End = start }
+ NewText = $"{i}=({l},{c})" })
+ |> test
+
+ testCase "can sort mix of same and different positions"
+ <| fun _ ->
+ [ (1, 5)
+ (1, 1)
+ (3, 2)
+ (5, 4)
+ (1, 5)
+ (8, 5)
+ (5, 4)
+ (5, 6)
+ (4, 11)
+ (4, 11)
+ (1, 7) ]
+ |> List.mapi (fun i (l, c) ->
+ // end doesn't really matter (no overlap allowed)
+ let start = { Line = l; Character = c }
+
+ { Range = { Start = start; End = start }
+ NewText = $"{i}=({l},{c})" })
+ |> test ]
+
+ let tryFindErrorTests =
+ testList
+ (nameof TextEdits.tryFindError)
+ [ testCase "valid single edit should succeed"
+ <| fun _ ->
+ [ { NewText = "foo"
+ Range = { Start = pos 1 2; End = pos 1 5 } } ]
+ |> TextEdits.tryFindError
+ |> Flip.Expect.isNone "valid single edit should succeed"
+ testCase "valid multiple edits should succeed"
+ <| fun _ ->
+ [ { NewText = "foo"
+ Range = { Start = pos 1 2; End = pos 1 5 } }
+ { NewText = "bar"
+ Range = { Start = pos 5 2; End = pos 5 2 } }
+ { NewText = "baz"
+ Range = { Start = pos 2 2; End = pos 3 3 } } ]
+ |> TextEdits.tryFindError
+ |> Flip.Expect.isNone "valid multiple edits should succeed"
+ testCase "no edit should fail"
+ <| fun _ -> TextEdits.tryFindError [] |> Flip.Expect.isSome "No edit should fail"
+ let replace (start, fin) text : TextEdit =
+ { NewText = text
+ Range = { Start = start; End = fin } }
+
+ let _delete (start, fin) = replace (start, fin) ""
+ let insert pos text = replace (pos, pos) text
+ let empty pos = insert pos ""
+ /// used to mark edits that aren't main parts of the test, but instead are just used as 'surrounding'
+ /// -> `filler` used for tagging
+ let inline filler v = v
+
+ testCase "single empty edit should fail"
+ <| fun _ ->
+ TextEdits.tryFindError [ empty (pos 2 3) ]
+ |> Flip.Expect.isSome "Empty edit should fail"
+
+ testCase "multiple empty edits should fail"
+ <| fun _ ->
+ TextEdits.tryFindError [ empty (pos 2 3); empty (pos 3 5); empty (pos 1 1) ]
+ |> Flip.Expect.isSome "Empty edit should fail"
+
+ testCase "empty edit in list with valid edits should fail"
+ <| fun _ ->
+ [ filler <| replace (pos 1 2, pos 1 5) "0"
+ filler <| replace (pos 5 2, pos 5 2) "1"
+ empty (pos 1 7)
+ filler <| replace (pos 2 2, pos 3 3) "1" ]
+ |> TextEdits.tryFindError
+ |> Flip.Expect.isSome "Empty edit should fail"
+
+ testCase "two overlapping edits (Back/Front) on one line should fail"
+ <| fun _ ->
+ [ replace (pos 1 2, pos 1 5) "front overlap"
+ replace (pos 1 3, pos 1 7) "back overlap" ]
+ |> TextEdits.tryFindError
+ |> Flip.Expect.isSome "Overlapping edits should fail"
+
+ testCase "two overlapping edits (Front/Back) on one line should fail"
+ <| fun _ ->
+ [ replace (pos 1 3, pos 1 7) "back overlap"
+ replace (pos 1 2, pos 1 5) "front overlap" ]
+ |> TextEdits.tryFindError
+ |> Flip.Expect.isSome "Overlapping edits should fail"
+
+ testCase "two overlapping edits (Back/Front) over multiple lines should fail"
+ <| fun _ ->
+ [ replace (pos 1 2, pos 3 5) "front overlap"
+ replace (pos 2 3, pos 5 7) "back overlap" ]
+ |> TextEdits.tryFindError
+ |> Flip.Expect.isSome "Overlapping edits should fail"
+
+ testCase "two touching edits should succeed"
+ <| fun _ ->
+ // valid because: cursor is between characters
+ // -> replace prior to (3,5); replace after (3,5)
+ // -> do not interfere with each other
+ [ replace (pos 1 2, pos 3 5) "front"; replace (pos 3 5, pos 5 7) "back" ]
+ |> TextEdits.tryFindError
+ |> Flip.Expect.isNone "Touching edits should succeed"
+
+ testCase "two overlapping edits (Front/Back) over multiple lines should fail"
+ <| fun _ ->
+ [ replace (pos 2 3, pos 5 7) "back overlap"
+ replace (pos 1 2, pos 3 5) "front overlap" ]
+ |> TextEdits.tryFindError
+ |> Flip.Expect.isSome "Overlapping edits should fail"
+
+ testCase "overlapping edits (Back/Front) in list with valid edits should fail"
+ <| fun _ ->
+ [ filler <| replace (pos 1 1, pos 1 1) "0"
+ filler <| replace (pos 17 8, pos 19 8) "1"
+ replace (pos 1 2, pos 3 5) "front overlap"
+ filler <| replace (pos 7 5, pos 8 9) "2"
+ replace (pos 2 3, pos 5 7) "back overlap"
+ filler <| replace (pos 11 1, pos 15 9) "3" ]
+ |> TextEdits.tryFindError
+ |> Flip.Expect.isSome "Overlapping edits should fail"
+
+ testCase "replace inside another replace should fail"
+ <| fun _ ->
+ [ replace (pos 2 3, pos 4 1) "inside"; replace (pos 1 2, pos 5 7) "outside" ]
+ |> TextEdits.tryFindError
+ |> Flip.Expect.isSome "Inside edits should fail"
+
+ testCase "replace with another replace inside should fail"
+ <| fun _ ->
+ [ replace (pos 1 2, pos 5 7) "outside"; replace (pos 2 3, pos 4 1) "inside" ]
+ |> TextEdits.tryFindError
+ |> Flip.Expect.isSome "Inside edits should fail"
+
+ testCase "inserts with same position should succeed"
+ <| fun _ ->
+ [ insert (pos 2 4) "insert 1"; insert (pos 2 4) "insert 2" ]
+ |> TextEdits.tryFindError
+ |> Flip.Expect.isNone "Same position inserts should succeed"
+
+ testCase "inserts with same position followed by replace starting at same position should succeed"
+ <| fun _ ->
+ [ insert (pos 2 4) "insert 1"
+ insert (pos 2 4) "insert 2"
+ replace (pos 2 4, pos 4 7) "replace" ]
+ |> TextEdits.tryFindError
+ |> Flip.Expect.isNone "Same position inserts followed by replace should succeed"
+
+ testCase "replace before insert on same position should fail"
+ <| fun _ ->
+ [ replace (pos 2 4, pos 4 7) "replace"; insert (pos 2 4) "a" ]
+ |> TextEdits.tryFindError
+ |> Flip.Expect.isSome "Replace before insert on same position should fail"
+
+ testCase
+ "inserts with same position followed by replace at same position intermingled with other valid edits should succeed"
+ <| fun _ ->
+ [ filler <| replace (pos 6 7, pos 7 9) "0"
+ insert (pos 2 4) "insert 1"
+ filler <| replace (pos 1 4, pos 2 1) "1"
+ filler <| replace (pos 11 17, pos 18 19) "2"
+ insert (pos 2 4) "insert 2"
+ filler <| replace (pos 6 1, pos 6 2) "3"
+ replace (pos 2 4, pos 4 7) "replace"
+ filler <| replace (pos 9 2, pos 9 7) "4" ]
+ |> TextEdits.tryFindError
+ |> Flip.Expect.isNone "Same position inserts followed by replace should succeed"
+
+ testCase "replace before insert on same position intermingled with other valid edits should fail"
+ <| fun _ ->
+ [ filler <| replace (pos 6 7, pos 7 9) "0"
+ insert (pos 2 4) "insert 1"
+ filler <| replace (pos 1 4, pos 2 1) "1"
+ filler <| replace (pos 11 17, pos 18 19) "2"
+ replace (pos 2 4, pos 4 7) "replace"
+ filler <| replace (pos 6 1, pos 6 2) "3"
+ insert (pos 2 4) "insert 2"
+ filler <| replace (pos 9 2, pos 9 7) "4" ]
+ |> TextEdits.tryFindError
+ |> Flip.Expect.isSome "Replace before insert on same position should fail"
+
+ testCase "two replaces in same position should fail"
+ <| fun _ ->
+ [ replace (pos 2 4, pos 5 9) "replace 1"
+ replace (pos 2 4, pos 4 7) "replace 2" ]
+ |> TextEdits.tryFindError
+ |> Flip.Expect.isSome "Two replaces in same position should fail"
+
+ testCase "two replaces in same position intermingled with other valid edits should fail should fail"
+ <| fun _ ->
+ [ filler <| replace (pos 6 7, pos 7 9) "0"
+ replace (pos 2 4, pos 5 9) "replace 1"
+ filler <| replace (pos 1 4, pos 2 1) "1"
+ replace (pos 2 4, pos 4 7) "replace 2"
+ filler <| replace (pos 6 1, pos 6 2) "2" ]
+ |> TextEdits.tryFindError
+ |> Flip.Expect.isSome "Two replaces in same position should fail" ]
+
+ let applyTests =
+ testList
+ (nameof TextEdits.apply)
+ [ testList
+ "single edit"
+ [ testCase "insert"
+ <| fun _ ->
+ let (range, text) =
+ Cursor.assertExtractRange
+ !- """
let foo = 42$0
let bar = 2
"""
- let edit: TextEdit = {
- NewText = "\nlet baz = 4"
- Range = range
- }
- let expected = !- """
+
+ let edit: TextEdit =
+ { NewText = "\nlet baz = 4"
+ Range = range }
+
+ let expected =
+ !- """
let foo = 42
let baz = 4
let bar = 2
"""
- let actual =
- text
- |> TextEdit.apply edit
- |> Flip.Expect.wantOk "Apply should not fail"
- Expect.equal actual expected "Apply should produce correct text"
- testCase "remove" <| fun _ ->
- let (range, text) = Cursor.assertExtractRange !- """
+
+ let actual =
+ text |> TextEdit.apply edit |> Flip.Expect.wantOk "Apply should not fail"
+
+ Expect.equal actual expected "Apply should produce correct text"
+ testCase "remove"
+ <| fun _ ->
+ let (range, text) =
+ Cursor.assertExtractRange
+ !- """
let foo = $042
let bar = $02
"""
- let edit: TextEdit = {
- NewText = ""
- Range = range
- }
- let expected = !- """
+
+ let edit: TextEdit = { NewText = ""; Range = range }
+
+ let expected =
+ !- """
let foo = 2
"""
- let actual =
- text
- |> TextEdit.apply edit
- |> Flip.Expect.wantOk "Apply should not fail"
- Expect.equal actual expected "Apply should produce correct text"
- testCase "replace" <| fun _ ->
- let (range, text) = Cursor.assertExtractRange !- """
+
+ let actual =
+ text |> TextEdit.apply edit |> Flip.Expect.wantOk "Apply should not fail"
+
+ Expect.equal actual expected "Apply should produce correct text"
+ testCase "replace"
+ <| fun _ ->
+ let (range, text) =
+ Cursor.assertExtractRange
+ !- """
let foo = $042
let bar$0 = 2
"""
- let edit: TextEdit = {
- NewText = "1\nlet baz"
- Range = range
- }
- let expected = !- """
+
+ let edit: TextEdit =
+ { NewText = "1\nlet baz"
+ Range = range }
+
+ let expected =
+ !- """
let foo = 1
let baz = 2
"""
- let actual =
- text
- |> TextEdit.apply edit
- |> Flip.Expect.wantOk "Apply should not fail"
- Expect.equal actual expected "Apply should produce correct text"
- ]
- ]
- let tests = testList (nameof TextEdits) [
- sortByRangeTests
- tryFindErrorTests
- applyTests
- ]
-
-let tests = testList (nameof TextEdit) [
- Cursor.tests
- Cursors.tests
- Text.tests
- TextEdit.tests
- TextEdits.tests
-]
+
+ let actual =
+ text |> TextEdit.apply edit |> Flip.Expect.wantOk "Apply should not fail"
+
+ Expect.equal actual expected "Apply should produce correct text" ] ]
+
+ let tests =
+ testList (nameof TextEdits) [ sortByRangeTests; tryFindErrorTests; applyTests ]
+
+let tests =
+ testList (nameof TextEdit) [ Cursor.tests; Cursors.tests; Text.tests; TextEdit.tests; TextEdits.tests ]
diff --git a/test/FsAutoComplete.Tests.Lsp/Utils/Utils.fs b/test/FsAutoComplete.Tests.Lsp/Utils/Utils.fs
index e039936d1..cb75e3b36 100644
--- a/test/FsAutoComplete.Tests.Lsp/Utils/Utils.fs
+++ b/test/FsAutoComplete.Tests.Lsp/Utils/Utils.fs
@@ -4,53 +4,58 @@ module Expect =
open FsAutoComplete.Utils
open Expecto
- let failureMatching (m: AssertException -> bool) (f: Async<_>) = async {
- let failed = async {
- try
- do! f |> Async.map ignore
- return false
- with
- | :? AssertException as ex when m ex -> return true
- // keep other exceptions
+ let failureMatching (m: AssertException -> bool) (f: Async<_>) =
+ async {
+ let failed =
+ async {
+ try
+ do! f |> Async.map ignore
+ return false
+ with :? AssertException as ex when m ex ->
+ return true
+ // keep other exceptions
+ }
+
+ let! failed = failed
+
+ if not failed then
+ failtestf "Expected AssertException, but was no exception"
}
- let! failed = failed
- if not failed then
- failtestf "Expected AssertException, but was no exception"
- }
/// passed Async `f` is expected to throw `Expecto.AssertException`
/// -> Expecto Test in `f` is expected to fail
- ///
+ ///
/// ~ Basically fancy `Async` wrapper for `Expect.throwsT`
- ///
+ ///
/// Note: `failwith` doesn't trigger success (throws `System.Exception`). Use `failtest` instead
let failure f = failureMatching (fun _ -> true) f
module private Seq =
let tryMin source =
source
- |> Seq.fold (fun m e ->
- match m with
- | None -> Some e
- | Some m -> Some (min m e)
- ) None
+ |> Seq.fold
+ (fun m e ->
+ match m with
+ | None -> Some e
+ | Some m -> Some(min m e))
+ None
module Position =
open Ionide.LanguageServerProtocol.Types
let inline assertPositive (pos: Position) =
- assert(pos.Line >= 0)
- assert(pos.Character >= 0)
+ assert (pos.Line >= 0)
+ assert (pos.Character >= 0)
- let inline eq p1 p2 =
+ let inline eq p1 p2 =
// p1.Line = p2.Line && p1.Character = p2.Character
p1 = p2
- let inline gt p1 p2 =
- p1.Line > p2.Line || (p1.Line = p2.Line && p1.Character > p2.Character)
+
+ let inline gt p1 p2 = p1.Line > p2.Line || (p1.Line = p2.Line && p1.Character > p2.Character)
let inline geq p1 p2 = eq p1 p2 || gt p1 p2
let inline lt p1 p2 = gt p2 p1
let inline leq p1 p2 = geq p2 p1
-
+
/// Note: Always assumes correct order inside Range: `Start <= End`
module Range =
open Ionide.LanguageServerProtocol.Types
@@ -60,11 +65,10 @@ module Range =
/// Range represents a single position (`Start = End`)
- let inline isPosition (range: Range) =
- range.Start = range.End
+ let inline isPosition (range: Range) = range.Start = range.End
/// Strict: `pos` on `Start` or `End` of `range` counts as containing
- ///
+ ///
/// ```text
/// ----------------------------------->
/// ^^^^^^^^^^^^^^^^^ range
@@ -79,7 +83,7 @@ module Range =
Position.leq range.Start pos && Position.leq pos range.End
/// Loose: `pos` on `Start` or `End` of `range` doesn't count as containing
- ///
+ ///
/// ```text
/// ----------------------------------->
/// ^^^^^^^^^^^^^^^^^ range
@@ -102,8 +106,7 @@ module Range =
/// | ┕ true
/// ┕ false
/// ```
- let inline onBorder (pos: Position) (range: Range) =
- pos = range.Start || pos = range.End
+ let inline onBorder (pos: Position) (range: Range) = pos = range.Start || pos = range.End
/// Share a Start/End or End/Start, but nothing else.
///
@@ -113,13 +116,12 @@ module Range =
/// | | ^^^^^^^^ false
/// | ^^^^^^^^ true
/// ^^^^^^^^ false
- /// ^^^^^^ false
+ /// ^^^^^^ false
/// ^^^ false
/// ^ true
/// ^^^ true
/// ```
- let inline touches (range1: Range) (range2: Range) =
- range1.Start = range2.End || range1.End = range2.Start
+ let inline touches (range1: Range) (range2: Range) = range1.Start = range2.End || range1.End = range2.Start
/// Strict: Just sharing a Start/End (touching) counts as overlap too
///
@@ -129,27 +131,24 @@ module Range =
/// | | ^^^^^^^^ false
/// | ^^^^^^^^ true
/// ^^^^^^^^ true
- /// ^^^^^^^ true
+ /// ^^^^^^^ true
/// ```
let overlapsStrictly (range1: Range) (range2: Range) =
range1 |> containsStrictly range2.Start
- ||
- range1 |> containsStrictly range2.End
- ||
- range2 |> containsStrictly range1.Start
- ||
- range2 |> containsStrictly range1.End
-
+ || range1 |> containsStrictly range2.End
+ || range2 |> containsStrictly range1.Start
+ || range2 |> containsStrictly range1.End
+
/// Loose: Touching doesn't count as overlapping.
/// Neither does both just position and same position
- ///
+ ///
/// ```text
/// -------------------------->
/// ^^^^^^^
/// | | | ^^^^^^^^ false
/// | | ^^^^^^^^ false
/// | ^^^^^^^^ true
- /// ^^^^^^^ true
+ /// ^^^^^^^ true
/// ```
/// ```text
/// -------------------------->
@@ -160,12 +159,10 @@ module Range =
/// ^^^^^^ true
/// ```
let overlapsLoosely (range1: Range) (range2: Range) =
- (range1 |> overlapsStrictly range2)
- &&
- not (range1 |> touches range2)
+ (range1 |> overlapsStrictly range2) && not (range1 |> touches range2)
/// Strict: Touching is not disjoint
- ///
+ ///
/// ```text
/// -------------------------->
/// ^^^^^^^
@@ -174,10 +171,9 @@ module Range =
/// | ^ false
/// ^^^^^^^^ false
/// ```
- let isDisjointStrictly (range1: Range) (range2: Range) =
- not <| overlapsStrictly range1 range2
+ let isDisjointStrictly (range1: Range) (range2: Range) = not <| overlapsStrictly range1 range2
/// Loose: Touching is disjoint
- ///
+ ///
/// ```text
/// -------------------------->
/// ^^^^^^^
@@ -186,8 +182,7 @@ module Range =
/// | ^ true
/// ^^^^^^^^ false
/// ```
- let isDisjointLoosely (range1: Range) (range2: Range) =
- not <| overlapsLoosely range1 range2
+ let isDisjointLoosely (range1: Range) (range2: Range) = not <| overlapsLoosely range1 range2
module Text =
@@ -196,45 +191,33 @@ module Text =
let inline assertNoCarriageReturn (text: string) =
if text.Contains '\r' then
Expecto.Tests.failtest "Text contains `\\r` (either alone or as `\\r\\n`). But only `\\n` is supported"
- let removeCarriageReturn (text: string) =
- text.Replace("\r\n", "\n").Replace("\r", "\n")
+
+ let removeCarriageReturn (text: string) = text.Replace("\r\n", "\n").Replace("\r", "\n")
/// Note: only works with `\n`, but fails with `\r`!
- let lines (text: string) =
+ let lines (text: string) =
assertNoCarriageReturn text
text.Split '\n'
- /// remove leading `\n` from triple quoted string with text starting in next line
- let private trimLeadingNewLine (text: string) =
- if text.StartsWith '\n' then
- text.Substring 1
- else
- text
- /// remove trailing whitespace from last line, if last line is otherwise empty.
- /// Note: keeps the `\n`!
+ /// remove leading `\n` from triple quoted string with text starting in next line
+ let private trimLeadingNewLine (text: string) = if text.StartsWith '\n' then text.Substring 1 else text
+
+ /// remove trailing whitespace from last line, if last line is otherwise empty.
+ /// Note: keeps the `\n`!
/// Note: doesn't trim a single line with just whitespace -> requires at least one `\n`
let private trimLastWhitespacesLine (text: string) =
match text.LastIndexOf '\n' with
| -1 -> text
| i ->
- let tail = text.AsSpan(i+1)
- if not tail.IsEmpty && tail.IsWhiteSpace() then
- text.Substring(0, i+1)
- else
- text
- /// remove trailing last line, if last line is empty.
- /// Unlike `trimLastWhitespacesLine` this removes the trailing `\n` too
- /// Note: doesn't trim a single line with just whitespace -> requires at least one `\n`
- let private trimTrailingEmptyLine (text: string) =
- match text.LastIndexOf '\n' with
- | -1 ->
+ let tail = text.AsSpan(i + 1)
+
+ if not tail.IsEmpty && tail.IsWhiteSpace() then
+ text.Substring(0, i + 1)
+ else
text
- | i when text.AsSpan().Slice(i).IsWhiteSpace() -> // `\n` is whitespace
- text.Substring(0, i)
- | _ -> text
- let getIndentation (line: string) =
- line.Length - line.AsSpan().TrimStart().Length
+ let getIndentation (line: string) = line.Length - line.AsSpan().TrimStart().Length
+
let private detectIndentation (text: string) =
text
|> lines
@@ -247,29 +230,25 @@ module Text =
match text |> detectIndentation with
| 0 -> text
| ind ->
- text
- |> lines
- |> Seq.map (fun line ->
- if line.Length <= ind then
- assert(line |> String.IsNullOrWhiteSpace)
- ""
- else
- line.Substring ind
- )
- |> String.concat "\n"
-
+ text
+ |> lines
+ |> Seq.map (fun line ->
+ if line.Length <= ind then
+ assert (line |> String.IsNullOrWhiteSpace)
+ ""
+ else
+ line.Substring ind)
+ |> String.concat "\n"
+
/// Trim:
/// * Leading `\n` from triple quotes string with text starting in next line
/// * indentation (measured for non-empty lines)
- /// * Trailing whitespace in otherwise empty last line
+ /// * Trailing whitespace in otherwise empty last line
/// Note: `\n` isn't removed
- ///
+ ///
/// Note: Asserts the passed text contains no `\r` (neither `\r` nor `\r\n`).
/// It doesn't replace `\r` with `\n` but instead fails!
let trimTripleQuotation (text: string) =
assertNoCarriageReturn text
- text
- |> trimLeadingNewLine
- |> trimIndentation
- |> trimLastWhitespacesLine
+ text |> trimLeadingNewLine |> trimIndentation |> trimLastWhitespacesLine
diff --git a/test/OptionAnalyzer/Analyzer.fs b/test/OptionAnalyzer/Analyzer.fs
index 6517d243a..e10311ef3 100644
--- a/test/OptionAnalyzer/Analyzer.fs
+++ b/test/OptionAnalyzer/Analyzer.fs
@@ -6,146 +6,142 @@ open FSharp.Compiler.Symbols
open FSharp.Compiler.Text
open FsAutoComplete.Logging
-let rec visitExpr memberCallHandler (e:FSharpExpr) =
- match e with
- | FSharpExprPatterns.AddressOf(lvalueExpr) ->
- visitExpr memberCallHandler lvalueExpr
- | FSharpExprPatterns.AddressSet(lvalueExpr, rvalueExpr) ->
- visitExpr memberCallHandler lvalueExpr; visitExpr memberCallHandler rvalueExpr
- | FSharpExprPatterns.Application(funcExpr, typeArgs, argExprs) ->
- visitExpr memberCallHandler funcExpr; visitExprs memberCallHandler argExprs
- | FSharpExprPatterns.Call(objExprOpt, memberOrFunc, typeArgs1, typeArgs2, argExprs) ->
- memberCallHandler e.Range memberOrFunc
- visitObjArg memberCallHandler objExprOpt; visitExprs memberCallHandler argExprs
- | FSharpExprPatterns.Coerce(targetType, inpExpr) ->
- visitExpr memberCallHandler inpExpr
- | FSharpExprPatterns.FastIntegerForLoop(startExpr, limitExpr, consumeExpr, isUp, _, _) ->
- visitExpr memberCallHandler startExpr; visitExpr memberCallHandler limitExpr; visitExpr memberCallHandler consumeExpr
- | FSharpExprPatterns.ILAsm(asmCode, typeArgs, argExprs) ->
- visitExprs memberCallHandler argExprs
- | FSharpExprPatterns.ILFieldGet (objExprOpt, fieldType, fieldName) ->
- visitObjArg memberCallHandler objExprOpt
- | FSharpExprPatterns.ILFieldSet (objExprOpt, fieldType, fieldName, valueExpr) ->
- visitObjArg memberCallHandler objExprOpt
- | FSharpExprPatterns.IfThenElse (guardExpr, thenExpr, elseExpr) ->
- visitExpr memberCallHandler guardExpr; visitExpr memberCallHandler thenExpr; visitExpr memberCallHandler elseExpr
- | FSharpExprPatterns.Lambda(lambdaVar, bodyExpr) ->
- visitExpr memberCallHandler bodyExpr
- | FSharpExprPatterns.Let((bindingVar, bindingExpr, _), bodyExpr) ->
- visitExpr memberCallHandler bindingExpr; visitExpr memberCallHandler bodyExpr
- | FSharpExprPatterns.LetRec(recursiveBindings, bodyExpr) ->
- List.iter ((fun (_, x, _) -> x) >> visitExpr memberCallHandler) recursiveBindings; visitExpr memberCallHandler bodyExpr
- | FSharpExprPatterns.NewArray(arrayType, argExprs) ->
- visitExprs memberCallHandler argExprs
- | FSharpExprPatterns.NewDelegate(delegateType, delegateBodyExpr) ->
- visitExpr memberCallHandler delegateBodyExpr
- | FSharpExprPatterns.NewObject(objType, typeArgs, argExprs) ->
- visitExprs memberCallHandler argExprs
- | FSharpExprPatterns.NewRecord(recordType, argExprs) ->
- visitExprs memberCallHandler argExprs
- | FSharpExprPatterns.NewTuple(tupleType, argExprs) ->
- visitExprs memberCallHandler argExprs
- | FSharpExprPatterns.NewUnionCase(unionType, unionCase, argExprs) ->
- visitExprs memberCallHandler argExprs
- | FSharpExprPatterns.Quote(quotedExpr) ->
- visitExpr memberCallHandler quotedExpr
- | FSharpExprPatterns.FSharpFieldGet(objExprOpt, recordOrClassType, fieldInfo) ->
- visitObjArg memberCallHandler objExprOpt
- | FSharpExprPatterns.FSharpFieldSet(objExprOpt, recordOrClassType, fieldInfo, argExpr) ->
- visitObjArg memberCallHandler objExprOpt; visitExpr memberCallHandler argExpr
- | FSharpExprPatterns.Sequential(firstExpr, secondExpr) ->
- visitExpr memberCallHandler firstExpr; visitExpr memberCallHandler secondExpr
- | FSharpExprPatterns.TryFinally(bodyExpr, finalizeExpr, _, _) ->
- visitExpr memberCallHandler bodyExpr; visitExpr memberCallHandler finalizeExpr
- | FSharpExprPatterns.TryWith(bodyExpr, _, _, catchVar, catchExpr, _, _) ->
- visitExpr memberCallHandler bodyExpr; visitExpr memberCallHandler catchExpr
- | FSharpExprPatterns.TupleGet(tupleType, tupleElemIndex, tupleExpr) ->
- visitExpr memberCallHandler tupleExpr
- | FSharpExprPatterns.DecisionTree(decisionExpr, decisionTargets) ->
- visitExpr memberCallHandler decisionExpr; List.iter (snd >> visitExpr memberCallHandler) decisionTargets
- | FSharpExprPatterns.DecisionTreeSuccess (decisionTargetIdx, decisionTargetExprs) ->
- visitExprs memberCallHandler decisionTargetExprs
- | FSharpExprPatterns.TypeLambda(genericParam, bodyExpr) ->
- visitExpr memberCallHandler bodyExpr
- | FSharpExprPatterns.TypeTest(ty, inpExpr) ->
- visitExpr memberCallHandler inpExpr
- | FSharpExprPatterns.UnionCaseSet(unionExpr, unionType, unionCase, unionCaseField, valueExpr) ->
- visitExpr memberCallHandler unionExpr; visitExpr memberCallHandler valueExpr
- | FSharpExprPatterns.UnionCaseGet(unionExpr, unionType, unionCase, unionCaseField) ->
- visitExpr memberCallHandler unionExpr
- | FSharpExprPatterns.UnionCaseTest(unionExpr, unionType, unionCase) ->
- visitExpr memberCallHandler unionExpr
- | FSharpExprPatterns.UnionCaseTag(unionExpr, unionType) ->
- visitExpr memberCallHandler unionExpr
- | FSharpExprPatterns.ObjectExpr(objType, baseCallExpr, overrides, interfaceImplementations) ->
- visitExpr memberCallHandler baseCallExpr
- List.iter (visitObjMember memberCallHandler) overrides
- List.iter (snd >> List.iter (visitObjMember memberCallHandler)) interfaceImplementations
- | FSharpExprPatterns.TraitCall(sourceTypes, traitName, typeArgs, typeInstantiation, argTypes, argExprs) ->
- visitExprs memberCallHandler argExprs
- | FSharpExprPatterns.ValueSet(valToSet, valueExpr) ->
- visitExpr memberCallHandler valueExpr
- | FSharpExprPatterns.WhileLoop(guardExpr, bodyExpr, _) ->
- visitExpr memberCallHandler guardExpr; visitExpr memberCallHandler bodyExpr
- | FSharpExprPatterns.BaseValue baseType -> ()
- | FSharpExprPatterns.DefaultValue defaultType -> ()
- | FSharpExprPatterns.ThisValue thisType -> ()
- | FSharpExprPatterns.Const(constValueObj, constType) -> ()
- | FSharpExprPatterns.Value(valueToGet) -> ()
- | _ -> ()
-
-and visitExprs f exprs =
- List.iter (visitExpr f) exprs
-
-and visitObjArg f objOpt =
- Option.iter (visitExpr f) objOpt
-
-and visitObjMember f memb =
- visitExpr f memb.Body
+let rec visitExpr memberCallHandler (e: FSharpExpr) =
+ match e with
+ | FSharpExprPatterns.AddressOf(lvalueExpr) -> visitExpr memberCallHandler lvalueExpr
+ | FSharpExprPatterns.AddressSet(lvalueExpr, rvalueExpr) ->
+ visitExpr memberCallHandler lvalueExpr
+ visitExpr memberCallHandler rvalueExpr
+ | FSharpExprPatterns.Application(funcExpr, _, argExprs) ->
+ visitExpr memberCallHandler funcExpr
+ visitExprs memberCallHandler argExprs
+ | FSharpExprPatterns.Call(objExprOpt, memberOrFunc, _, _, argExprs) ->
+ memberCallHandler e.Range memberOrFunc
+ visitObjArg memberCallHandler objExprOpt
+ visitExprs memberCallHandler argExprs
+ | FSharpExprPatterns.Coerce(_, inpExpr) -> visitExpr memberCallHandler inpExpr
+ | FSharpExprPatterns.FastIntegerForLoop(startExpr, limitExpr, consumeExpr, _, _, _) ->
+ visitExpr memberCallHandler startExpr
+ visitExpr memberCallHandler limitExpr
+ visitExpr memberCallHandler consumeExpr
+ | FSharpExprPatterns.ILAsm(_, _, argExprs) -> visitExprs memberCallHandler argExprs
+ | FSharpExprPatterns.ILFieldGet(objExprOpt, _, _) -> visitObjArg memberCallHandler objExprOpt
+ | FSharpExprPatterns.ILFieldSet(objExprOpt, _, _, _) -> visitObjArg memberCallHandler objExprOpt
+ | FSharpExprPatterns.IfThenElse(guardExpr, thenExpr, elseExpr) ->
+ visitExpr memberCallHandler guardExpr
+ visitExpr memberCallHandler thenExpr
+ visitExpr memberCallHandler elseExpr
+ | FSharpExprPatterns.Lambda(_, bodyExpr) -> visitExpr memberCallHandler bodyExpr
+ | FSharpExprPatterns.Let((_, bindingExpr, _), bodyExpr) ->
+ visitExpr memberCallHandler bindingExpr
+ visitExpr memberCallHandler bodyExpr
+ | FSharpExprPatterns.LetRec(recursiveBindings, bodyExpr) ->
+ List.iter ((fun (_, x, _) -> x) >> visitExpr memberCallHandler) recursiveBindings
+ visitExpr memberCallHandler bodyExpr
+ | FSharpExprPatterns.NewArray(_, argExprs) -> visitExprs memberCallHandler argExprs
+ | FSharpExprPatterns.NewDelegate(_, delegateBodyExpr) -> visitExpr memberCallHandler delegateBodyExpr
+ | FSharpExprPatterns.NewObject(_, _, argExprs) -> visitExprs memberCallHandler argExprs
+ | FSharpExprPatterns.NewRecord(_, argExprs) -> visitExprs memberCallHandler argExprs
+ | FSharpExprPatterns.NewTuple(_, argExprs) -> visitExprs memberCallHandler argExprs
+ | FSharpExprPatterns.NewUnionCase(_, _, argExprs) -> visitExprs memberCallHandler argExprs
+ | FSharpExprPatterns.Quote(quotedExpr) -> visitExpr memberCallHandler quotedExpr
+ | FSharpExprPatterns.FSharpFieldGet(objExprOpt, _, _) -> visitObjArg memberCallHandler objExprOpt
+ | FSharpExprPatterns.FSharpFieldSet(objExprOpt, _, _, argExpr) ->
+ visitObjArg memberCallHandler objExprOpt
+ visitExpr memberCallHandler argExpr
+ | FSharpExprPatterns.Sequential(firstExpr, secondExpr) ->
+ visitExpr memberCallHandler firstExpr
+ visitExpr memberCallHandler secondExpr
+ | FSharpExprPatterns.TryFinally(bodyExpr, finalizeExpr, _, _) ->
+ visitExpr memberCallHandler bodyExpr
+ visitExpr memberCallHandler finalizeExpr
+ | FSharpExprPatterns.TryWith(bodyExpr, _, _, _, catchExpr, _, _) ->
+ visitExpr memberCallHandler bodyExpr
+ visitExpr memberCallHandler catchExpr
+ | FSharpExprPatterns.TupleGet(_, _, tupleExpr) -> visitExpr memberCallHandler tupleExpr
+ | FSharpExprPatterns.DecisionTree(decisionExpr, decisionTargets) ->
+ visitExpr memberCallHandler decisionExpr
+ List.iter (snd >> visitExpr memberCallHandler) decisionTargets
+ | FSharpExprPatterns.DecisionTreeSuccess(_, decisionTargetExprs) -> visitExprs memberCallHandler decisionTargetExprs
+ | FSharpExprPatterns.TypeLambda(_, bodyExpr) -> visitExpr memberCallHandler bodyExpr
+ | FSharpExprPatterns.TypeTest(_, inpExpr) -> visitExpr memberCallHandler inpExpr
+ | FSharpExprPatterns.UnionCaseSet(unionExpr, _, _, _, valueExpr) ->
+ visitExpr memberCallHandler unionExpr
+ visitExpr memberCallHandler valueExpr
+ | FSharpExprPatterns.UnionCaseGet(unionExpr, _, _, _) -> visitExpr memberCallHandler unionExpr
+ | FSharpExprPatterns.UnionCaseTest(unionExpr, _, _) -> visitExpr memberCallHandler unionExpr
+ | FSharpExprPatterns.UnionCaseTag(unionExpr, _) -> visitExpr memberCallHandler unionExpr
+ | FSharpExprPatterns.ObjectExpr(_, baseCallExpr, overrides, interfaceImplementations) ->
+ visitExpr memberCallHandler baseCallExpr
+ List.iter (visitObjMember memberCallHandler) overrides
+ List.iter (snd >> List.iter (visitObjMember memberCallHandler)) interfaceImplementations
+ | FSharpExprPatterns.TraitCall(_, _, _, _, _, argExprs) -> visitExprs memberCallHandler argExprs
+ | FSharpExprPatterns.ValueSet(_, valueExpr) -> visitExpr memberCallHandler valueExpr
+ | FSharpExprPatterns.WhileLoop(guardExpr, bodyExpr, _) ->
+ visitExpr memberCallHandler guardExpr
+ visitExpr memberCallHandler bodyExpr
+ | FSharpExprPatterns.BaseValue _ -> ()
+ | FSharpExprPatterns.DefaultValue _ -> ()
+ | FSharpExprPatterns.ThisValue _ -> ()
+ | FSharpExprPatterns.Const(_, _) -> ()
+ | FSharpExprPatterns.Value _ -> ()
+ | _ -> ()
+
+and visitExprs f exprs = List.iter (visitExpr f) exprs
+
+and visitObjArg f objOpt = Option.iter (visitExpr f) objOpt
+
+and visitObjMember f memb = visitExpr f memb.Body
let rec visitDeclaration f d =
- match d with
- | FSharpImplementationFileDeclaration.Entity (e, subDecls) ->
- for subDecl in subDecls do
- visitDeclaration f subDecl
- | FSharpImplementationFileDeclaration.MemberOrFunctionOrValue(v, vs, e) ->
- visitExpr f e
- | FSharpImplementationFileDeclaration.InitAction(e) ->
- visitExpr f e
-
-let notUsed() =
- let option : Option = None
- option.Value
+ match d with
+ | FSharpImplementationFileDeclaration.Entity(_, subDecls) ->
+ for subDecl in subDecls do
+ visitDeclaration f subDecl
+ | FSharpImplementationFileDeclaration.MemberOrFunctionOrValue(_, _, e) -> visitExpr f e
+ | FSharpImplementationFileDeclaration.InitAction(e) -> visitExpr f e
+
+let notUsed () =
+ let option: Option = None
+ option.Value
let logger = LogProvider.getLoggerByName "OptionAnalyzer"
let info message items =
let mutable log = Log.setMessage message
- for (name, value) in items do
+
+ for name, value in items do
log <- log >> Log.addContextDestructured name value
+
logger.info log
let inline (==>) x y = x, box y
[]
-let optionValueAnalyzer : Analyzer =
+let optionValueAnalyzer: Analyzer =
fun ctx ->
info "analyzing {file} for uses of Option.Value" [ "file" ==> ctx.FileName ]
let state = ResizeArray()
+
let handler (range: Range) (m: FSharpMemberOrFunctionOrValue) =
- let rangeString = sprintf "(%d,%d)-(%d,%d)" range.Start.Line range.Start.Column range.End.Line range.End.Column
+ let rangeString =
+ sprintf "(%d,%d)-(%d,%d)" range.Start.Line range.Start.Column range.End.Line range.End.Column
+
let name = String.Join(".", m.DeclaringEntity.Value.FullName, m.DisplayName)
- info "checking value at {range} with name {name}" [ "range" ==> rangeString
- "name" ==> name ]
+ info "checking value at {range} with name {name}" [ "range" ==> rangeString; "name" ==> name ]
+
if name = "Microsoft.FSharp.Core.FSharpOption`1.Value" then
info "matched at range {range}" [ "range" ==> rangeString ]
state.Add range
+
ctx.TypedTree.Declarations |> List.iter (visitDeclaration handler)
+
state
- |> Seq.map (fun r -> { Type = "Option.Value analyzer"
- Message = "Option.Value shouldn't be used"
- Code = "OV001"
- Severity = Warning
- Range = r
- Fixes = [] })
+ |> Seq.map (fun r ->
+ { Type = "Option.Value analyzer"
+ Message = "Option.Value shouldn't be used"
+ Code = "OV001"
+ Severity = Warning
+ Range = r
+ Fixes = [] })
|> Seq.toList