Skip to content

Commit 2aa4213

Browse files
committed
Right after pair programming session.
1 parent e0fbb4b commit 2aa4213

File tree

13 files changed

+209
-41
lines changed

13 files changed

+209
-41
lines changed

ReSharper.FSharp/src/FSharp.Psi.Features/src/CodeCompletion/FSharpCodeCompletionContext.fs

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@ open JetBrains.DocumentModel
88
open JetBrains.ReSharper.Feature.Services.CodeCompletion
99
open JetBrains.ReSharper.Feature.Services.CodeCompletion.Impl
1010
open JetBrains.ReSharper.Feature.Services.CodeCompletion.Infrastructure
11+
open JetBrains.ReSharper.Plugins.FSharp
1112
open JetBrains.ReSharper.Plugins.FSharp.Psi
1213
open JetBrains.ReSharper.Plugins.FSharp.Psi.Features
1314
open JetBrains.ReSharper.Plugins.FSharp.Psi.Features.CodeCompletion
@@ -53,7 +54,7 @@ type FSharpReparseContext(fsFile: IFSharpFile, treeTextRange: TreeTextRange) =
5354
let document = documentFactory.CreateSimpleDocumentFromText(source, moniker)
5455

5556
// todo: reparse as sig?
56-
let parser = fsFile.GetFSharpLanguageService().CreateParser(document, fsFile.GetSourceFile(), true)
57+
let parser = fsFile.GetFSharpLanguageService().CreateParser(document, fsFile.GetSourceFile(), FSharpProjectFileType.FsExtension)
5758
let newFile =
5859
try
5960
let newFile =

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

Lines changed: 20 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
namespace JetBrains.ReSharper.Plugins.FSharp.Psi.Features.LanguageService
22

3+
open System.Runtime.InteropServices
34
open FSharp.Compiler.Symbols
45
open FSharp.Compiler.Syntax
56
open JetBrains.Diagnostics
@@ -19,7 +20,8 @@ open JetBrains.ReSharper.Psi.Naming
1920
open JetBrains.ReSharper.Psi.Tree
2021
open JetBrains.ReSharper.Resources.Shell
2122

22-
type FSharpElementFactory(languageService: IFSharpLanguageService, sourceFile: IPsiSourceFile, psiModule: IPsiModule) =
23+
type FSharpElementFactory(languageService: IFSharpLanguageService, sourceFile: IPsiSourceFile, psiModule: IPsiModule,
24+
[<Optional; DefaultParameterValue(null)>] extension: string) =
2325
let [<Literal>] moniker = "F# element factory"
2426

2527
let getNamingService () =
@@ -31,14 +33,19 @@ type FSharpElementFactory(languageService: IFSharpLanguageService, sourceFile: I
3133

3234
let createFile source =
3335
let document = createDocument source
34-
let parser = languageService.CreateParser(document, sourceFile)
36+
let parser = languageService.CreateParser(document, sourceFile, extension)
3537

3638
let fsFile = parser.ParseFSharpFile(noCache = true, StandaloneDocument = document)
3739
SandBox.CreateSandBoxFor(fsFile, psiModule)
3840
fsFile
41+
42+
let createFileWithModule source =
43+
createFile $"module X
44+
{source}
45+
"
3946

4047
let getModuleDeclaration source =
41-
let fsFile = createFile source
48+
let fsFile = createFileWithModule source
4249
fsFile.ModuleDeclarations.First()
4350

4451
let getModuleMember source =
@@ -407,5 +414,13 @@ type FSharpElementFactory(languageService: IFSharpLanguageService, sourceFile: I
407414
file.ModuleDeclarations.[0] :?> INamespaceDeclaration
408415

409416
member this.CreateNestedModule(name) =
410-
let file = createFile $"module {name} = begin end"
411-
file.ModuleDeclarations.[0].Members.[0] :?> INestedModuleDeclaration
417+
let file = createFileWithModule $"module {name} = begin end"
418+
file.ModuleDeclarations.[0].Members.[0] :?> INestedModuleDeclaration
419+
420+
member this.CreateModuleMember(source) =
421+
let file = createFileWithModule source
422+
file.ModuleDeclarations.[0].Members.[0]
423+
424+
member this.CreateTypeMemberSignature(source) =
425+
(getTypeDecl source).TypeMembers.[0]
426+
:> IFSharpTypeMemberDeclaration

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

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -143,8 +143,9 @@ type FSharpLanguageService(languageType, constantValueService, cacheProvider: FS
143143
override x.GetTypeConversionRule(_, _) = ClrPredefinedTypeConversionRule.INSTANCE
144144

145145
interface IFSharpLanguageService with
146-
member x.CreateParser(document: IDocument, sourceFile, [<Optional; DefaultParameterValue(false)>] useFsExtension) =
146+
member x.CreateParser(document: IDocument, sourceFile, [<Optional; DefaultParameterValue(null)>] overrideExtension) =
147147
let lexer = TokenBuffer(lexerFactory.CreateLexer(document.Buffer)).CreateLexer()
148-
FSharpParser(lexer, document, sourceFile, checkerService, null, useFsExtension) :> _
148+
FSharpParser(lexer, document, sourceFile, checkerService, null, overrideExtension) :> _
149149

150-
member x.CreateElementFactory(sourceFile, psiModule) = FSharpElementFactory(x, sourceFile, psiModule) :> _
150+
member x.CreateElementFactory(sourceFile, psiModule, [<Optional; DefaultParameterValue(null)>] extension) =
151+
FSharpElementFactory(x, sourceFile, psiModule, extension) :> _

ReSharper.FSharp/src/FSharp.Psi.Features/src/Parsing/FSharpParser.fs

Lines changed: 7 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -56,6 +56,9 @@ type FSharpParser(lexer: ILexer, document: IDocument, path: VirtualFileSystemPat
5656
ResolvedSymbolsCache = symbolsCache,
5757
LanguageType = language)
5858

59+
static member val SandBoxPath = VirtualFileSystemPath.Parse("Sandbox.fs", InteractionContext.SolutionContext)
60+
static member val SandBoxSignaturePath = VirtualFileSystemPath.Parse("Sandbox.fsi", InteractionContext.SolutionContext)
61+
5962
new (lexer, [<NotNull>] sourceFile: IPsiSourceFile, checkerService, symbolsCache) =
6063
// During rename of type + file the source file returns the new path,
6164
// but the parsing/project options still have the old one. It doesn't seem to affect anything.
@@ -64,18 +67,17 @@ type FSharpParser(lexer: ILexer, document: IDocument, path: VirtualFileSystemPat
6467
let path = if isNotNull sourceFile then sourceFile.GetLocation() else null
6568
FSharpParser(lexer, document, path, sourceFile, checkerService, symbolsCache)
6669

67-
new (lexer, document, sourceFile: IPsiSourceFile, checkerService, symbolsCache, useFsExtension) =
70+
new (lexer, document, sourceFile: IPsiSourceFile, checkerService, symbolsCache, overrideExtension: string) =
6871
let path =
69-
if not useFsExtension && isNotNull sourceFile && sourceFile.LanguageType.Is<FSharpSignatureProjectFileType>() then
72+
if isNotNull overrideExtension && overrideExtension = ".fsi" then
73+
FSharpParser.SandBoxSignaturePath
74+
elif isNull overrideExtension && isNotNull sourceFile && sourceFile.LanguageType.Is<FSharpSignatureProjectFileType>() then
7075
FSharpParser.SandBoxSignaturePath
7176
else
7277
FSharpParser.SandBoxPath
7378

7479
FSharpParser(lexer, document, path, sourceFile, checkerService, symbolsCache)
7580

76-
static member val SandBoxPath = VirtualFileSystemPath.Parse("Sandbox.fs", InteractionContext.SolutionContext)
77-
static member val SandBoxSignaturePath = VirtualFileSystemPath.Parse("Sandbox.fsi", InteractionContext.SolutionContext)
78-
7981
interface IFSharpParser with
8082
member this.ParseFSharpFile(noCache) = parseFile noCache
8183
member this.ParseFile() = parseFile false :> _

ReSharper.FSharp/src/FSharp.Psi.Intentions/src/Intentions/GenerateSignatureFileAction.fs

Lines changed: 138 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,13 @@
11
namespace JetBrains.ReSharper.Plugins.FSharp.Psi.Features.ContextActions
22

33
open System.IO
4+
open System.Linq
5+
open System.Text
6+
open FSharp.Compiler.Symbols
47
open JetBrains.Application.UI.PopupLayout
58
open JetBrains.ReSharper.Feature.Services.Navigation
9+
open JetBrains.ReSharper.Plugins.FSharp.Psi.Parsing
10+
open JetBrains.ReSharper.Psi.Naming
611
open JetBrains.ReSharper.Psi.Tree
712
open JetBrains.DocumentManagers.Transactions.ProjectHostActions.Ordering
813
open JetBrains.ProjectModel.ProjectsHost
@@ -20,41 +25,150 @@ open JetBrains.ReSharper.Resources.Shell
2025
// extract value -> ctrl alt v
2126
// undo that -> ctrl alt n
2227

28+
// TODO: what about attributes, type parameters, delegates, exceptions
29+
30+
// FSharpTokenType.AND.CreateLeafElement()
31+
2332
[<ContextAction(Group = "F#", Name = "Generate signature file for current file", Priority = 1s,
2433
Description = "Generate signature file for current file.")>]
2534
type GenerateSignatureFileAction(dataProvider: FSharpContextActionDataProvider) =
2635
inherit FSharpContextActionBase(dataProvider)
2736

2837
let mkSignatureFile (fsharpFile: IFSharpFile) : IFSharpFile =
29-
let factory : IFSharpElementFactory = fsharpFile.CreateElementFactory()
38+
let factory : IFSharpElementFactory = fsharpFile.CreateElementFactory(extension = FSharpSignatureProjectFileType.FsiExtension)
3039
let signatureFile : IFSharpFile = factory.CreateEmptyFile()
3140
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
32126

33-
let rec processModuleLikeDeclaration (indentation: int) (moduleDecl: IModuleLikeDeclaration) (moduleSig: IModuleLikeDeclaration) : IFSharpTreeNode =
127+
and processModuleLikeDeclaration (indentation: int) (moduleDecl: IModuleLikeDeclaration) (moduleSig: IModuleLikeDeclaration) : IFSharpTreeNode =
34128
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
55139

56140
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+
58172
for decl in fsharpFile.ModuleDeclarations do
59173
let signatureModule : IModuleLikeDeclaration =
60174
match decl with
@@ -81,6 +195,7 @@ type GenerateSignatureFileAction(dataProvider: FSharpContextActionDataProvider)
81195
if not isSettingEnabled then false else
82196
let currentFSharpFile = dataProvider.PsiFile
83197
let fcsService = currentFSharpFile.FcsCheckerService
198+
// TODO: don't check has pair in unit test
84199
let hasSignature = fcsService.FcsProjectProvider.HasPairFile dataProvider.SourceFile
85200
not hasSignature
86201

ReSharper.FSharp/src/FSharp.Psi/src/FSharpTreeNodeExtensions.cs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -12,8 +12,8 @@ public static IFSharpLanguageService GetFSharpLanguageService([NotNull] this ITr
1212
treeNode.GetPsiServices().GetComponent<LanguageManager>().GetService<IFSharpLanguageService>(treeNode.Language);
1313

1414
[NotNull]
15-
public static IFSharpElementFactory CreateElementFactory([NotNull] this ITreeNode treeNode) =>
16-
treeNode.GetFSharpLanguageService().CreateElementFactory(treeNode.GetSourceFile(), treeNode.GetPsiModule());
15+
public static IFSharpElementFactory CreateElementFactory([NotNull] this ITreeNode treeNode, string extension = null) =>
16+
treeNode.GetFSharpLanguageService().CreateElementFactory(treeNode.GetSourceFile(), treeNode.GetPsiModule(), extension);
1717

1818
public static bool IsFSharpSigFile([NotNull] this ITreeNode treeNode) =>
1919
treeNode.GetContainingFile() is IFSharpSigFile;

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

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -70,5 +70,8 @@ public interface IFSharpElementFactory
7070
INamedModuleDeclaration CreateModule(string name);
7171
INamespaceDeclaration CreateNamespace(string name);
7272
INestedModuleDeclaration CreateNestedModule(string name);
73+
74+
IModuleMember CreateModuleMember(string source);
75+
IFSharpTypeMemberDeclaration CreateTypeMemberSignature(string source);
7376
}
7477
}

ReSharper.FSharp/src/FSharp.Psi/src/IFSharpLanguageService.cs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@ namespace JetBrains.ReSharper.Plugins.FSharp.Psi
77
{
88
public interface IFSharpLanguageService
99
{
10-
IFSharpParser CreateParser(IDocument document, IPsiSourceFile sourceFile, bool useFsExtension = false);
11-
IFSharpElementFactory CreateElementFactory(IPsiSourceFile sourceFile, IPsiModule psiModule);
10+
IFSharpParser CreateParser(IDocument document, IPsiSourceFile sourceFile, string overrideExtension = null);
11+
IFSharpElementFactory CreateElementFactory(IPsiSourceFile sourceFile, IPsiModule psiModule, string overrideExtension = null);
1212
}
1313
}
Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
module Foo
2+
3+
open System
4+
{caret}
5+
let a = 0
Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
module Foo
2+
3+
open System
4+
{caret}
5+
let a = 0

0 commit comments

Comments
 (0)