Skip to content

Commit 6dd739e

Browse files
committed
Initial generation signature feature toggle.
1 parent 525c85c commit 6dd739e

File tree

6 files changed

+134
-2
lines changed

6 files changed

+134
-2
lines changed

ReSharper.FSharp/src/FSharp.Common/src/Settings/FSharpOptions.fs

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -86,7 +86,7 @@ module FSharpExperimentalFeatures =
8686
let [<Literal>] outOfProcessTypeProviders = "Host type providers out-of-process"
8787
let [<Literal>] generativeTypeProvidersInMemoryAnalysis = "Enable generative type providers analysis in C#/VB.NET projects"
8888
let [<Literal>] tryRecoverFcsProjects = "Try to reuse FCS results on project changes"
89-
89+
let [<Literal>] generateSignatureFile = "Generate signature file"
9090

9191
[<SettingsKey(typeof<FSharpOptions>, "F# experimental features")>]
9292
type FSharpExperimentalFeatures =
@@ -109,8 +109,10 @@ type FSharpExperimentalFeatures =
109109
mutable GenerativeTypeProvidersInMemoryAnalysis: bool
110110

111111
[<SettingsEntry(false, FSharpExperimentalFeatures.tryRecoverFcsProjects); DefaultValue>]
112-
mutable TryRecoverFcsProjects: bool }
112+
mutable TryRecoverFcsProjects: bool
113113

114+
[<SettingsEntry(false, FSharpExperimentalFeatures.generateSignatureFile); DefaultValue>]
115+
mutable GenerateSignatureFile: bool }
114116

115117
[<AllowNullLiteral>]
116118
type FSharpSettingsProviderBase<'T>(lifetime: Lifetime, settings: IContextBoundSettingsStoreLive,
@@ -146,6 +148,7 @@ type FSharpExperimentalFeaturesProvider(lifetime, solution, settings, settingsSc
146148
member val OutOfProcessTypeProviders = base.GetValueProperty<bool>("OutOfProcessTypeProviders")
147149
member val GenerativeTypeProvidersInMemoryAnalysis = base.GetValueProperty<bool>("GenerativeTypeProvidersInMemoryAnalysis")
148150
member val TryRecoverFcsProjects = base.GetValueProperty<bool>("TryRecoverFcsProjects")
151+
member val GenerateSignatureFile = base.GetValueProperty<bool>("GenerateSignatureFile")
149152

150153

151154
[<SolutionInstanceComponent>]
@@ -220,6 +223,7 @@ type FSharpOptionsPage(lifetime: Lifetime, optionsPageContext, settings,
220223
this.AddBoolOption((fun key -> key.RedundantParensAnalysis), RichText(FSharpExperimentalFeatures.redundantParenAnalysis), null) |> ignore
221224
this.AddBoolOption((fun key -> key.Formatter), RichText(FSharpExperimentalFeatures.formatter), null) |> ignore
222225
this.AddBoolOptionWithComment((fun key -> key.NonFSharpProjectInMemoryAnalysis), nonFSharpProjectInMemoryAnalysis, "Requires restart") |> ignore
226+
this.AddBoolOption((fun key -> key.GenerateSignatureFile), RichText(FSharpExperimentalFeatures.generateSignatureFile)) |> ignore
223227

224228

225229
[<ShellComponent>]

ReSharper.FSharp/src/FSharp.Common/src/Util/FSharpExperimentalFeatures.fs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@ type ExperimentalFeature =
1313
| RedundantParenAnalysis = 3
1414
| AssemblyReaderShim = 4
1515
| TryRecoverFcsProjects = 5
16+
| GenerateSignatureFile = 6
1617

1718
type FSharpExperimentalFeatureCookie(feature: ExperimentalFeature) =
1819
static let cookies = OneToListMap<ExperimentalFeature, IDisposable>()
@@ -45,6 +46,7 @@ type FSharpExperimentalFeatures() =
4546
| ExperimentalFeature.PostfixTemplates -> experimentalFeatures.EnablePostfixTemplates.Value
4647
| ExperimentalFeature.RedundantParenAnalysis -> experimentalFeatures.RedundantParensAnalysis.Value
4748
| ExperimentalFeature.TryRecoverFcsProjects -> experimentalFeatures.TryRecoverFcsProjects.Value
49+
| ExperimentalFeature.GenerateSignatureFile -> experimentalFeatures.GenerateSignatureFile.Value
4850
| _ -> failwith $"Unexpected feature: {feature}"
4951

5052
[<Extension>]

ReSharper.FSharp/src/FSharp.Psi.Features/src/LanguageService/FSharpElementFactory.fs

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -395,3 +395,23 @@ type FSharpElementFactory(languageService: IFSharpLanguageService, sourceFile: I
395395
let source = $"member val P = 3 with {accessors}"
396396
let t = getTypeDecl source
397397
t.MemberDeclarations[0].As<IAutoPropertyDeclaration>().AccessorsClause
398+
399+
member this.CreateEmptyFile() = createFile ""
400+
401+
member this.CreateModule(name) =
402+
let file = createFile $"module {name}"
403+
match file.ModuleDeclarations.[0] with
404+
| :? INamedModuleDeclaration as nmd -> nmd
405+
| _ -> failwith "Could not get named module"
406+
407+
member this.CreateNamespace(name) =
408+
let file = createFile $"namespace {name}"
409+
match file.ModuleDeclarations.[0] with
410+
| :? INamedNamespaceDeclaration as nnd -> nnd
411+
| _ -> failwith "Could not get namespace"
412+
413+
member this.CreateNestedModule(name) =
414+
let file = createFile $"module {name} = begin end"
415+
match file.ModuleDeclarations.[0].Members.[0] with
416+
| :? INestedModuleDeclaration as nmd -> nmd
417+
| _ -> failwith "Could not get nested module"

ReSharper.FSharp/src/FSharp.Psi.Intentions/FSharp.Psi.Intentions.fsproj

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,7 @@
2525
<Compile Include="src\Intentions\SetNameAction.fs" />
2626
<Compile Include="src\Intentions\LetToUseAction.fs" />
2727
<Compile Include="src\Intentions\RenameFileToMatchTypeNameAction.fs" />
28+
<Compile Include="src\Intentions\GenerateSignatureFileAction.fs" />
2829
<Compile Include="src\QuickFixes\FSharpQuickFixBase.fs" />
2930
<Compile Include="src\QuickFixes\RemoveUnusedOpensFix.fs" />
3031
<Compile Include="src\QuickFixes\ReplaceUseWithLetFix.fs" />
Lines changed: 100 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,100 @@
1+
namespace JetBrains.ReSharper.Plugins.FSharp.Psi.Features.ContextActions
2+
3+
open System.IO
4+
open JetBrains.ReSharper.Feature.Services.ContextActions
5+
open JetBrains.ReSharper.Plugins.FSharp.Psi
6+
open JetBrains.ReSharper.Plugins.FSharp.Psi.Features.Intentions
7+
open type JetBrains.ReSharper.Psi.PsiSourceFileExtensions
8+
open JetBrains.ReSharper.Plugins.FSharp
9+
open JetBrains.ReSharper.Plugins.FSharp.Psi.Tree
10+
open JetBrains.ReSharper.Psi
11+
open JetBrains.ReSharper.Psi.ExtensionsAPI.Tree
12+
13+
type TopLevelModuleOrNamespace =
14+
{
15+
IsModule: bool
16+
Name: string
17+
NestedModules: NestedModule list
18+
}
19+
20+
and NestedModule =
21+
{
22+
Name: string
23+
NestedModules: NestedModule list
24+
}
25+
26+
[<ContextAction(Group = "F#", Name = "Generate signature file for current file", Priority = 1s,
27+
Description = "Generate signature file for current file.")>]
28+
type GenerateSignatureFileAction(dataProvider: FSharpContextActionDataProvider) =
29+
inherit FSharpContextActionBase(dataProvider)
30+
31+
let mkSignatureFile (fsharpFile: IFSharpFile) : IFSharpFile =
32+
let factory : IFSharpElementFactory = fsharpFile.CreateElementFactory()
33+
let signatureFile : IFSharpFile = factory.CreateEmptyFile()
34+
35+
let rec processModuleMembers (parent: IFSharpTreeNode) (members: IModuleMember seq) =
36+
for m in members do
37+
match m with
38+
| :? INestedModuleDeclaration as nmd ->
39+
let nestedModuleNode = factory.CreateNestedModule(nmd.NameIdentifier.Name)
40+
// TODO: if the nested module has nested modules, clear the content (`begin end`) and process them.
41+
ModificationUtil.AddChild(parent, nestedModuleNode) |> ignore
42+
| _ -> ()
43+
44+
for decl in fsharpFile.ModuleDeclarations do
45+
match decl with
46+
| :? INamedModuleDeclaration as nmd ->
47+
let moduleNode = factory.CreateModule(nmd.NameIdentifier.Name)
48+
processModuleMembers moduleNode nmd.Members
49+
ModificationUtil.AddChild(signatureFile, moduleNode) |> ignore
50+
| :? INamedNamespaceDeclaration as nnd ->
51+
let namespaceNode = factory.CreateModule(nnd.NameIdentifier.Name)
52+
processModuleMembers namespaceNode nnd.Members
53+
ModificationUtil.AddChild(signatureFile, namespaceNode) |> ignore
54+
| _ -> ()
55+
56+
signatureFile
57+
58+
override this.Text = "Generate signature file for current file"
59+
60+
override this.IsAvailable _ =
61+
let solution = dataProvider.Solution
62+
let isSettingEnabled = solution.IsFSharpExperimentalFeatureEnabled(ExperimentalFeature.GenerateSignatureFile)
63+
if not isSettingEnabled then false else
64+
let currentFSharpFile = dataProvider.PsiFile
65+
let fcsService = currentFSharpFile.FcsCheckerService
66+
let hasSignature = fcsService.FcsProjectProvider.HasPairFile dataProvider.SourceFile
67+
not hasSignature
68+
69+
override this.ExecutePsiTransaction(solution, _) =
70+
let projectFile = dataProvider.SourceFile.ToProjectFile()
71+
let fsharpFile = projectFile.GetPrimaryPsiFile().AsFSharpFile()
72+
let physicalPath = dataProvider.SourceFile.ToProjectFile().Location.FileAccessPath
73+
let fsiFile = Path.ChangeExtension(physicalPath, ".fsi")
74+
75+
let signatureFile = mkSignatureFile fsharpFile
76+
// try
77+
// let currentFSharpFile = dataProvider.PsiFile
78+
// let fcsService = currentFSharpFile.FcsCheckerService
79+
// let checkResult = fcsService.ParseAndCheckFile(currentFSharpFile.GetSourceFile(), "for signature file", true)
80+
// do
81+
// match checkResult with
82+
// | None -> ()
83+
// | Some { CheckResults = checkResult } ->
84+
//
85+
// match checkResult.GenerateSignature() with
86+
// | None -> ()
87+
// | Some signatureSourceText ->
88+
// let content = string signatureSourceText
89+
// File.WriteAllText(fsiFile, content)
90+
// with ex ->
91+
// // TODO: show some balloon thing?
92+
// ()
93+
94+
// solution.InvokeUnderTransaction(fun transactionCookie ->
95+
// let virtualPath = FileSystemPath.TryParse(fsiFile).ToVirtualFileSystemPath()
96+
// let relativeTo = RelativeTo(projectFile, RelativeToType.Before)
97+
// transactionCookie.AddFile(projectFile.ParentFolder, virtualPath, context = OrderingContext(relativeTo))
98+
// |> ignore)
99+
100+
null

ReSharper.FSharp/src/FSharp.Psi/src/IFSharpElementFactory.cs

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -64,5 +64,10 @@ public interface IFSharpElementFactory
6464
ITypeParameterDeclarationList CreateTypeParameterOfTypeList(FSharpList<string> names);
6565

6666
IAccessorsNamesClause CreateAccessorsNamesClause(bool withGetter, bool withSetter);
67+
68+
IFSharpFile CreateEmptyFile();
69+
INamedModuleDeclaration CreateModule(string name);
70+
INamedNamespaceDeclaration CreateNamespace(string name);
71+
INestedModuleDeclaration CreateNestedModule(string name);
6772
}
6873
}

0 commit comments

Comments
 (0)