1
1
namespace JetBrains.ReSharper.Plugins.FSharp.Psi.Features.ContextActions
2
2
3
3
open System.IO
4
+ open System.Linq
5
+ open System.Text
6
+ open FSharp.Compiler .Symbols
4
7
open JetBrains.Application .UI .PopupLayout
5
8
open JetBrains.ReSharper .Feature .Services .Navigation
9
+ open JetBrains.ReSharper .Plugins .FSharp .Psi .Parsing
10
+ open JetBrains.ReSharper .Psi .Naming
6
11
open JetBrains.ReSharper .Psi .Tree
7
12
open JetBrains.DocumentManagers .Transactions .ProjectHostActions .Ordering
8
13
open JetBrains.ProjectModel .ProjectsHost
@@ -20,41 +25,150 @@ open JetBrains.ReSharper.Resources.Shell
20
25
// extract value -> ctrl alt v
21
26
// undo that -> ctrl alt n
22
27
28
+ // TODO: what about attributes, type parameters, delegates, exceptions
29
+
30
+ // FSharpTokenType.AND.CreateLeafElement()
31
+
23
32
[<ContextAction( Group = " F#" , Name = " Generate signature file for current file" , Priority = 1 s,
24
33
Description = " Generate signature file for current file." ) >]
25
34
type GenerateSignatureFileAction ( dataProvider : FSharpContextActionDataProvider ) =
26
35
inherit FSharpContextActionBase( dataProvider)
27
36
28
37
let mkSignatureFile ( fsharpFile : IFSharpFile ) : IFSharpFile =
29
- let factory : IFSharpElementFactory = fsharpFile.CreateElementFactory()
38
+ let factory : IFSharpElementFactory = fsharpFile.CreateElementFactory( extension = FSharpSignatureProjectFileType.FsiExtension )
30
39
let signatureFile : IFSharpFile = factory.CreateEmptyFile()
31
40
let lineEnding = fsharpFile.GetLineEnding()
41
+ let getName ( decl : IFSharpDeclaration ) = NamingManager.GetNamingLanguageService( fsharpFile.Language) .MangleNameIfNecessary( decl.SourceName)
42
+
43
+ let rec createModuleMemberSig ( indentation : int ) ( moduleDecl : IModuleLikeDeclaration ) ( moduleMember : IModuleMember ) : IFSharpTreeNode =
44
+ match moduleMember with
45
+ | :? ITypeDeclarationGroup as typeGroup ->
46
+ let sigTypeDeclGroups =
47
+ // TODO: Update type into and keyword for second IFSharpTypeDeclaration in a group.
48
+
49
+ typeGroup.TypeDeclarations
50
+ |> Seq.choose ( function
51
+ | :? IFSharpTypeDeclaration as typeDecl ->
52
+ // TODO: update and keyword
53
+ (*
54
+ ModificationUtil.ReplaceChild(
55
+ sigTypeGroup.TypeDeclarations.[1].TypeKeyword,
56
+ (FSharpTokenType.AND.CreateLeafElement()))
57
+ *)
58
+
59
+ // Resharper implementation
60
+ //typeDecl.MemberDeclarations
61
+
62
+ // Untyped tree like
63
+ // typeDecl.TypeMembers
64
+ let sigMembers =
65
+ typeDecl.TypeMembers
66
+ |> Seq.choose ( createMemberDeclaration >> Option.ofObj)
67
+
68
+ match typeDecl.TypeRepresentation with
69
+ // TODO: checkout delegates
70
+ | :? ITypeAbbreviationRepresentation as abbr ->
71
+ let sigTypeGroup = factory.CreateModuleMember( $" type {getName typeDecl} = int" )
72
+ match sigTypeGroup with
73
+ | :? ITypeDeclarationGroup as sigTypeGroup ->
74
+ let sigDecl = sigTypeGroup.TypeDeclarations.[ 0 ] :?> IFSharpTypeDeclaration
75
+
76
+
77
+
78
+ ModificationUtil.DeleteChildRange( sigDecl.EqualsToken.NextSibling, sigDecl.LastChild)
79
+ addNodesAfter sigDecl.EqualsToken [
80
+ Whitespace()
81
+ abbr.Copy()
82
+ ]
83
+ |> ignore
84
+ Some sigTypeGroup
85
+ | _ -> None
86
+ | :? ISimpleTypeRepresentation as repr ->
87
+ let sigTypeGroup = factory.CreateModuleMember( $" type {getName typeDecl} = int" )
88
+ match sigTypeGroup with
89
+ | :? ITypeDeclarationGroup as sigTypeGroup ->
90
+ let sigDecl = sigTypeGroup.TypeDeclarations.[ 0 ] :?> IFSharpTypeDeclaration
91
+ ModificationUtil.DeleteChildRange( sigDecl.EqualsToken.NextSibling, sigDecl.LastChild)
92
+ addNodesAfter sigDecl.EqualsToken [
93
+ NewLine( lineEnding)
94
+ Whitespace( indentation + moduleDecl.GetIndentSize())
95
+ repr.Copy()
96
+ for sigMember in sigMembers do
97
+ NewLine( lineEnding)
98
+ Whitespace( indentation + moduleDecl.GetIndentSize())
99
+ sigMember
100
+ ]
101
+ |> ignore
102
+ Some sigTypeGroup
103
+ | _ -> None
104
+ | _ -> None
105
+
106
+
107
+
108
+ | _ -> None) // TODO: address this
109
+
110
+ sigTypeDeclGroups.FirstOrDefault()
111
+ // TODO : ITypeExtensionDeclaration
112
+
113
+ | :? INestedModuleDeclaration as nestedNestedModule ->
114
+ let nestedSigModule = factory.CreateNestedModule( nestedNestedModule.NameIdentifier.Name)
115
+ let members = nestedNestedModule.Members
116
+ let shouldEmptyContent =
117
+ not members.IsEmpty
118
+ && members |> Seq.forall ( function | :? IExpressionStatement -> false | _ -> true )
119
+
120
+ if shouldEmptyContent then
121
+ ModificationUtil.DeleteChildRange ( nestedSigModule.EqualsToken.NextSibling, nestedSigModule.LastChild)
122
+ processModuleLikeDeclaration ( indentation + moduleDecl.GetIndentSize()) nestedNestedModule nestedSigModule
123
+ | :? IOpenStatement as openStatement ->
124
+ openStatement.Copy()
125
+ | _ -> null
32
126
33
- let rec processModuleLikeDeclaration ( indentation : int ) ( moduleDecl : IModuleLikeDeclaration ) ( moduleSig : IModuleLikeDeclaration ) : IFSharpTreeNode =
127
+ and processModuleLikeDeclaration ( indentation : int ) ( moduleDecl : IModuleLikeDeclaration ) ( moduleSig : IModuleLikeDeclaration ) : IFSharpTreeNode =
34
128
for moduleMember in moduleDecl.Members do
35
- // newline + indentation whitespace
36
- addNodesAfter moduleSig.LastChild [
37
- NewLine( lineEnding)
38
- Whitespace( indentation)
39
- match moduleMember with
40
- | :? INestedModuleDeclaration as nestedNestedModule ->
41
- let nestedSigModule = factory.CreateNestedModule( nestedNestedModule.NameIdentifier.Name)
42
- let members = nestedNestedModule.Members
43
- let shouldEmptyContent =
44
- not members.IsEmpty
45
- && members |> Seq.forall ( function | :? IExpressionStatement -> false | _ -> true )
46
-
47
- if shouldEmptyContent then
48
- ModificationUtil.DeleteChildRange ( nestedSigModule.EqualsToken.NextSibling, nestedSigModule.LastChild)
49
- processModuleLikeDeclaration ( indentation + moduleDecl.GetIndentSize()) nestedNestedModule nestedSigModule
50
- | :? IOpenStatement as openStatement ->
51
- openStatement.Copy()
52
- | _ -> ()
53
- ]
54
- |> ignore
129
+ let signatureMember = createModuleMemberSig indentation moduleDecl moduleMember
130
+
131
+ if isNotNull signatureMember then
132
+ // newline + indentation whitespace
133
+ addNodesAfter moduleSig.LastChild [
134
+ NewLine( lineEnding)
135
+ Whitespace( indentation)
136
+ signatureMember
137
+ ]
138
+ |> ignore
55
139
56
140
moduleSig
57
-
141
+
142
+ and createMemberDeclaration ( memberDecl : IFSharpTypeMemberDeclaration ) : IFSharpTypeMemberDeclaration =
143
+ match memberDecl with
144
+ | :? IMemberDeclaration as memberDecl ->
145
+ let sourceString =
146
+ let sb = StringBuilder()
147
+
148
+ if memberDecl.IsStatic then
149
+ sb.Append( " static " ) |> ignore
150
+
151
+ sb.Append( memberDecl.MemberKeyword.GetText()) |> ignore
152
+ sb.Append( " " ) |> ignore
153
+
154
+ if isNotNull memberDecl.AccessModifier then
155
+ sb.Append( memberDecl.AccessModifier.GetText()) |> ignore
156
+
157
+ sb.Append( getName memberDecl) |> ignore
158
+ sb.Append( " : " ) |> ignore
159
+
160
+ let symbolUse = memberDecl.GetFcsSymbolUse()
161
+ if isNotNull symbolUse then
162
+ let mfv = symbolUse.Symbol.As< FSharpMemberOrFunctionOrValue>()
163
+ if isNotNull mfv then
164
+ sb.Append( mfv.FullType.Format( symbolUse.DisplayContext)) |> ignore
165
+
166
+ sb.ToString()
167
+
168
+ factory.CreateTypeMemberSignature( sourceString)
169
+ | _ -> null
170
+
171
+
58
172
for decl in fsharpFile.ModuleDeclarations do
59
173
let signatureModule : IModuleLikeDeclaration =
60
174
match decl with
@@ -81,6 +195,7 @@ type GenerateSignatureFileAction(dataProvider: FSharpContextActionDataProvider)
81
195
if not isSettingEnabled then false else
82
196
let currentFSharpFile = dataProvider.PsiFile
83
197
let fcsService = currentFSharpFile.FcsCheckerService
198
+ // TODO: don't check has pair in unit test
84
199
let hasSignature = fcsService.FcsProjectProvider.HasPairFile dataProvider.SourceFile
85
200
not hasSignature
86
201
0 commit comments