diff options
author | Sam Williams <sam.williams@Ashleys-MacBook-Pro.local> | 2019-01-06 07:16:19 +0300 |
---|---|---|
committer | monojenkins <jo.shields+jenkins@xamarin.com> | 2019-01-08 13:53:16 +0300 |
commit | 77c06005b2e255910687bdad0cdbd5479b74304a (patch) | |
tree | 258cc1fc4247c7e65c69718c8fe2e7ba9aee5e2e /main/external/fsharpbinding | |
parent | 47f4888946626df13ab08514f869c97a7e072d49 (diff) |
F# open statements code clean up. Added async support. Fixed up adjustments for where the open declaration is made
Diffstat (limited to 'main/external/fsharpbinding')
4 files changed, 169 insertions, 764 deletions
diff --git a/main/external/fsharpbinding/MonoDevelop.FSharpBinding/CodeFix/AddOpenCodeFixProvider.fs b/main/external/fsharpbinding/MonoDevelop.FSharpBinding/CodeFix/AddOpenCodeFixProvider.fs index 5d714cd31f..7ac727a118 100644 --- a/main/external/fsharpbinding/MonoDevelop.FSharpBinding/CodeFix/AddOpenCodeFixProvider.fs +++ b/main/external/fsharpbinding/MonoDevelop.FSharpBinding/CodeFix/AddOpenCodeFixProvider.fs @@ -38,8 +38,16 @@ module internal InsertContext = // it's an implicit module without any open declarations let line = getLineStr (ctx.Pos.Line - 2) let isImpliciteTopLevelModule = not (line.StartsWith "module" && not (line.EndsWith "=")) - if isImpliciteTopLevelModule then 1 else ctx.Pos.Line - else 1 + + if isImpliciteTopLevelModule then + let rec skipComments (line:string) count = + if line.StartsWith ("//") then + let count = count + 1 + skipComments (getLineStr count) count + else count + skipComments (getLineStr 0) 0 + else ctx.Pos.Line - 1 + else 0 | ScopeKind.Namespace -> // for namespaces the start line is start line of the first nested entity if ctx.Pos.Line > 1 then @@ -49,204 +57,68 @@ module internal InsertContext = if lineStr.StartsWith "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 + | Some line -> line + 1 + | None -> ctx.Pos.Line - 1 else 1 - | _ -> ctx.Pos.Line - - let toOffSet line column = - let lengthOfLines = - sourceText - |> Array.take (line - 1) // Don't count the current line - |> Array.map (fun x -> x.Length) - |> Array.sum - - let numOfNewLines = (line - 1) // Again, don't count the current line - lengthOfLines + column + numOfNewLines + | _ -> ctx.Pos.Line - 1 + if line = 0 then 0 + else + let lengthOfLines = sourceText |> Array.take line |> String.concat "\n" |> sprintf "%s\n" + lengthOfLines.Length - toOffSet ctx.Pos.Line ctx.Pos.Column - //{ ctx.Pos with Line = line } /// <summary> - /// Inserts open declaration into `SourceText`. + /// Creates a TextReplaceChange with open declaration at an appropriate offset /// </summary> /// <param name="sourceText">SourceText.</param> /// <param name="ctx">Insertion context. Typically returned from tryGetInsertionContext</param> /// <param name="ns">Namespace to open.</param> - //let insertOpenDeclaration (sourceText: SourceText) (ctx: InsertContext) (ns: string) : SourceText = - //let insert line lineStr (sourceText: SourceText) : SourceText = - // let pos = sourceText.Lines.[line].Start - // sourceText.WithChanges(TextChange(TextSpan(pos, 0), lineStr + Environment.NewLine)) - - //let pos = adjustInsertionPoint sourceText ctx - //let docLine = pos.Line - 1 - //let lineStr = (String.replicate pos.Column " ") + "open " + ns - //let sourceText = sourceText |> insert docLine lineStr - //// if there's no a blank line between open declaration block and the rest of the code, we add one - //let sourceText = - // if sourceText.Lines.[docLine + 1].ToString().Trim() <> "" then - // sourceText |> insert (docLine + 1) "" - // else sourceText - //// for top level module we add a blank line between the module declaration and first open statement - //if (pos.Column = 0 || ctx.ScopeKind = ScopeKind.Namespace) && docLine > 0 - // && not (sourceText.Lines.[docLine - 1].ToString().Trim().StartsWith "open") then - // sourceText |> insert docLine "" - //else sourceText - - - let insertOpenDeclartionWithEditor lastIdent findIdent (editor:TextEditor) (ctx) (ns:string, multipleNames, name) = + let insertOpenDeclartionWithEditor (editor:TextEditor) (ctx) displayText = + let activeDocFileName = editor.FileName.ToString () + let offset = adjustInsertionPoint editor.Text ctx + TextReplaceChange (FileName = activeDocFileName, + Offset = offset, + RemovedChars = 0, + InsertedText = displayText, + Description = String.Format ("Insert open declartion '{0}''", displayText)) :> Change + |> Array.singleton - //match findIdent () with - //| Some (symbolUse: FSharpSymbolUse) -> - let activeDocFileName = editor.FileName.ToString () - let offset = adjustInsertionPoint editor.Text ctx - //let symbols = - // languageService.GetUsesOfSymbolInProject (ctx.Project.FileName.ToString(), activeDocFileName, editor.Text, symbolUse.Symbol) - // |> (fun p -> Async.RunSynchronously(p, timeout=ServiceSettings.maximumTimeout)) - - //let locations = - // symbols |> Array.map (Symbols.getTextSpanTrimmed lastIdent) - - //let file = - //locations - //|> Array.map fst - //|> Array.head - - //if fileLocations.Count = 1 then +[<CompilationRepresentation (CompilationRepresentationFlags.ModuleSuffix)>] +module internal FSharpAddOpenCodeFixProvider = + + let getSuggestions editor monitor (candidates: (Entity * InsertContext) list) = + candidates + |> Seq.choose (fun (entity, ctx) -> entity.Namespace |> Option.map (fun ns -> ns, entity.Name, ctx)) + |> Seq.groupBy (fun (ns, _, _) -> ns) + |> Seq.map (fun (ns, xs) -> + ns, + xs + |> Seq.map (fun (_, name, ctx) -> name, ctx) + |> Seq.distinctBy (fun (name, _) -> name) + |> Seq.sort + |> Seq.toArray) + |> Seq.map (fun (ns, names) -> + let multipleNames = names |> Array.length > 1 + names |> Seq.map (fun (name, ctx) -> ns, name, ctx, multipleNames)) + |> Seq.concat + |> Seq.map (fun (ns, name, ctx, multipleNames) -> let displayText = "open " + ns + if multipleNames then " (" + name + ")" else "" + Environment.NewLine - TextReplaceChange (FileName = activeDocFileName, - Offset = offset, - RemovedChars = 0, - InsertedText = displayText, - Description = String.Format ("Replace '{0}' with '{1}'", lastIdent, ns)) :> Change - |> Array.singleton - //| None ->[||] - //|> Array.iter (fun x -> x.) - - //MessageService.ShowCustomDialog (Dialog.op_Implicit (new Rename.RenameItemDialog("Add Open", symbolUse.Symbol.DisplayName, performChanges symbolUse locations))) - //|> ignore - - //if (segment.Offset <= editor.CaretOffset && editor.CaretOffset <= segment.EndOffset) then - // link.Links.Insert (0, segment) - ////else - //link.AddLink (segment) - //links.Add (link) - //editor.StartTextLinkMode (TextLinkModeOptions (links)) + displayText, + fun () -> + let changes = InsertContext.insertOpenDeclartionWithEditor editor ctx displayText + RefactoringService.AcceptChanges(monitor, changes) + ) -[<ExportCodeFixProvider("F#", Name = "AddOpen"); Shared>] -type internal FSharpAddOpenCodeFixProvider - [<ImportingConstructor>] - ( - document: MonoDevelop.Ide.Gui.Document, - assemblyContentProvider: AssemblyContentProvider, - findIdent: unit -> FSharpSymbolUse option, - monitor - ) = - inherit CodeFixProvider() - let fixableDiagnosticIds = ["FS0039"] - - let checker = MonoDevelop.FSharp.MDLanguageService.Instance.Checker - let fixUnderscoresInMenuText (text: string) = text.Replace("_", "__") - // TryGetOptionsForEditingDocumentOrProject - //let projectInfoManger = MonoDevelop.FSharp.MDLanguageService.Instance.Getproj - - //let qualifySymbolFix (context: CodeFixContext) (fullName, qualifier) = - //CodeAction.Create( - //fixUnderscoresInMenuText fullName, - //fun (cancellationToken: CancellationToken) -> - //async { - // let! (sourceText : SourceText) = context.Document.GetTextAsync() - // return context.Document.WithText(sourceText.Replace(context.Span, qualifier)) - //} |> CommonRoslynHelpers.StartAsyncAsTask(cancellationToken)) - - //let openNamespaceFix (contextDocument: Document) ctx name ns multipleNames = - //let displayText = "open " + ns + if multipleNames then " (" + name + ")" else "" - //// TODO when fresh Roslyn NuGet packages are published, assign "Namespace" Tag to this CodeAction to show proper glyph. - //CodeAction.Create( - //fixUnderscoresInMenuText displayText, - //(fun (cancellationToken: CancellationToken) -> - // async { - // let! sourceText = contextDocument.GetTextAsync() - // return contextDocument.WithText(InsertContext.insertOpenDeclaration sourceText ctx ns) - // } |> CommonRoslynHelpers.StartAsyncAsTask(cancellationToken)), - //displayText) - - let getSuggestions lastIdent (candidates: (Entity * InsertContext) list) = - //let openNamespaceFixes = - candidates - |> Seq.choose (fun (entity, ctx) -> entity.Namespace |> Option.map (fun ns -> ns, entity.Name, ctx)) - |> Seq.groupBy (fun (ns, _, _) -> ns) - |> Seq.map (fun (ns, xs) -> - ns, - xs - |> Seq.map (fun (_, name, ctx) -> name, ctx) - |> Seq.distinctBy (fun (name, _) -> name) - |> Seq.sort - |> Seq.toArray) - |> Seq.map (fun (ns, names) -> - let multipleNames = names |> Array.length > 1 - names |> Seq.map (fun (name, ctx) -> ns, name, ctx, multipleNames)) - - //names |> Seq.map (fun (name) -> ns, name, multipleNames)) - |> Seq.concat - |> Seq.map (fun (ns, name, ctx, multipleNames) -> - - ns, - fun () -> - let changes = InsertContext.insertOpenDeclartionWithEditor lastIdent findIdent document.Editor ctx (ns, multipleNames, name) - RefactoringService.AcceptChanges(monitor, changes) - ) - //openNamespaceFix contextDocument ctx name ns multipleNames) - //"open " + ns + if multipleNames then " (" + name + ")" else "" ) - //|> Seq.toList - - //let quilifySymbolFixes = - //candidates - //|> Seq.map (fun (entity, _) -> entity.FullRelativeName, entity.Qualifier) - //|> Seq.distinct - //|> Seq.sort - //|> Seq.map (qualifySymbolFix context) - //|> Seq.toList - - //openNamespaceFixes - //(openNamespaceFixes )//@ quilifySymbolFixes) - //|> List.map (fun x codefix -> - // //context.RegisterCodeFix - // (codeFix, (context.Diagnostics |> Seq.filter (fun x -> fixableDiagnosticIds |> List.contains x.Id)) - //) - - override __.FixableDiagnosticIds = fixableDiagnosticIds.ToImmutableArray() - - override __.RegisterCodeFixesAsync context : Task = Task.CompletedTask - - member __.CodeFixesAsync (ast:ParseAndCheckResults) (unresolvedIdentRange:Range.range) (cancellationToken) = // (context:CodeFixContext) = + let getCodeFixesAsync (editor:TextEditor) (assemblyContentProvider: AssemblyContentProvider) monitor + (ast:ParseAndCheckResults) (unresolvedIdentRange:Range.range) = asyncMaybe { - //let _, sourceText = document.Editor - //let editor = document.Editor - //let sourceText = document.Editor.Text - //let! options = languageService.GetCheckerOptions(contextDocument.FilePath, contextDocument.Project.FilePath, sourceText.ToString()) - //let! sourceText = contextDocument.GetTextAsync(cancellationToken) - //let! _, parsedInput, checkResults = checker.ParseAndCheckDocument(contextDocument, options, allowStaleResults = true, sourceText = sourceText) - //let! ast = editor.DocumentContext.TryGetAst() let! parsedInput = ast.ParseTree let! checkResults = ast.CheckResults - let sourceText = document.Editor.Text - //let! parsedInput = document.TryGetFSharpParsedDocument() - //parseedInput. - - //let unresolvedIdentRange = - //let startLinePos = sourceText.Lines.GetLinePosition context.Span.Start - //let startPos = Pos.fromZ startLinePos.Line startLinePos.Character - //let endLinePos = sourceText.Lines.GetLinePosition context.Span.End - //let endPos = Pos.fromZ endLinePos.Line endLinePos.Character - //Range.mkRange contextDocument.FilePath startPos endPos - let isAttribute = UntypedParseImpl.GetEntityKind(unresolvedIdentRange.Start, parsedInput) = Some EntityKind.Attribute let entities = @@ -263,403 +135,20 @@ type internal FSharpAddOpenCodeFixProvider e.CleanedIdents |> Array.replace (e.CleanedIdents.Length - 1) (lastIdent.Substring(0, lastIdent.Length - 9)) ]) - //entities |> List.iter (printfn "%A") + let idents = ParsedInput.getLongIdents (Some parsedInput) let longIdent = ParsedInput.getLongIdentAt parsedInput unresolvedIdentRange.End - + let! maybeUnresolvedIdents = longIdent |> Option.map (fun longIdent -> longIdent |> List.map (fun ident -> { Ident = ident.idText - Resolved = not (ident.idRange = unresolvedIdentRange) }) + Resolved = false }) // not (ident.idRange = unresolvedIdentRange) }) |> List.toArray) let createEntity = ParsedInput.tryFindInsertionContext unresolvedIdentRange.StartLine parsedInput maybeUnresolvedIdents let candidates = entities |> Seq.map createEntity |> Seq.concat |> Seq.toList - return getSuggestions "Application" candidates |> Seq.toList - } - //|> Async.Ignore - //|> CommonRoslynHelpers.StartAsyncUnitAsTask(cancellationToken) - - -namespace MonoDevelop.FSharp.CodeActions -open MonoDevelop.CodeActions -open MonoDevelop.Ide.Editor.Extension -open System.Threading -open System.Threading.Tasks -open System -open MonoDevelop.Components.Commands -open MonoDevelop.Refactoring - -//type FSharpCodeActionEditorExtension() = - //inherit TextEditorExtension() - - //let quickFixCancellationTokenSource = new CancellationTokenSource () - - //member internal this.PopupQuickFixMenu (evt:Gdk.EventButton, menuAction:Action<CodeFixMenu>, ?point:Nullable<Xwt.Point>): unit = - // async { - // Thread.Sleep 1000 - // } |> Async.Start - - //member this.SmartTagMarginMarker_ShowPopup (sender:obj, e:EventArgs ):unit = - - // let marker = sender :?> (MonoDevelop.SourceEditor.SmartTagMarginMarker) - // marker |> ignore - - - //[<CommandHandler (RefactoryCommands.QuickFix)>] - //member this.OnQuickFixCommand () = - // //if not AnalysisOptions.EnableFancyFeatures || smartTagMarginMarker = null then - // // //Fixes = RefactoringService.GetValidActions (Editor, DocumentContext, Editor.CaretLocation).Result; - // // PopupQuickFixMenu (null, null); - // //else - // //CancelSmartTagPopupTimeout (); - // this.PopupQuickFixMenu (null, fun menu -> ()); - - - - //member internal this.GetCurrentFixesAsync (cancellationToken:CancellationToken ):Task<CodeActionContainer> = - // //var loc = Editor.CaretOffset; - // //var ad = DocumentContext.AnalysisDocument; - // //var line = Editor.GetLine (Editor.CaretLine); - - // //if (ad == null) { - // // return Task.FromResult (CodeActionContainer.Empty); - // //} - // //TextSpan span; - // //if (Editor.IsSomethingSelected) { - // // var selectionRange = Editor.SelectionRange; - // // span = selectionRange.Offset >= 0 ? TextSpan.FromBounds (selectionRange.Offset, selectionRange.EndOffset) : TextSpan.FromBounds (loc, loc); - // //} else { - // // span = TextSpan.FromBounds (loc, loc); - // //} - - //Async.StartAsTask(async { return null }, cancellationToken = cancellationToken ) - - -//namespace MonoDevelop.CodeActions - -open Gdk -open MonoDevelop.SourceEditor -open MonoDevelop.AnalysisCore.Gui -//open RefactoringEssentials -open MonoDevelop.Refactoring -open MonoDevelop.Ide.Editor.Extension -open MonoDevelop.Ide.Editor -open MonoDevelop.Core.Text -open MonoDevelop.Core -open MonoDevelop.Components.Commands -open MonoDevelop.Components -open MonoDevelop.CodeIssues -open MonoDevelop.AnalysisCore -open Microsoft.CodeAnalysis.Text -open Microsoft.CodeAnalysis.Formatting -open Microsoft.CodeAnalysis.CodeRefactorings -open Microsoft.CodeAnalysis.CodeFixes -open Microsoft.CodeAnalysis -open Gtk -open System.Threading.Tasks -open System.Threading -open System.Reflection -open System.Linq -open System.Collections.Immutable -open System -open System.Collections.Generic - -type FsharpCodeActionEditorExtension() as this = - inherit TextEditorExtension() - let mutable menuTimeout = 150 - let mutable smartTagTask = null - //let mutable quickFixCancellationTokenSource = ObjectCreationExpressionSyntax - //let mutable codeFixService = - // this.Ide.Composition.CompositionManager.``GetExportedValue<ICodeFixService>`` - // () - //let mutable codeRefactoringService = - //this.Ide.Composition.CompositionManager.``GetExportedValue<ICodeRefactoringService>`` - //() - - let mutable smartTagMarginMarker = null - let mutable beginVersion = null - member val internal smartTagPopupTimeoutId: int32 = -1 with get, set - member val internal HasCurrentFixes: bool = false with get, set - - //member this.CancelSmartTagPopupTimeout() = - //if this.smartTagPopupTimeoutId <> 0 then - //GLib.Source.Remove(smartTagPopupTimeoutId) - //this.smartTagPopupTimeoutId <- 0 - - //member this.RemoveWidget() = - //if smartTagMarginMarker <> null then - // this.Editor.RemoveMarker(this.smartTagMarginMarker) - // ``smartTagMarginMarker.ShowPopup -= SmartTagMarginMarker_ShowPopup`` - // smartTagMarginMarker <- null - //else this.CancelSmartTagPopupTimeout() - - //member this.Dispose() = - //this.CancelQuickFixTimer() - //this.RefactoringPreviewTooltipWindow.HidePreviewTooltip() - //``Editor.CaretPositionChanged -= HandleCaretPositionChanged`` - //``DocumentContext.DocumentParsed -= HandleDocumentDocumentParsed`` - //``Editor.TextChanged -= Editor_TextChanged`` - //``Editor.BeginAtomicUndoOperation -= Editor_BeginAtomicUndoOperation`` - //``Editor.EndAtomicUndoOperation -= Editor_EndAtomicUndoOperation`` - //this.RemoveWidget() - //base.Dispose() - - //member this.CancelQuickFixTimer() = - //quickFixCancellationTokenSource.Cancel() - //quickFixCancellationTokenSource <- ObjectCreationExpressionSyntax - //smartTagTask <- null - - //member this.HandleCaretPositionChanged(sender: obj, e: EventArgs) = - // if this.Editor.IsInAtomicUndo then () - // else - // this.CancelQuickFixTimer() - // let mutable token = quickFixCancellationTokenSource.Token - // if this.AnalysisOptions.EnableFancyFeatures - // && this.DocumentContext.ParsedDocument <> null then - // if HasCurrentFixes then - // let mutable curOffset = this.Editor.CaretOffset - // for fix in smartTagTask.Result.CodeFixActions do - // if not fix.TextSpan.Contains(curOffset) then - // this.RemoveWidget() - // BreakStatement - // smartTagTask <- this.GetCurrentFixesAsync(token) - // else this.RemoveWidget() - - //member this.GetCurrentFixesAsync(cancellationToken: CancellationToken) = - // let mutable loc = this.Editor.CaretOffset - // let mutable ad = this.DocumentContext.AnalysisDocument - // let mutable line = this.Editor.GetLine(this.Editor.CaretLine) - // let mutable span = null - // if this.Editor.IsSomethingSelected then - // let mutable selectionRange = this.Editor.SelectionRange - // span <- ConditionalExpressionSyntax - // else span <- this.TextSpan.FromBounds(loc, loc) - // this.Task.Run(fun () -> TryStatement, cancellationToken) - - //member this.FilterOnUIThread(collections: ``ImmutableArray<CodeFixCollection>``, - // workspace: Workspace) = - // this.Runtime.AssertMainThread() - // let mutable caretOffset = this.Editor.CaretOffset - // collections.``Select (c => FilterOnUIThread (c, workspace))``.``Where(x => x != null)``.``OrderBy(x => GetDistance (x, caretOffset))``.ToImmutableArray - // () - - //member this.GetDistance(fixCollection: CodeFixCollection, caretOffset: int) = - // ConditionalExpressionSyntax - - //member this.FilterOnUIThread(collection: CodeFixCollection, - // workspace: Workspace) = - // this.Runtime.AssertMainThread() - // let mutable applicableFixes = - // collection.Fixes.WhereAsArray - // (fun f -> this.IsApplicable(f.Action, workspace)) - // ConditionalExpressionSyntax - - //member this.IsApplicable(action: Microsoft.CodeAnalysis.CodeActions.CodeAction, - // workspace: Workspace) = - // if not action.PerformFinalApplicabilityCheck then True - // else - // this.Runtime.AssertMainThread() - // action.IsApplicable(workspace) - - //member this.PopupQuickFixMenu(evt: Gdk.EventButton, - // menuAction: ``Action<CodeFixMenu>``, - // point: Xwt.``Point?``) = - // use ``_`` = - // this.Refactoring.Counters.FixesMenu.BeginTiming - // ("Show quick fixes menu") - // let mutable token = quickFixCancellationTokenSource.Token - // let mutable fixes = AwaitExpressionSyntax - // if token.IsCancellationRequested then () - // Editor.SuppressTooltips <- True - // this.PopupQuickFixMenu(evt, fixes, menuAction, point) - - //member this.PopupQuickFixMenu(evt: Gdk.EventButton, - // fixes: CodeActionContainer, - // menuAction: ``Action<CodeFixMenu>``, - // point: Xwt.``Point?``) = - // let mutable token = quickFixCancellationTokenSource.Token - // if token.IsCancellationRequested then () - // let mutable menu = - // this.CodeFixMenuService.CreateFixMenu(Editor, fixes, token) - // if token.IsCancellationRequested then () - // if menu.Items.Count = 0 then () - // if menuAction <> null then menuAction (menu) - // let mutable rect = null - // let mutable widget = Editor - // if not point.HasValue then - // let mutable p = - // this.Editor.LocationToPoint(this.Editor.CaretLocation) - // rect <- ObjectCreationExpressionSyntax - // else rect <- ObjectCreationExpressionSyntax - // this.ShowFixesMenu(widget, rect, menu) - - //member this.ShowFixesMenu(parent: Widget, evt: Gdk.Rectangle, - // entrySet: CodeFixMenu) = - // if parent = null || parent.GdkWindow = null then - // Editor.SuppressTooltips <- False - // True - // else TryStatement; True - - //member this.CreateContextMenu(entrySet: CodeFixMenu) = - // let mutable menu = ObjectCreationExpressionSyntax - // for item in entrySet.Items do - // if item = this.CodeFixMenuEntry.Separator then - // menu.Items.Add(ObjectCreationExpressionSyntax) - // ContinueStatement - // let mutable menuItem = ObjectCreationExpressionSyntax - // menuItem.Context <- item.Action - // if item.Action = null then - // if not ParenthesizedExpressionSyntax - // || itemAsMenu.Items.Count <= 0 then - // menuItem.Sensitive <- False - // let mutable subMenu = item :?> CodeFixMenu - // if subMenu <> null then - // menuItem.SubMenu <- this.CreateContextMenu(subMenu) - // menuItem.Selected.AddHandler<_> - // (fun () -> - // this.RefactoringPreviewTooltipWindow.HidePreviewTooltip()) - // menuItem.Deselected.AddHandler<_> - // (fun () -> - // this.RefactoringPreviewTooltipWindow.HidePreviewTooltip()) - // else - // menuItem.Clicked.AddHandler<_> - // (fun (sender, e) -> - // this.``((System``.``Action)((ContextMenuItem)sender)``.``Context)`` - // ()) - // menuItem.Selected.AddHandler<_> - // (fun (sender, e) -> - // this.RefactoringPreviewTooltipWindow.HidePreviewTooltip() - // if item.ShowPreviewTooltip <> null then - // item.ShowPreviewTooltip(e)) - // menuItem.Deselected.AddHandler<_> - // (fun () -> - // this.RefactoringPreviewTooltipWindow.HidePreviewTooltip()) - // menu.Items.Add(menuItem) - // menu.Closed.AddHandler<_> - // (fun () -> this.RefactoringPreviewTooltipWindow.HidePreviewTooltip()) - // menu - - //member this.CreateSmartTag(fixes: CodeActionContainer, offset: int) = - // if not this.AnalysisOptions.EnableFancyFeatures || fixes.IsEmpty then - // this.RemoveWidget() - // () - // else - // let mutable editor = Editor - // if editor = null then - // this.RemoveWidget() - // () - // if this.DocumentContext.ParsedDocument = null - // || this.DocumentContext.ParsedDocument.IsInvalid then - // this.RemoveWidget() - // () - // let mutable severity = fixes.GetSmartTagSeverity() - // if ConditionalAccessExpressionSyntax <> editor.CaretLine then - // this.RemoveWidget() - // smartTagMarginMarker <- ObjectCreationExpressionSyntax - // smartTagMarginMarker.ShowPopup.AddHandler<_> - // (SmartTagMarginMarker_ShowPopup) - // editor.AddMarker - // (editor.GetLine(editor.CaretLine), smartTagMarginMarker) - // else - // smartTagMarginMarker.SmartTagSeverity <- severity - // let mutable view = editor.``GetContent<SourceEditorView>`` () - // view.TextEditor.RedrawMarginLine - // (view.TextEditor.TextArea.QuickFixMargin, editor.CaretLine) - - //member this.SmartTagMarginMarker_ShowPopup(sender: obj, e: EventArgs) = - // let mutable marker = CastExpressionSyntax - // this.CancelSmartTagPopupTimeout() - // smartTagPopupTimeoutId <- this.GLib.Timeout.Add(menuTimeout, - // fun () -> - // this.PopupQuickFixMenu - // (null, - // fun menu -> (), - // ObjectCreationExpressionSyntax) - // smartTagPopupTimeoutId <- 0 - // False) - - //member this.Initialize() = - // base.Initialize() - // DocumentContext.DocumentParsed.AddHandler<_> - // (HandleDocumentDocumentParsed) - // Editor.CaretPositionChanged.AddHandler<_> (HandleCaretPositionChanged) - // Editor.TextChanged.AddHandler<_> (Editor_TextChanged) - // Editor.BeginAtomicUndoOperation.AddHandler<_> - // (Editor_BeginAtomicUndoOperation) - // Editor.EndAtomicUndoOperation.AddHandler<_> - // (Editor_EndAtomicUndoOperation) - - //member this.Editor_BeginAtomicUndoOperation(sender: obj, e: EventArgs) = - // beginVersion <- this.Editor.Version - - //member this.Editor_EndAtomicUndoOperation(sender: obj, e: EventArgs) = - // if beginVersion <> null - // && beginVersion.CompareAge(this.Editor.Version) <> 0 then - // this.RemoveWidget() - // else beginVersion <- null - - //member this.Editor_TextChanged(sender: obj, - // e: MonoDevelop.Core.Text.TextChangeEventArgs) = - // if this.Editor.IsInAtomicUndo then () - // else - // this.RemoveWidget() - // this.HandleCaretPositionChanged(null, this.EventArgs.Empty) - - //member this.HandleDocumentDocumentParsed(sender: obj, e: EventArgs) = - // this.HandleCaretPositionChanged(null, this.EventArgs.Empty) - - //member this.CurrentSmartTagPopup() = - //this.CancelSmartTagPopupTimeout() - //smartTagPopupTimeoutId <- this.GLib.Timeout.Add(menuTimeout, - //fun () -> - //this.PopupQuickFixMenu - // (null, - // fun menu -> ()) - //smartTagPopupTimeoutId <- 0 - //False) - - [<CommandHandler(RefactoryCommands.QuickFix)>] - member this.OnQuickFixCommand() = - //if not AnalysisOptions.EnableFancyFeatures - //|| smartTagMarginMarker = null then - //this.PopupQuickFixMenu(null, null) - () - //else - //this.CancelSmartTagPopupTimeout() - //this.PopupQuickFixMenu(null, fun menu -> ()) - -open MonoDevelop.Core -open System.Threading -open System.Threading.Tasks -open MonoDevelop.Ide -open MonoDevelop.CodeActions -open System -open MonoDevelop.Components.Commands - -open MonoDevelop.Ide.CodeCompletion -open System.Collections.Immutable -open Microsoft.CodeAnalysis -open MonoDevelop.Projects -open MonoDevelop.Ide -open Microsoft.CodeAnalysis.Options -open MonoDevelop.Ide.Editor -open MonoDevelop.Core.Instrumentation -open System.Diagnostics -open MonoDevelop.Ide.TypeSystem -open MonoDevelop.CodeIssues -open MonoDevelop.CodeActions -open System.Threading -open System.Threading.Tasks -open MonoDevelop.AnalysisCore -open System.Linq -open MonoDevelop.Ide.Gui -open MonoDevelop.Core -open Mono.Addins -open System -open System.Collections.Generic - - + return getSuggestions editor monitor candidates |> Seq.toList + } diff --git a/main/external/fsharpbinding/MonoDevelop.FSharpBinding/RefactoringOperationsHandler.fs b/main/external/fsharpbinding/MonoDevelop.FSharpBinding/RefactoringOperationsHandler.fs index bf654def09..2a2e682e8d 100644 --- a/main/external/fsharpbinding/MonoDevelop.FSharpBinding/RefactoringOperationsHandler.fs +++ b/main/external/fsharpbinding/MonoDevelop.FSharpBinding/RefactoringOperationsHandler.fs @@ -756,6 +756,7 @@ open System.Collections.Immutable; open Microsoft.FSharp.Compiler open Microsoft.FSharp.Compiler.Range open Microsoft.FSharp.Compiler.Ast +open MonoDevelop.FSharp.Shared type RapidFixMenuHandler() as this = @@ -782,100 +783,84 @@ type RapidFixMenuHandler() as this = let symbolStart, _symbolEnd = symbolRange symbolStart >= selection.Offset - let getExpandRange (editor:TextEditor, tree:ParsedInput) = - let posStart = Range.mkPos 7 12 - let posEnd = Range.mkPos 7 23 - Range.mkRange "File" posStart posEnd |> Some + - //if not editor.IsSomethingSelected then None - //else - //let rangeAsOffsets(range:Range.range) = - // let startPos = editor.LocationToOffset(range.StartLine, range.StartColumn + 1) - // let endPos = editor.LocationToOffset(range.EndLine, range.EndColumn + 1) - // (startPos, endPos) + let getSymbolAtLocationInFile(projectFilename, fileName, version, source, line:int, col, lineStr) = + asyncMaybe { + LoggingService.logDebug "LanguageService: GetUsesOfSymbolAtLocationInFile: file:%s, line:%i, col:%i" (Path.GetFileName(fileName)) line col + let! _colu, identIsland = Parsing.findIdents col lineStr SymbolLookupKind.ByLongIdent |> async.Return + let! results = languageService.GetTypedParseResultWithTimeout(projectFilename, fileName, version, source, AllowStaleResults.MatchingSource) + + let! range = + results.GetErrors() + |> Seq.filter (fun x -> x.Severity = FSharpErrorSeverity.Error) + |> Seq.filter (fun x -> x.StartLineAlternate = x.EndLineAlternate) + |> Seq.filter (fun x -> x.StartLineAlternate = line) + |> Seq.filter (fun x -> x.StartColumn < col && col < x.EndColumn) + |> Seq.map (fun x -> + let startPos = mkPos line x.StartColumn + let endPos = mkPos line x.EndColumn + mkRange "" startPos endPos + ) + |> Seq.distinct + |> Seq.tryHead + return range - - //let rec walker = - // { new AstTraversal.AstVisitorBase<_>() with - - // override this.VisitModuleDecl(defaultTraverse, decl) = - // match decl with - // | SynModuleDecl.Open(_, range) -> Some([], range) - // | _ -> defaultTraverse(decl) - - // member this.VisitExpr(path, traverseSynExpr, defaultTraverse, expr) = - // match expr with - // | SynExpr.LongIdent(_,_,_,range) -> Some (path, range) - // | SynExpr.Ident ident -> Some (path, ident.idRange) - // | SynExpr.Const(synconst, constRange) -> - // match synconst with - // | SynConst.String(_str, range) -> Some (path, range) - // | _ -> Some (path, constRange) - // | _ -> - // if inside(rangeAsOffsets expr.Range, editor.SelectionRange) then - // Some (path, expr.Range) - // else - // defaultTraverse(expr) } - - ////let traversePath = - //AstTraversal.Traverse(mkPos editor.CaretLine (editor.CaretColumn), tree, walker) - //|> Option.map (snd) - - //let rangesFromTraverse = function - // | TraverseStep.Binding binding -> [binding.RangeOfHeadPat; binding.RangeOfBindingAndRhs; binding.RangeOfBindingSansRhs] - // | TraverseStep.MatchClause synMatchClause -> [synMatchClause.Range] - // | TraverseStep.Expr synExpr -> [synExpr.Range] - // | TraverseStep.MemberDefn synMemberDefn -> [synMemberDefn.Range] - // | TraverseStep.Module synModuleDecl -> [synModuleDecl.Range] - // | TraverseStep.ModuleOrNamespace synModuleOrNamespace -> [synModuleOrNamespace.Range] - // | TraverseStep.TypeDefn synTypeDefn -> [synTypeDefn.Range] - - //let selectionLineStart = - // let line = editor.GetLine editor.SelectionRegion.BeginLine - // if editor.SelectionRegion.BeginLine <> editor.SelectionRegion.EndLine then - // line.Offset + line.GetIndentation(editor).Length, editor.SelectionRange.EndOffset - // else - // line.Offset + line.GetIndentation(editor).Length, line.EndOffset - - //let wholeDocument = - // 0, editor.Length - - //traversePath - //|> Option.bind - //(fun (traverseSteps, range) -> - //let ranges = - // traverseSteps - // |> List.collect rangesFromTraverse - - //let offsetRanges = - // range::ranges |> List.map rangeAsOffsets - - //let allranges = - // [yield wholeDocument; yield selectionLineStart; yield! offsetRanges] - - //allranges - //|> List.filter (fun range -> biggerOverlap(range, editor.SelectionRange)) - //|> List.sortBy (fun (startPos, endPos) -> endPos - startPos) - //|> List.tryHead) - let selection (editor:TextEditor) = - maybe { - let! ast = editor.DocumentContext.TryGetAst() - let! tree = ast.ParseTree - let! selection = getExpandRange(editor, tree) - return (ast, selection) + //let! foo = results.GetSymbolAtLocation (line, col, lineStr) |> Async.RunSynchronously + //let foobar = results.GetDeclarationLocation (line, col, lineStr) |> Async.RunSynchronously + + //let range: Range.range option = None + //let! symbolUse = results.GetSymbolAtLocation(line, col, lineStr) + //let lastIdent = Seq.last identIsland + //return (lastIdent, symbolUse) } - let provider () = - IdeApp.Workbench.ActiveDocument - |> Option.ofObj - |> Option.bind (fun document -> - selection document.Editor |> Option.map (fun (ast, range) -> document, ast, range)) - |> Option.map (fun (document, ast, range) -> - let monitor = IdeApp.Workbench.ProgressMonitors.GetBackgroundProgressMonitor ("Add Open", IconId()); - ast, range, FSharpAddOpenCodeFixProvider(document, MonoDevelop.FSharp.AssemblyContentProvider (), findIdent, monitor ) - ) + //let getSymbolAtLocationInFile (projectFilename, fileName, version, source, line:int, col, lineStr) = + //asyncMaybe { + //LoggingService.logDebug "LanguageService: GetUsesOfSymbolAtLocationInFile: file:%s, line:%i, col:%i" (Path.GetFileName(fileName)) line col + //let! _colu, identIsland = Parsing.findIdents col lineStr SymbolLookupKind.ByLongIdent |> async.Return + //let! results = languageService.GetTypedParseResultWithTimeout(projectFilename, fileName, version, source, AllowStaleResults.MatchingSource) + //let! symbolUse = results.GetSymbolAtLocation(line, col, lineStr) + //let lastIdent = Seq.last identIsland + //return (lastIdent, symbolUse) } + + let getSymbolUseForEditorCaret (editor: TextEditor) = + match IdeApp.Workbench.ActiveDocument with + | null -> async { return None } + | doc when doc.FileName = FilePath.Null || doc.FileName <> editor.FileName || doc.ParsedDocument = null -> async { return None } + | _doc -> + let documentContext = _doc + async { + LoggingService.logDebug "HighlightUsagesExtension: ResolveAsync starting on %s" (documentContext.Name |> IO.Path.GetFileName ) + try + let line, col, lineStr = editor.GetLineInfoByCaretOffset () + + //let idents = Parsing.findIdents col lineStr MonoDevelop.FSharp.Shared.SymbolLookupKind.ByLongIdent + let currentFile = documentContext.Name + let source = editor.Text + let projectFile = documentContext.Project |> function null -> currentFile | project -> project.FileName.ToString() + + //let tast = lang + let! symbolReferences = getSymbolAtLocationInFile (projectFile, currentFile, 0, source, line, col, lineStr) + return symbolReferences + with + | :? TaskCanceledException -> return None + | exn -> LoggingService.LogError("Unhandled Exception in F# HighlightingUsagesExtension", exn) + return None + } + + let getCodeFixes () = + asyncMaybe { + let! document = IdeApp.Workbench.ActiveDocument |> Option.ofObj + let editor = document.Editor + let! ast = editor.DocumentContext.TryGetAst() + let! range = getSymbolUseForEditorCaret editor - let cancellationToken = new CancellationToken() + let monitor = IdeApp.Workbench.ProgressMonitors.GetBackgroundProgressMonitor ("Add Open", IconId()); + let assemblyProvider = MonoDevelop.FSharp.AssemblyContentProvider () + let! codeFixes = FSharpAddOpenCodeFixProvider.getCodeFixesAsync document.Editor assemblyProvider monitor ast range + return codeFixes + } override x.Run () = base.Run () @@ -888,98 +873,31 @@ type RapidFixMenuHandler() as this = override x.Update (ci:CommandInfo) = this.Update (ci.ArrayInfo) + override x.UpdateAsync (info:CommandArrayInfo, cancelToken: CancellationToken) = + info.Add (new CommandInfo (GettextCatalog.GetString ("Loading..."), false, false), null); + let mainThread = System.Threading.SynchronizationContext.Current - override x.Update (ainfo:CommandArrayInfo) = - //let ainfo = ci.ArrayInfo - - let document = IdeApp.Workbench.ActiveDocument - let editor = document.Editor - - - //document.Project - - - //let mutable fixes = [] - //let context = - //new CodeFixContext(document, ImmutableArray<Diagnostic>.Empty, - //// TODO: Can we share code between similar lambdas that we pass to this API in BatchFixAllProvider.cs, CodeFixService.cs and CodeRefactoringService.cs? - //Action<CodeActions.CodeAction, ImmutableArray<Diagnostic>>(fun action applicableDiagnostics -> - //// Serialize access for thread safety - we don't know what thread the fix provider will call this delegate from. - //lock (fixes) (fun () -> - // fixes <- (new CodeFix(document.Project, action, applicableDiagnostics)) :: fixes - //)), cancellationToken) - //, - //verifyArguments = false, - //cancellationToken = cancellationToken); - - provider () - |> Option.bind (fun (ast, range, x) -> - let t = x.CodeFixesAsync ast range cancellationToken - Async.RunSynchronously (t, cancellationToken = cancellationToken) - - ) - |> Option.toList - |> List.concat - |> function - | [] -> - ainfo.Add (new CommandInfo (GettextCatalog.GetString ("No code fixes available"), false, false), null); - | xs -> - xs |> List.iter (fun (x, action) -> - //let x = x.Title - printfn "Suggestion: %s" x - ainfo.Add (new CommandInfo (GettextCatalog.GetString (x), true, false), Action(action)); - ) - - //context.Document. - - - - - //member x.UpdateQuickFixMenu(ci:CommandInfo) = - // x.Update(ci) - - //override x.Update (ci:CommandInfo) = - - //let info = ci.ArrayInfo - //ainfo.Add (new CommandInfo (GettextCatalog.GetString ("No code fixes available"), false, false), null); - - - - - //let mutable ext = editor |> Option.bind (fun x -> x.GetContent<CodeActionEditorExtension> () |> Option.ofObj) - //if Option.isNone ext then () else - //() - //let mutable metadata = new Counters.FixesMenuMetadata (); - //use timer = Counters.FixesMenu.BeginTiming ("Quick Fix menu", metadata) - //info.Add(ObjectCreationExpressionSyntax, null) - //let mutable currentFixes = AwaitExpressionSyntax - //let mutable menu = - // this.CodeFixMenuService.CreateFixMenu - // (editor, currentFixes, cancelToken) - //info.Clear() - //for item in menu.Items do - // this.AddItem(info, item) - //if menu.Items.Count = 0 then - // info.Add(ObjectCreationExpressionSyntax, null) - //metadata.SetSuccess() - //info.NotifyChanged() - - member this.CreateCommandInfoSet(menu: CodeFixMenu) = - - let mutable cis = new CommandInfoSet (); - cis.Text <- menu.Label - for item in menu.Items do - this.AddItem(cis.CommandInfos, item) - cis - - //override this.Run(data) = - // match data with - // | :? Action as x -> x.Invoke() - // | _ -> () - - //member this.Run(editor:TextEditor, ctx:DocumentContext) = - //() - + async { + let! codeFixQuery = getCodeFixes () + + do! Async.SwitchToContext mainThread + info.Clear() + + match codeFixQuery with + | None -> + info.Add (new CommandInfo (GettextCatalog.GetString ("No code fixes available"), false, false), null); + | Some codeFixes -> + match codeFixes with + | [] -> + info.Add (new CommandInfo (GettextCatalog.GetString ("No code fixes available"), false, false), null); + | xs -> + xs |> List.iter (fun (x, action) -> + info.Add (new CommandInfo (GettextCatalog.GetString (x), true, false), Action(action)); + ) + info.NotifyChanged (); + } + |> Async.Ignore + |> CommonRoslynHelpers.StartAsyncUnitAsTask cancelToken member this.AddItem(cis: CommandArrayInfo, item: CodeFixMenuEntry) = if item = CodeFixMenuEntry.Separator then @@ -1024,12 +942,4 @@ type FSharpCommandsTextEditorExtension () = [<CommandHandler ("MonoDevelop.Refactoring.RefactoryCommands.FindReferences")>] member x.FindReferences () = FindReferencesHandler().Run(x.Editor, x.DocumentContext) - - //[<CommandHandler ("MonoDevelop.Refactoring.RefactoryCommands.QuickFixMenu")>] - //member x.QuickFixComand () = - // SuperFixMenuHandler().Run(x.Editor, x.DocumentContext) - - //[<CommandHandler ("MonoDevelop.Refactoring.RefactoryCommands.QuickFixMenu")>] - //member x.QuickFixCommand_Update(ci:CommandInfo) = - //SuperFixMenuHandler().UpdateQuickFixMenu(ci)
\ No newline at end of file diff --git a/main/external/fsharpbinding/MonoDevelop.FSharpBinding/Services/LanguageService.fs b/main/external/fsharpbinding/MonoDevelop.FSharpBinding/Services/LanguageService.fs index c1a4f6ef5b..1fea03dc65 100644 --- a/main/external/fsharpbinding/MonoDevelop.FSharpBinding/Services/LanguageService.fs +++ b/main/external/fsharpbinding/MonoDevelop.FSharpBinding/Services/LanguageService.fs @@ -510,6 +510,15 @@ type LanguageService(dirtyNotify, _extraProjectInfo) as x = let! refs = results.GetUsesOfSymbolInFile(symbolUse.Symbol) |> Async.map Some return (lastIdent, refs) } + member x.GetSymbolAtLocationInFile(projectFilename, fileName, version, source, line:int, col, lineStr) = + asyncMaybe { + LoggingService.logDebug "LanguageService: GetUsesOfSymbolAtLocationInFile: file:%s, line:%i, col:%i" (Path.GetFileName(fileName)) line col + let! _colu, identIsland = Parsing.findIdents col lineStr SymbolLookupKind.ByLongIdent |> async.Return + let! results = x.GetTypedParseResultWithTimeout(projectFilename, fileName, version, source, AllowStaleResults.MatchingSource) + let! symbolUse = results.GetSymbolAtLocation(line, col, lineStr) + let lastIdent = Seq.last identIsland + return (lastIdent, symbolUse) } + /// Get all the uses of the specified symbol in the current project and optionally all dependent projects member x.GetUsesOfSymbolInProject(projectFilename, file, source, symbol:FSharpSymbol, ?dependentProjects) = async { diff --git a/main/external/fsharpbinding/MonoDevelop.FSharpBinding/VS/ServiceAssemblyContent.fs b/main/external/fsharpbinding/MonoDevelop.FSharpBinding/VS/ServiceAssemblyContent.fs index 3565b1dddc..239f1eb888 100644 --- a/main/external/fsharpbinding/MonoDevelop.FSharpBinding/VS/ServiceAssemblyContent.fs +++ b/main/external/fsharpbinding/MonoDevelop.FSharpBinding/VS/ServiceAssemblyContent.fs @@ -469,7 +469,7 @@ module internal Entity = |> Array.heads // long ident must contain an unresolved part, otherwise we show false positive suggestions like // "open System" for `let _ = System.DateTime.Naaaw`. Here only "Naaw" is unresolved. - //|> Array.filter (fun x -> x |> Array.exists (fun x -> not x.Resolved)) + |> Array.filter (fun x -> x |> Array.exists (fun x -> not x.Resolved)) |> Array.choose (fun parts -> let parts = parts |> Array.map (fun x -> x.Ident) if not (candidate |> Array.endsWith parts) then None @@ -892,12 +892,9 @@ module internal ParsedInput = let getLongIdentAt ast pos = let idents = getLongIdents (Some ast) - idents.Values |> Seq.tryFind (fun x -> - x |> List.exists (fun x -> x.idText = "Application") ) - - //match idents.TryGetValue pos with - //| true, idents -> Some idents - //| _ -> None + match idents.TryGetValue pos with + | true, idents -> Some idents + | _ -> None type Col = int |