Repository navigation
Place the added open from the syntax tree, not from a line of text #20500
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We鈥檒l occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: main
Are you sure you want to change the base?
Changes from all commits
2d32167
9d7c8dc
6182b92
9542a9b
bced991
1eff620
64ee01b
9e488ea
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -2461,9 +2461,9 @@ module ParsedInput = | |
| ) | ||
| | _ -> () | ||
|
|
||
| let getMinColumn decls = | ||
| let tryGetFirstDeclStart decls = | ||
| match decls with | ||
| | [] -> None | ||
| | [] -> ValueNone | ||
| | firstDecl :: _ -> | ||
| match firstDecl with | ||
| | SynModuleDecl.NestedModule(range = r) | ||
|
|
@@ -2472,9 +2472,16 @@ module ParsedInput = | |
| | SynModuleDecl.Types(range = r) | ||
| | SynModuleDecl.Exception(range = r) | ||
| | SynModuleDecl.Open(range = r) | ||
| | SynModuleDecl.HashDirective(range = r) -> Some r | ||
| | _ -> None | ||
| |> Option.map (fun r -> r.StartColumn) | ||
| | SynModuleDecl.HashDirective(range = r) -> ValueSome r.Start | ||
| | _ -> ValueNone | ||
|
|
||
| let headerEndLine (keyword: range) (ident: LongIdent) (equals: range option) = | ||
| let nameEndLine = | ||
| ident | ||
| |> List.fold (fun line part -> max line part.idRange.EndLine) keyword.EndLine | ||
|
|
||
| equals | ||
| |> Option.fold (fun line equalsSign -> max line equalsSign.EndLine) nameEndLine | ||
|
|
||
| let rec walkImplFileInput (file: ParsedImplFileInput) = | ||
| List.iter (walkSynModuleOrNamespace []) file.Contents | ||
|
|
@@ -2496,45 +2503,62 @@ module ParsedInput = | |
|
|
||
| let fullIdent = parent @ ident | ||
|
|
||
| // Use trivia to get the actual module/namespace keyword line, which excludes attributes | ||
| let startLine = | ||
| let lineAboveBody = | ||
| match trivia.LeadingKeyword with | ||
| | SynModuleOrNamespaceLeadingKeyword.Module moduleRange -> moduleRange.StartLine | ||
| | SynModuleOrNamespaceLeadingKeyword.Namespace namespaceRange -> namespaceRange.StartLine - 1 | ||
| | SynModuleOrNamespaceLeadingKeyword.None -> | ||
| // No keyword (implicit module), use range.StartLine | ||
| if isModule then range.StartLine else range.StartLine - 1 | ||
| | SynModuleOrNamespaceLeadingKeyword.Module keyword | ||
| | SynModuleOrNamespaceLeadingKeyword.Namespace keyword -> headerEndLine keyword ident None | ||
| | SynModuleOrNamespaceLeadingKeyword.None -> range.StartLine - 1 | ||
|
|
||
| let scopeKind = | ||
| match isModule, parent with | ||
| | true, [] -> TopModule | ||
| | true, _ -> NestedModule | ||
| | _ -> Namespace | ||
|
|
||
| doRange scopeKind fullIdent startLine range.StartColumn | ||
| doRange scopeKind fullIdent lineAboveBody range.StartColumn | ||
| addModule (fullIdent, range) | ||
| List.iter (walkSynModuleDecl fullIdent) decls | ||
|
|
||
| and walkSynModuleDecl (parent: LongIdent) (decl: SynModuleDecl) = | ||
| match decl with | ||
| | SynModuleDecl.NamespaceFragment fragment -> walkSynModuleOrNamespace parent fragment | ||
| | SynModuleDecl.NestedModule(moduleInfo = compInfo; decls = decls; range = range; trivia = trivia) -> | ||
| let (SynComponentInfo(attributes = attributes)) = compInfo | ||
| let ident = compInfo.LongIdent | ||
| let fullIdent = parent @ ident | ||
| addModule (fullIdent, range) | ||
|
|
||
| if range.EndLine >= currentLine then | ||
| // Use trivia to get the actual module keyword line, which excludes attributes | ||
| let moduleKeywordLine = | ||
| let headerLine = | ||
| match trivia.ModuleKeyword with | ||
| | Some moduleKeywordRange -> moduleKeywordRange.StartLine | ||
| | Some moduleKeyword -> headerEndLine moduleKeyword ident trivia.EqualsRange | ||
| | None -> range.StartLine // Fallback if trivia unavailable | ||
|
Comment on lines
+2532
to
2535
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Fixed in 777c4bd: a nested module whose first declaration shares the header's line is now entered at that declaration, so the fix breaks the line there and the |
||
|
|
||
| let firstDeclStart = tryGetFirstDeclStart decls | ||
|
|
||
| let moduleBodyIndentation = | ||
| getMinColumn decls |> Option.defaultValue (range.StartColumn + 4) | ||
| firstDeclStart | ||
| |> ValueOption.map _.Column | ||
| |> ValueOption.defaultValue (range.StartColumn + 4) | ||
|
|
||
| let bodyStartsOnHeaderLine = | ||
| firstDeclStart |> ValueOption.exists (fun start -> start.Line = headerLine) | ||
|
|
||
| doRange NestedModule fullIdent moduleKeywordLine moduleBodyIndentation | ||
| List.iter (walkSynModuleDecl fullIdent) decls | ||
| let lineAboveBody = | ||
| if bodyStartsOnHeaderLine then | ||
| headerLine - 1 | ||
| else | ||
| headerLine | ||
|
|
||
| let useMayBeInModuleAttribute = | ||
| bodyStartsOnHeaderLine | ||
| && currentLine = headerLine | ||
| && attributes | ||
| |> List.exists (fun attributeList -> attributeList.Range.EndLine = headerLine) | ||
|
|
||
| if headerLine <= currentLine && not useMayBeInModuleAttribute then | ||
| doRange NestedModule fullIdent lineAboveBody moduleBodyIndentation | ||
| List.iter (walkSynModuleDecl fullIdent) decls | ||
| | SynModuleDecl.Open(_, range) -> doRange OpenDeclaration [] range.EndLine (range.StartColumn - 5) | ||
| | SynModuleDecl.HashDirective(_, range) -> doRange HashDirective [] range.EndLine range.StartColumn | ||
| | _ -> () | ||
|
|
@@ -2609,50 +2633,12 @@ module ParsedInput = | |
| entities | ||
| |> Array.map (fun e -> e, findBestPositionToInsertOpenDeclaration modules scope pos entity) | ||
|
|
||
| /// Corrects insertion line number based on kind of scope and text surrounding the insertion point. | ||
| let AdjustInsertionPoint (getLineStr: int -> string) ctx = | ||
| let line = | ||
| match ctx.ScopeKind with | ||
| | ScopeKind.TopModule -> | ||
| if ctx.Pos.Line > 1 then | ||
| // it's an implicit module without any open declarations | ||
| let line = getLineStr (ctx.Pos.Line - 2) | ||
|
|
||
| let isImplicitTopLevelModule = | ||
| not (line.StartsWithOrdinal("module") && not (line.EndsWithOrdinal("="))) | ||
|
|
||
| if isImplicitTopLevelModule then 1 else ctx.Pos.Line | ||
| else | ||
| 1 | ||
|
|
||
| | ScopeKind.Namespace -> | ||
| // For namespaces the start line is start line of the first nested entity | ||
| // If we are not on the first line, try to find opening namespace, and return line after it (in F# format) | ||
| if ctx.Pos.Line > 1 then | ||
| [ 0 .. ctx.Pos.Line - 1 ] | ||
| |> List.mapi (fun i line -> i, getLineStr line) | ||
| |> List.tryPick (fun (i, lineStr) -> | ||
| if lineStr.StartsWithOrdinal("namespace") then | ||
| Some i | ||
| else | ||
| None) | ||
| |> function | ||
| // move to the next line below "namespace" and convert it to F# 1-based line number | ||
| | Some line -> line + 2 | ||
| | None -> ctx.Pos.Line | ||
| // If we are on 1st line in the namespace ctx, this line _should_ be the namespace declaration, check it and return next line. | ||
| // Otherwise, return first line (which theoretically should not happen). | ||
| else | ||
| let lineStr = getLineStr (ctx.Pos.Line - 1) | ||
|
|
||
| if lineStr.StartsWithOrdinal("namespace") then | ||
| ctx.Pos.Line + 1 | ||
| else | ||
| ctx.Pos.Line | ||
|
|
||
| | _ -> ctx.Pos.Line | ||
|
|
||
| mkPos line ctx.Pos.Column | ||
| match ctx.ScopeKind with | ||
| | ScopeKind.TopModule | ||
| | ScopeKind.Namespace | ||
| | ScopeKind.NestedModule when getLineStr (Line.toZ ctx.Pos.Line) = "" -> mkPos (ctx.Pos.Line + 1) ctx.Pos.Column | ||
| | _ -> ctx.Pos | ||
|
|
||
| let FindNearestPointToInsertOpenDeclaration | ||
| (currentLine: int) | ||
|
|
@@ -2695,8 +2681,7 @@ module ParsedInput = | |
| | _ -> 0 | ||
|
|
||
| if lastReferenceLine > 0 then | ||
| // `AdjustInsertionPoint` snaps a `TopModule` position up to line 1, above the directives; | ||
| // remap it to `HashDirective`, which (like the other scopes) it passes through unchanged. | ||
| // AdjustInsertionPoint would move a TopModule position below the blank line under the directives. | ||
| let scopeKind = | ||
| if ctx.ScopeKind = ScopeKind.TopModule then | ||
| ScopeKind.HashDirective | ||
|
|
||
Uh oh!
There was an error while loading. Please reload this page.