Skip to content

Commit

Permalink
After pair programming session.
Browse files Browse the repository at this point in the history
  • Loading branch information
nojaf committed Feb 27, 2023
1 parent 6dd739e commit 132b25c
Show file tree
Hide file tree
Showing 8 changed files with 87 additions and 84 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -400,18 +400,12 @@ type FSharpElementFactory(languageService: IFSharpLanguageService, sourceFile: I

member this.CreateModule(name) =
let file = createFile $"module {name}"
match file.ModuleDeclarations.[0] with
| :? INamedModuleDeclaration as nmd -> nmd
| _ -> failwith "Could not get named module"
file.ModuleDeclarations.[0] :?> INamedModuleDeclaration

member this.CreateNamespace(name) =
let file = createFile $"namespace {name}"
match file.ModuleDeclarations.[0] with
| :? INamedNamespaceDeclaration as nnd -> nnd
| _ -> failwith "Could not get namespace"
file.ModuleDeclarations.[0] :?> INamespaceDeclaration

member this.CreateNestedModule(name) =
let file = createFile $"module {name} = begin end"
match file.ModuleDeclarations.[0].Members.[0] with
| :? INestedModuleDeclaration as nmd -> nmd
| _ -> failwith "Could not get nested module"
file.ModuleDeclarations.[0].Members.[0] :?> INestedModuleDeclaration
Original file line number Diff line number Diff line change
Expand Up @@ -34,11 +34,11 @@ type FSharpImplTreeBuilder(lexer, document, decls, lifetime, path, projectedOffs
let (SynModuleOrNamespace(lid, _, moduleKind, decls, XmlDoc xmlDoc, attrs, _, range, _)) = moduleOrNamespace
let mark, elementType = x.StartTopLevelDeclaration(lid, attrs, moduleKind, xmlDoc, range)
for decl in decls do
x.ProcessModuleMemberDeclaration(decl)
x.ProcessModuleMemberDeclaration(decl, moduleOrNamespace.Range)
x.EnsureMembersAreFinished()
x.FinishTopLevelDeclaration(mark, range, elementType)

member x.ProcessModuleMemberDeclaration(moduleMember) =
member x.ProcessModuleMemberDeclaration(moduleMember, parentRange) =
match unfinishedDeclaration with
| None -> ()
| Some(mark, range, elementType) ->
Expand All @@ -52,7 +52,12 @@ type FSharpImplTreeBuilder(lexer, document, decls, lifetime, path, projectedOffs
| SynModuleDecl.NestedModule(SynComponentInfo(attrs, _, _, _, XmlDoc xmlDoc, _, _, _), _, decls, _, range, _) ->
let mark = x.MarkAndProcessIntro(attrs, xmlDoc, null, range)
for decl in decls do
x.ProcessModuleMemberDeclaration(decl)
x.ProcessModuleMemberDeclaration(decl, parentRange)

if decls.IsEmpty then
x.AdvanceToTokenOrRangeEnd(FSharpTokenType.END, parentRange)
x.Advance()

x.Done(range, mark, ElementType.NESTED_MODULE_DECLARATION)

| SynModuleDecl.Types(typeDefns, range) ->
Expand Down
Original file line number Diff line number Diff line change
@@ -1,57 +1,72 @@
namespace JetBrains.ReSharper.Plugins.FSharp.Psi.Features.ContextActions

open System.IO
open JetBrains.ReSharper.Psi.Tree
open JetBrains.DocumentManagers.Transactions.ProjectHostActions.Ordering
open JetBrains.ProjectModel.ProjectsHost
open JetBrains.RdBackend.Common.Features.ProjectModel
open JetBrains.ReSharper.Feature.Services.ContextActions
open JetBrains.ReSharper.Plugins.FSharp.Psi
open JetBrains.ReSharper.Plugins.FSharp.Psi.Features.Intentions
open type JetBrains.ReSharper.Psi.PsiSourceFileExtensions
open JetBrains.ReSharper.Plugins.FSharp
open JetBrains.ReSharper.Plugins.FSharp.Psi.Impl.Tree
open JetBrains.ReSharper.Plugins.FSharp.Psi.Tree
open JetBrains.ReSharper.Psi
open JetBrains.ReSharper.Psi.ExtensionsAPI.Tree

type TopLevelModuleOrNamespace =
{
IsModule: bool
Name: string
NestedModules: NestedModule list
}

and NestedModule =
{
Name: string
NestedModules: NestedModule list
}
// extract value -> ctrl alt v
// undo that -> ctrl alt n

[<ContextAction(Group = "F#", Name = "Generate signature file for current file", Priority = 1s,
Description = "Generate signature file for current file.")>]
type GenerateSignatureFileAction(dataProvider: FSharpContextActionDataProvider) =
inherit FSharpContextActionBase(dataProvider)

let mkSignatureFile (fsharpFile: IFSharpFile) : IFSharpFile =
let factory : IFSharpElementFactory = fsharpFile.CreateElementFactory()
let signatureFile : IFSharpFile = factory.CreateEmptyFile()

let rec processModuleMembers (parent: IFSharpTreeNode) (members: IModuleMember seq) =
for m in members do
match m with
| :? INestedModuleDeclaration as nmd ->
let nestedModuleNode = factory.CreateNestedModule(nmd.NameIdentifier.Name)
// TODO: if the nested module has nested modules, clear the content (`begin end`) and process them.
ModificationUtil.AddChild(parent, nestedModuleNode) |> ignore
| _ -> ()
let lineEnding = fsharpFile.GetLineEnding()

let rec processModuleLikeDeclaration (indentation: int) (moduleDecl: IModuleLikeDeclaration) (moduleSig: IModuleLikeDeclaration) : IFSharpTreeNode =
for moduleMember in moduleDecl.Members do
// newline + indentation whitespace
addNodesAfter moduleSig.LastChild [
NewLine(lineEnding)
Whitespace(indentation)
match moduleMember with
| :? INestedModuleDeclaration as nestedNestedModule ->
let nestedSigModule = factory.CreateNestedModule(nestedNestedModule.NameIdentifier.Name)
let members = nestedNestedModule.Members
let shouldEmptyContent =
not members.IsEmpty
&& members |> Seq.forall (function | :? IExpressionStatement -> false | _ -> true)

if shouldEmptyContent then
ModificationUtil.DeleteChildRange (nestedSigModule.EqualsToken.NextSibling, nestedSigModule.LastChild)
processModuleLikeDeclaration (indentation + moduleDecl.GetIndentSize()) nestedNestedModule nestedSigModule
| :? IOpenStatement as openStatement ->
openStatement.Copy()
| _ -> ()
]
|> ignore

moduleSig

for decl in fsharpFile.ModuleDeclarations do
match decl with
| :? INamedModuleDeclaration as nmd ->
let moduleNode = factory.CreateModule(nmd.NameIdentifier.Name)
processModuleMembers moduleNode nmd.Members
ModificationUtil.AddChild(signatureFile, moduleNode) |> ignore
| :? INamedNamespaceDeclaration as nnd ->
let namespaceNode = factory.CreateModule(nnd.NameIdentifier.Name)
processModuleMembers namespaceNode nnd.Members
ModificationUtil.AddChild(signatureFile, namespaceNode) |> ignore
| _ -> ()
let signatureModule : IModuleLikeDeclaration =
match decl with
| :? INamedModuleDeclaration as nmd ->
factory.CreateModule(nmd.DeclaredElement.GetClrName().FullName)
| :? IGlobalNamespaceDeclaration ->
factory.CreateNamespace("global") :?> _
| :? INamedNamespaceDeclaration as nnd ->
// TODO: add an interface that could unify named and global namespace.
factory.CreateNamespace(nnd.QualifiedName) :?> _
| decl -> failwithf $"Unexpected declaration, got: %A{decl}"

ModificationUtil.AddChildAfter(signatureModule.LastChild, NewLine(lineEnding)) |> ignore
let signatureModule = processModuleLikeDeclaration 0 decl signatureModule
ModificationUtil.AddChild(signatureFile, signatureModule) |> ignore

signatureFile

Expand All @@ -71,30 +86,18 @@ type GenerateSignatureFileAction(dataProvider: FSharpContextActionDataProvider)
let fsharpFile = projectFile.GetPrimaryPsiFile().AsFSharpFile()
let physicalPath = dataProvider.SourceFile.ToProjectFile().Location.FileAccessPath
let fsiFile = Path.ChangeExtension(physicalPath, ".fsi")

let signatureFile = mkSignatureFile fsharpFile
// try
// let currentFSharpFile = dataProvider.PsiFile
// let fcsService = currentFSharpFile.FcsCheckerService
// let checkResult = fcsService.ParseAndCheckFile(currentFSharpFile.GetSourceFile(), "for signature file", true)
// do
// match checkResult with
// | None -> ()
// | Some { CheckResults = checkResult } ->
//
// match checkResult.GenerateSignature() with
// | None -> ()
// | Some signatureSourceText ->
// let content = string signatureSourceText
// File.WriteAllText(fsiFile, content)
// with ex ->
// // TODO: show some balloon thing?
// ()
File.WriteAllText(fsiFile, signatureFile.GetText())

// solution.InvokeUnderTransaction(fun transactionCookie ->
// let virtualPath = FileSystemPath.TryParse(fsiFile).ToVirtualFileSystemPath()
// let relativeTo = RelativeTo(projectFile, RelativeToType.Before)
// transactionCookie.AddFile(projectFile.ParentFolder, virtualPath, context = OrderingContext(relativeTo))
// |> ignore)
solution.InvokeUnderTransaction(fun transactionCookie ->
let virtualPath = FileSystemPath.TryParse(fsiFile).ToVirtualFileSystemPath()
let relativeTo = RelativeTo(projectFile, RelativeToType.Before)
transactionCookie.AddFile(projectFile.ParentFolder, virtualPath, context = OrderingContext(relativeTo))
|> ignore)

null
// TODO: it would be nice if we opened the signature file that was just created. Maybe split?
null

// First test name would be: ``ModuleStructure 01`` , ``NamespaceStructure 01``

// TODO: raise parser issue.
2 changes: 1 addition & 1 deletion ReSharper.FSharp/src/FSharp.Psi/src/FSharp.psi
Original file line number Diff line number Diff line change
Expand Up @@ -99,7 +99,7 @@ namedNamespaceDeclaration options { stubBase="FSharpDeclarationBase"; }:
topLevelNamedDeclarationName
moduleMember<MODULE_MEMBER, Members>*;

globalNamespaceDeclaration options { stubBase="FSharpDeclarationBase"; }:
globalNamespaceDeclaration options { stubBase="NamedNamespaceDeclaration"; }:
NAMESPACE<NAMESPACE, ModuleOrNamespaceKeyword>
REC<REC, RecKeyword>?
GLOBAL<GLOBAL, GlobalKeyword>
Expand Down
3 changes: 2 additions & 1 deletion ReSharper.FSharp/src/FSharp.Psi/src/IFSharpElementFactory.cs
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
using System;
using FSharp.Compiler.Symbols;
using JetBrains.ReSharper.Plugins.FSharp.Psi.Tree;
using JetBrains.ReSharper.Psi.Tree;
using Microsoft.FSharp.Collections;

namespace JetBrains.ReSharper.Plugins.FSharp.Psi
Expand Down Expand Up @@ -67,7 +68,7 @@ public interface IFSharpElementFactory

IFSharpFile CreateEmptyFile();
INamedModuleDeclaration CreateModule(string name);
INamedNamespaceDeclaration CreateNamespace(string name);
INamespaceDeclaration CreateNamespace(string name);
INestedModuleDeclaration CreateNestedModule(string name);
}
}
Original file line number Diff line number Diff line change
@@ -1,6 +1,4 @@
using JetBrains.Diagnostics;
using JetBrains.ReSharper.Plugins.FSharp.Psi.Parsing;
using JetBrains.ReSharper.Plugins.FSharp.Psi.Tree;
using JetBrains.ReSharper.Plugins.FSharp.Psi.Tree;
using JetBrains.ReSharper.Psi;
using JetBrains.ReSharper.Psi.ExtensionsAPI;

Expand All @@ -12,15 +10,6 @@ internal partial class GlobalNamespaceDeclaration
public override IDeclaredElement DeclaredElement => null;
public override string CompiledName => SharedImplUtil.MISSING_DECLARATION_NAME;
public override IFSharpIdentifierLikeNode NameIdentifier => null;

public bool IsRecursive => RecKeyword != null;

public void SetIsRecursive(bool value)
{
if (!value)
throw new System.NotImplementedException();

ModuleOrNamespaceKeyword.NotNull().AddTokenAfter(FSharpTokenType.REC);
}
public override string DeclaredName => "``global``";
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
using JetBrains.ReSharper.Psi.Tree;

namespace JetBrains.ReSharper.Plugins.FSharp.Psi.Tree
{
public partial interface IGlobalNamespaceDeclaration : INamespaceDeclaration
{
}
}
Original file line number Diff line number Diff line change
@@ -1,5 +1,8 @@
namespace JetBrains.ReSharper.Plugins.FSharp.Psi.Tree
{
// TODO: add triple slash comment.
// this is either a namespace,module or nested module, global namespace, anon module
// see inherits! (ctrl alt b)
public partial interface IModuleLikeDeclaration : IFSharpDeclaration
{
}
Expand Down

0 comments on commit 132b25c

Please sign in to comment.