未验证 提交 a5dfa94f 编写于 作者: P Petr 提交者: GitHub

Inline type hints for F# (#14159)

上级 0a7260a4
......@@ -126,6 +126,9 @@
<Compile Include="Build\SetGlobalPropertiesForSdkProjects.fs" />
<Compile Include="AutomaticCompletion\BraceCompletionSessionProvider.fsi" />
<Compile Include="AutomaticCompletion\BraceCompletionSessionProvider.fs" />
<Compile Include="Hints\HintService.fs" />
<Compile Include="Hints\NativeToRoslynHintConverter.fs" />
<Compile Include="Hints\RoslynAdapter.fs" />
<Compile Include="Lens\LensDisplayService.fs" />
<Compile Include="Lens\LensService.fs" />
<Compile Include="Lens\LensProvider.fs" />
......
// Copyright (c) Microsoft Corporation. All Rights Reserved. See License.txt in the project root for license information.
namespace Microsoft.VisualStudio.FSharp.Editor.Hints
open Microsoft.CodeAnalysis
open Microsoft.VisualStudio.FSharp.Editor
open FSharp.Compiler.CodeAnalysis
open FSharp.Compiler.Symbols
open FSharp.Compiler.Text
module HintService =
// Relatively convenient for testing
type NativeHint = {
Range: range
Parts: TaggedText list
}
let private isValidForHint
(parseFileResults: FSharpParseFileResults)
(symbol: FSharpMemberOrFunctionOrValue)
(symbolUse: FSharpSymbolUse) =
let isNotAnnotatedManually =
not (parseFileResults.IsTypeAnnotationGivenAtPosition symbolUse.Range.Start)
let isNotAfterDot =
symbolUse.IsFromDefinition
&& not symbol.IsMemberThisValue
let isNotTypeAlias =
not symbol.IsConstructorThisValue
symbol.IsValue // we'll be adding other stuff gradually here
&& isNotAnnotatedManually
&& isNotAfterDot
&& isNotTypeAlias
let private getHintParts
(symbol: FSharpMemberOrFunctionOrValue)
(symbolUse: FSharpSymbolUse) =
match symbol.GetReturnTypeLayout symbolUse.DisplayContext with
| Some typeInfo ->
let colon = TaggedText(TextTag.Text, ": ")
colon :: (typeInfo |> Array.toList)
// not sure when this can happen but better safe than sorry
| None ->
[]
let private getHintsForSymbol parseResults (symbolUse: FSharpSymbolUse) =
match symbolUse.Symbol with
| :? FSharpMemberOrFunctionOrValue as mfvSymbol
when isValidForHint parseResults mfvSymbol symbolUse ->
[ {
Range = symbolUse.Range
Parts = getHintParts mfvSymbol symbolUse
} ]
// we'll be adding other stuff gradually here
| _ ->
[]
let getHintsForDocument (document: Document) userOpName cancellationToken =
async {
if isSignatureFile document.FilePath
then
return []
else
let! parseResults, checkResults =
document.GetFSharpParseAndCheckResultsAsync userOpName
return
checkResults.GetAllUsesOfAllSymbolsInFile cancellationToken
|> Seq.toList
|> List.collect (getHintsForSymbol parseResults)
}
// Copyright (c) Microsoft Corporation. All Rights Reserved. See License.txt in the project root for license information.
namespace Microsoft.VisualStudio.FSharp.Editor.Hints
open System.Collections.Immutable
open Microsoft.CodeAnalysis.Text
open Microsoft.CodeAnalysis.ExternalAccess.FSharp.InlineHints
open Microsoft.VisualStudio.FSharp.Editor
open FSharp.Compiler.Text
open HintService
module NativeToRoslynHintConverter =
let rangeToSpan range sourceText =
let symbolSpan = RoslynHelpers.FSharpRangeToTextSpan(sourceText, range)
let overshadowLength = 0 // anything >0 means overlaying the code
TextSpan(symbolSpan.End, overshadowLength)
let nativeToRoslynText (taggedText: TaggedText) =
let tag = RoslynHelpers.roslynTag taggedText.Tag
let text = taggedText.Text
RoslynTaggedText(tag, text)
let convert sourceText hint =
let span = rangeToSpan hint.Range sourceText
let displayParts = hint.Parts |> Seq.map nativeToRoslynText
FSharpInlineHint(span, displayParts.ToImmutableArray())
\ No newline at end of file
// Copyright (c) Microsoft Corporation. All Rights Reserved. See License.txt in the project root for license information.
namespace Microsoft.VisualStudio.FSharp.Editor.Hints
open System.Collections.Immutable
open System.ComponentModel.Composition
open Microsoft.CodeAnalysis.ExternalAccess.FSharp.InlineHints
open Microsoft.VisualStudio.FSharp.Editor
// So the Roslyn interface is called IFSharpInlineHintsService
// but our implementation is called just HintsService.
// That's because we'll likely use this API for things other than inline hints,
// e.g. signature hints above the line, pipeline hints on the side and so on.
[<Export(typeof<IFSharpInlineHintsService>)>]
type internal RoslynAdapter
[<ImportingConstructor>]
(settings: EditorOptions) =
static let userOpName = "Hints"
interface IFSharpInlineHintsService with
member _.GetInlineHintsAsync(document, _, cancellationToken) =
async {
if not settings.Advanced.IsInlineHintsEnabled
then return ImmutableArray.Empty
else
let! sourceText = document.GetTextAsync cancellationToken |> Async.AwaitTask
let! nativeHints =
HintService.getHintsForDocument document userOpName cancellationToken
let roslynHints =
nativeHints
|> Seq.map (NativeToRoslynHintConverter.convert sourceText)
return roslynHints.ToImmutableArray()
} |> RoslynHelpers.StartAsyncAsTask cancellationToken
......@@ -85,10 +85,12 @@ type LensOptions =
[<CLIMutable>]
type AdvancedOptions =
{ IsBlockStructureEnabled: bool
IsOutliningEnabled: bool }
IsOutliningEnabled: bool
IsInlineHintsEnabled: bool }
static member Default =
{ IsBlockStructureEnabled = true
IsOutliningEnabled = true }
IsOutliningEnabled = true
IsInlineHintsEnabled = false }
[<CLIMutable>]
type FormattingOptions =
......
......@@ -24,6 +24,10 @@
<CheckBox x:Name="toggleOutloning" IsChecked="{Binding IsOutliningEnabled}"
Content="{x:Static local:Strings.Show_Outlining}"/>
</GroupBox>
<GroupBox Header="{x:Static local:Strings.Inline_Hints}">
<CheckBox x:Name="toggleInlineHints" IsChecked="{Binding IsInlineHintsEnabled}"
Content="{x:Static local:Strings.Show_Inline_Hints}"/>
</GroupBox>
</StackPanel>
</ScrollViewer>
</Grid>
......
......@@ -204,6 +204,15 @@ public class Strings {
}
}
/// <summary>
/// Looks up a localized string similar to Inline Hints.
/// </summary>
public static string Inline_Hints {
get {
return ResourceManager.GetString("Inline_Hints", resourceCulture);
}
}
/// <summary>
/// Looks up a localized string similar to IntelliSense Performance Options.
/// </summary>
......@@ -330,6 +339,15 @@ public class Strings {
}
}
/// <summary>
/// Looks up a localized string similar to Display inline type hints (experimental).
/// </summary>
public static string Show_Inline_Hints {
get {
return ResourceManager.GetString("Show_Inline_Hints", resourceCulture);
}
}
/// <summary>
/// Looks up a localized string similar to S_how navigation links as.
/// </summary>
......
......@@ -189,6 +189,12 @@
<data name="Show_Outlining" xml:space="preserve">
<value>Show outlining and collapsible nodes for F# code</value>
</data>
<data name="Inline_Hints" xml:space="preserve">
<value>Inline Hints</value>
</data>
<data name="Show_Inline_Hints" xml:space="preserve">
<value>Display inline type hints (experimental)</value>
</data>
<data name="Time_until_stale_completion" xml:space="preserve">
<value>Time until stale results are used (in milliseconds)</value>
</data>
......
......@@ -7,6 +7,11 @@
<target state="translated">Vždy umístit otevřené příkazy na nejvyšší úroveň</target>
<note />
</trans-unit>
<trans-unit id="Inline_Hints">
<source>Inline Hints</source>
<target state="new">Inline Hints</target>
<note />
</trans-unit>
<trans-unit id="Lens">
<source>Lens</source>
<target state="translated">Lens</target>
......@@ -77,6 +82,11 @@
<target state="new">Parallelization (requires restart)</target>
<note />
</trans-unit>
<trans-unit id="Show_Inline_Hints">
<source>Display inline type hints (experimental)</source>
<target state="new">Display inline type hints (experimental)</target>
<note />
</trans-unit>
<trans-unit id="Show_all_symbols">
<source>Show s_ymbols in unopened namespaces</source>
<target state="translated">Zobrazit s_ymboly v neotevřených oborech názvů</target>
......
......@@ -7,6 +7,11 @@
<target state="translated">open-Anweisungen immer an oberster Ebene platzieren</target>
<note />
</trans-unit>
<trans-unit id="Inline_Hints">
<source>Inline Hints</source>
<target state="new">Inline Hints</target>
<note />
</trans-unit>
<trans-unit id="Lens">
<source>Lens</source>
<target state="translated">Lens</target>
......@@ -77,6 +82,11 @@
<target state="new">Parallelization (requires restart)</target>
<note />
</trans-unit>
<trans-unit id="Show_Inline_Hints">
<source>Display inline type hints (experimental)</source>
<target state="new">Display inline type hints (experimental)</target>
<note />
</trans-unit>
<trans-unit id="Show_all_symbols">
<source>Show s_ymbols in unopened namespaces</source>
<target state="translated">S_ymbole in nicht geöffneten Namespaces anzeigen</target>
......
......@@ -7,6 +7,11 @@
<target state="translated">Colocar siempre las instrucciones open en el nivel superior</target>
<note />
</trans-unit>
<trans-unit id="Inline_Hints">
<source>Inline Hints</source>
<target state="new">Inline Hints</target>
<note />
</trans-unit>
<trans-unit id="Lens">
<source>Lens</source>
<target state="translated">Lens</target>
......@@ -77,6 +82,11 @@
<target state="new">Parallelization (requires restart)</target>
<note />
</trans-unit>
<trans-unit id="Show_Inline_Hints">
<source>Display inline type hints (experimental)</source>
<target state="new">Display inline type hints (experimental)</target>
<note />
</trans-unit>
<trans-unit id="Show_all_symbols">
<source>Show s_ymbols in unopened namespaces</source>
<target state="translated">Mostrar sím_bolos en espacios de nombres sin abrir</target>
......
......@@ -7,6 +7,11 @@
<target state="translated">Placer toujours les instructions open au niveau supérieur</target>
<note />
</trans-unit>
<trans-unit id="Inline_Hints">
<source>Inline Hints</source>
<target state="new">Inline Hints</target>
<note />
</trans-unit>
<trans-unit id="Lens">
<source>Lens</source>
<target state="translated">Lens</target>
......@@ -77,6 +82,11 @@
<target state="new">Parallelization (requires restart)</target>
<note />
</trans-unit>
<trans-unit id="Show_Inline_Hints">
<source>Display inline type hints (experimental)</source>
<target state="new">Display inline type hints (experimental)</target>
<note />
</trans-unit>
<trans-unit id="Show_all_symbols">
<source>Show s_ymbols in unopened namespaces</source>
<target state="translated">Afficher les sym_boles dans les espaces de noms non ouverts</target>
......
......@@ -7,6 +7,11 @@
<target state="translated">Inserisci sempre le istruzioni OPEN al primo livello</target>
<note />
</trans-unit>
<trans-unit id="Inline_Hints">
<source>Inline Hints</source>
<target state="new">Inline Hints</target>
<note />
</trans-unit>
<trans-unit id="Lens">
<source>Lens</source>
<target state="translated">Lens</target>
......@@ -77,6 +82,11 @@
<target state="new">Parallelization (requires restart)</target>
<note />
</trans-unit>
<trans-unit id="Show_Inline_Hints">
<source>Display inline type hints (experimental)</source>
<target state="new">Display inline type hints (experimental)</target>
<note />
</trans-unit>
<trans-unit id="Show_all_symbols">
<source>Show s_ymbols in unopened namespaces</source>
<target state="translated">Mostra si_mboli in spazi dei nomi non aperti</target>
......
......@@ -7,6 +7,11 @@
<target state="translated">Open ステートメントを常に最上位に配置する</target>
<note />
</trans-unit>
<trans-unit id="Inline_Hints">
<source>Inline Hints</source>
<target state="new">Inline Hints</target>
<note />
</trans-unit>
<trans-unit id="Lens">
<source>Lens</source>
<target state="translated">Lens</target>
......@@ -77,6 +82,11 @@
<target state="new">Parallelization (requires restart)</target>
<note />
</trans-unit>
<trans-unit id="Show_Inline_Hints">
<source>Display inline type hints (experimental)</source>
<target state="new">Display inline type hints (experimental)</target>
<note />
</trans-unit>
<trans-unit id="Show_all_symbols">
<source>Show s_ymbols in unopened namespaces</source>
<target state="translated">開かれていない名前空間の記号を表示する(_Y)</target>
......
......@@ -7,6 +7,11 @@
<target state="translated">항상 최상위에 open 문 배치</target>
<note />
</trans-unit>
<trans-unit id="Inline_Hints">
<source>Inline Hints</source>
<target state="new">Inline Hints</target>
<note />
</trans-unit>
<trans-unit id="Lens">
<source>Lens</source>
<target state="translated">Lens</target>
......@@ -77,6 +82,11 @@
<target state="new">Parallelization (requires restart)</target>
<note />
</trans-unit>
<trans-unit id="Show_Inline_Hints">
<source>Display inline type hints (experimental)</source>
<target state="new">Display inline type hints (experimental)</target>
<note />
</trans-unit>
<trans-unit id="Show_all_symbols">
<source>Show s_ymbols in unopened namespaces</source>
<target state="translated">열려 있지 않은 네임스페이스에 기호 표시(_Y)</target>
......
......@@ -7,6 +7,11 @@
<target state="translated">Zawsze umieszczaj otwarte instrukcje na najwyższym poziomie</target>
<note />
</trans-unit>
<trans-unit id="Inline_Hints">
<source>Inline Hints</source>
<target state="new">Inline Hints</target>
<note />
</trans-unit>
<trans-unit id="Lens">
<source>Lens</source>
<target state="translated">Lens</target>
......@@ -77,6 +82,11 @@
<target state="new">Parallelization (requires restart)</target>
<note />
</trans-unit>
<trans-unit id="Show_Inline_Hints">
<source>Display inline type hints (experimental)</source>
<target state="new">Display inline type hints (experimental)</target>
<note />
</trans-unit>
<trans-unit id="Show_all_symbols">
<source>Show s_ymbols in unopened namespaces</source>
<target state="translated">Pokaż s_ymbole w nieotwartych przestrzeniach nazw</target>
......
......@@ -7,6 +7,11 @@
<target state="translated">Sempre coloque as instruções abertas no nível superior</target>
<note />
</trans-unit>
<trans-unit id="Inline_Hints">
<source>Inline Hints</source>
<target state="new">Inline Hints</target>
<note />
</trans-unit>
<trans-unit id="Lens">
<source>Lens</source>
<target state="translated">Lens</target>
......@@ -77,6 +82,11 @@
<target state="new">Parallelization (requires restart)</target>
<note />
</trans-unit>
<trans-unit id="Show_Inline_Hints">
<source>Display inline type hints (experimental)</source>
<target state="new">Display inline type hints (experimental)</target>
<note />
</trans-unit>
<trans-unit id="Show_all_symbols">
<source>Show s_ymbols in unopened namespaces</source>
<target state="translated">Mostrar s_ímbolos em namespaces não abertos</target>
......
......@@ -7,6 +7,11 @@
<target state="translated">Всегда располагайте открытые операторы на верхнем уровне</target>
<note />
</trans-unit>
<trans-unit id="Inline_Hints">
<source>Inline Hints</source>
<target state="new">Inline Hints</target>
<note />
</trans-unit>
<trans-unit id="Lens">
<source>Lens</source>
<target state="translated">Lens</target>
......@@ -77,6 +82,11 @@
<target state="new">Parallelization (requires restart)</target>
<note />
</trans-unit>
<trans-unit id="Show_Inline_Hints">
<source>Display inline type hints (experimental)</source>
<target state="new">Display inline type hints (experimental)</target>
<note />
</trans-unit>
<trans-unit id="Show_all_symbols">
<source>Show s_ymbols in unopened namespaces</source>
<target state="translated">По_казать символы в неоткрытых пространствах имен</target>
......
......@@ -7,6 +7,11 @@
<target state="translated">Açık deyimleri her zaman en üst düzeye yerleştir</target>
<note />
</trans-unit>
<trans-unit id="Inline_Hints">
<source>Inline Hints</source>
<target state="new">Inline Hints</target>
<note />
</trans-unit>
<trans-unit id="Lens">
<source>Lens</source>
<target state="translated">Lens</target>
......@@ -77,6 +82,11 @@
<target state="new">Parallelization (requires restart)</target>
<note />
</trans-unit>
<trans-unit id="Show_Inline_Hints">
<source>Display inline type hints (experimental)</source>
<target state="new">Display inline type hints (experimental)</target>
<note />
</trans-unit>
<trans-unit id="Show_all_symbols">
<source>Show s_ymbols in unopened namespaces</source>
<target state="translated">Açılmamış ad alanlarında s_embolleri göster</target>
......
......@@ -7,6 +7,11 @@
<target state="translated">始终在顶层放置 open 语句</target>
<note />
</trans-unit>
<trans-unit id="Inline_Hints">
<source>Inline Hints</source>
<target state="new">Inline Hints</target>
<note />
</trans-unit>
<trans-unit id="Lens">
<source>Lens</source>
<target state="translated">Lens</target>
......@@ -77,6 +82,11 @@
<target state="new">Parallelization (requires restart)</target>
<note />
</trans-unit>
<trans-unit id="Show_Inline_Hints">
<source>Display inline type hints (experimental)</source>
<target state="new">Display inline type hints (experimental)</target>
<note />
</trans-unit>
<trans-unit id="Show_all_symbols">
<source>Show s_ymbols in unopened namespaces</source>
<target state="translated">显示未打开的命名空间中的符号(_Y)</target>
......
......@@ -7,6 +7,11 @@
<target state="translated">一律將 open 陳述式放在最上層</target>
<note />
</trans-unit>
<trans-unit id="Inline_Hints">
<source>Inline Hints</source>
<target state="new">Inline Hints</target>
<note />
</trans-unit>
<trans-unit id="Lens">
<source>Lens</source>
<target state="translated">Lens</target>
......@@ -77,6 +82,11 @@
<target state="new">Parallelization (requires restart)</target>
<note />
</trans-unit>
<trans-unit id="Show_Inline_Hints">
<source>Display inline type hints (experimental)</source>
<target state="new">Display inline type hints (experimental)</target>
<note />
</trans-unit>
<trans-unit id="Show_all_symbols">
<source>Show s_ymbols in unopened namespaces</source>
<target state="translated">顯示未開啟之命名空間中的符號(_Y)</target>
......
// Copyright (c) Microsoft Corporation. All Rights Reserved. See License.txt in the project root for license information.
namespace VisualFSharp.UnitTests.Editor.Hints
open NUnit.Framework
open HintTestFramework
module HintServiceTests =
[<Test>]
let ``Type hints are not shown in signature files`` () =
let fsiCode = """
module Test
val numbers: int[]
"""
let fsCode = """
module Test
let numbers = [|42|]
"""
let fsiDocument, _ = getFsiAndFsDocuments fsiCode fsCode
let result = getHints fsiDocument
Assert.IsEmpty(result)
[<Test>]
let ``Type hints are not shown for let-bound functions yet`` () =
let code = """
let setConsoleOut = System.Console.SetOut
"""
let document = getFsDocument code
let result = getHints document
Assert.IsEmpty(result)
[<Test>]
let ``Type hint is shown for a let binding`` () =
let code = """
type Song = { Artist: string; Title: string }
let s = { Artist = "Moby"; Title = "Porcelain" }
"""
let document = getFsDocument code
let expected = [{ Content = ": Song"; Location = (3, 6) }]
let actual = getHints document
Assert.AreEqual(expected, actual)
[<Test>]
let ``Type hint is not shown for a let binding when the type is manually specified`` () =
let code = """
type Song = { Artist: string; Title: string }
let s: Song = { Artist = "Moby"; Title = "Porcelain" }
"""
let document = getFsDocument code
let result = getHints document
Assert.IsEmpty(result)
[<Test>]
let ``Type hint is shown for a parameter`` () =
let code = """
type Song = { Artist: string; Title: string }
let whoSings s = s.Artist
"""
let document = getFsDocument code
let expected = [{ Content = ": Song"; Location = (3, 15) }]
let actual = getHints document
Assert.AreEqual(expected, actual)
[<Test>]
let ``Type hint is not shown for a parameter when the type is manually specified`` () =
let code = """
type Song = { Artist: string; Title: string }
let whoSings (s: Song) = s.Artist
"""
let document = getFsDocument code
let result = getHints document
Assert.IsEmpty(result)
[<Test>] // here we don't want a hint after "this"
let ``Type hint is not shown for type self-identifiers`` () =
let code = """
type Song() =
member this.GetName() = "Porcelain"
"""
let document = getFsDocument code
let result = getHints document
Assert.IsEmpty(result)
[<Test>] // here we don't want a hint after "x"
let ``Type hint is not shown for type aliases`` () =
let code = """
type Song() as x =
member this.Name = "Porcelain"
"""
let document = getFsDocument code
let result = getHints document
Assert.IsEmpty(result)
// Copyright (c) Microsoft Corporation. All Rights Reserved. See License.txt in the project root for license information.
namespace VisualFSharp.UnitTests.Editor.Hints
open System.Threading
open Microsoft.VisualStudio.FSharp.Editor
open Microsoft.VisualStudio.FSharp.Editor.Hints
open Microsoft.VisualStudio.FSharp.Editor.Hints.HintService
open VisualFSharp.UnitTests.Editor
open Microsoft.CodeAnalysis.Text
module HintTestFramework =
// another representation for extra convenience
type TestHint = {
Content: string
Location: int * int
}
let private convert hint =
let content =
hint.Parts
|> Seq.map (fun hintPart -> hintPart.Text)
|> String.concat ""
// that's about different coordinate systems
// in tests, the most convenient is the one used in editor,
// hence this conversion
let location = (hint.Range.StartLine - 1, hint.Range.EndColumn + 1)
{ Content = content
Location = location }
let getFsDocument code =
use project = SingleFileProject code
let fileName = fst project.Files.Head
let document, _ = RoslynTestHelpers.CreateSingleDocumentSolution(fileName, code)
document
let getFsiAndFsDocuments (fsiCode: string) (fsCode: string) =
RoslynTestHelpers.CreateTwoDocumentSolution(
"test.fsi",
SourceText.From fsiCode,
"test.fs",
SourceText.From fsCode)
let getHints document =
async {
let! hints = HintService.getHintsForDocument document "test" CancellationToken.None
return hints |> Seq.map convert
}
|> Async.RunSynchronously
......@@ -273,7 +273,59 @@ type RoslynTestHelpers private () =
document
static member CreateTwoDocumentSolution (filePath1, text1: SourceText, filePath2, text2: SourceText) =
let workspace = new AdhocWorkspace(TestHostServices())
let projId = ProjectId.CreateNewId()
let docId1 = DocumentId.CreateNewId(projId)
let docId2 = DocumentId.CreateNewId(projId)
let docInfo1 =
DocumentInfo.Create(
docId1,
filePath1,
loader=TextLoader.From(text1.Container, VersionStamp.Create(DateTime.UtcNow)),
filePath=filePath1,
sourceCodeKind= SourceCodeKind.Regular)
let docInfo2 =
DocumentInfo.Create(
docId2,
filePath2,
loader=TextLoader.From(text2.Container, VersionStamp.Create(DateTime.UtcNow)),
filePath=filePath2,
sourceCodeKind= SourceCodeKind.Regular)
let projFilePath = "C:\\test.fsproj"
let projInfo =
ProjectInfo.Create(
projId,
VersionStamp.Create(DateTime.UtcNow),
projFilePath,
"test.dll",
LanguageNames.FSharp,
documents = [docInfo1;docInfo2],
filePath = projFilePath
)
let solutionInfo = SolutionInfo.Create(SolutionId.CreateNewId(), VersionStamp.Create(DateTime.UtcNow), "test.sln", [projInfo])
let solution = workspace.AddSolution(solutionInfo)
workspace
.Services
.GetService<IFSharpWorkspaceService>()
.FSharpProjectOptionsManager
.SetCommandLineOptions(projId, [|filePath1;filePath2|], ImmutableArray.Empty)
let document1 = solution.GetProject(projId).GetDocument(docId1)
let document2 = solution.GetProject(projId).GetDocument(docId2)
document1, document2
static member CreateSingleDocumentSolution (filePath, code: string, ?options) =
let text = SourceText.From(code)
RoslynTestHelpers.CreateSingleDocumentSolution(filePath, text, ?options = options), text
static member CreateTwoDocumentSolution (filePath1, code1: string, filePath2, code2: string) =
let text1 = SourceText.From code1
let text2 = SourceText.From code2
RoslynTestHelpers.CreateTwoDocumentSolution(filePath1, text1, filePath2, text2)
......@@ -102,6 +102,12 @@
<Compile Include="QuickInfoTests.fs">
<Link>Editor\QuickInfoTests.fs</Link>
</Compile>
<Compile Include="HintTestFramework.fs">
<Link>Editor\Hints\HintTestFramework.fs</Link>
</Compile>
<Compile Include="HintServiceTests.fs">
<Link>Editor\Hints\HintServiceTests.fs</Link>
</Compile>
<Compile Include="GoToDefinitionServiceTests.fs">
<Link>Editor\GoToDefinitionServiceTests.fs</Link>
</Compile>
......
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册