Skip to content
This repository has been archived by the owner on Jan 3, 2019. It is now read-only.

Commit

Permalink
xmldoc help text and parameter info
Browse files Browse the repository at this point in the history
get the xml doc help for methods and display it
  • Loading branch information
funnelweb committed Nov 8, 2012
1 parent 9e696a6 commit 557d5c3
Show file tree
Hide file tree
Showing 2 changed files with 156 additions and 30 deletions.
61 changes: 48 additions & 13 deletions monodevelop/MonoDevelop.FSharpBinding/FSharpTextEditorCompletion.fs
Original file line number Diff line number Diff line change
Expand Up @@ -24,55 +24,88 @@ type internal FSharpMemberCompletionData(mi:Declaration) =
override x.Description = TipFormatter.formatTip mi.DescriptionText
override x.Icon = new MonoDevelop.Core.IconId(ServiceUtils.getIcon mi.Glyph)


/// Completion data representing a delayed fetch of completion data
type internal FSharpTryAgainMemberCompletionData() =
inherit CompletionData(CompletionText = "", DisplayText="Declarations list not yet available...", DisplayFlags = DisplayFlags.None )
override x.Description = "The declaration list is not yet available or the operation timed out. Try again?"
override x.Icon = new MonoDevelop.Core.IconId("md-event")

/// Completion data representing a failure in the ability to get completion data
type internal FSharpErrorCompletionData(exn:exn) =
inherit CompletionData(CompletionText = "", DisplayText=exn.Message, DisplayFlags = DisplayFlags.None )
let text = exn.ToString()
override x.Description = text
override x.Icon = new MonoDevelop.Core.IconId("md-event")

/// Provide information to the 'method overloads' windows that comes up when you type '('
type ParameterDataProvider(nameStart: int, meths: MethodOverloads) =
interface IParameterDataProvider with
member x.Count = meths.Methods.Length

// Get the index into the file where the parameter completion was triggered
member x.StartOffset = nameStart

// Returns the markup to use to represent the specified method overload
// in the parameter information window.
/// Returns the markup to use to represent the specified method overload
/// in the parameter information window.
member x.GetHeading (overload:int, parameterMarkup:string[], currentParameter:int) =
let meth = meths.Methods.[overload]
let text = TipFormatter.formatTip meth.Description
let lines = text.Split([| '\n';'\r' |])
if lines.Length = 0 then meths.Name else lines.[0]
//prename = GLib.Markup.EscapeText (function.FullName.Substring (0, len + 2));
//return prename + "<b>" + function.Name + "</b>" + " (" + paramTxt + ")" + cons;

let lines = text.Split [| '\n';'\r' |]

// Try to highlight the current parameter in bold. Hack apart the text based on (, comma, and ), then
// put it back together again.
//
// @todo This will not be perfect when the text contains generic types with more than one type parameter
// since they will have extra commas.

let text = if lines.Length = 0 then meths.Name else lines.[0]
let textL = text.Split '('
if textL.Length <> 2 then text else
let text0 = textL.[0]
let text1 = textL.[1]
let text1L = text1.Split ')'
if text1L.Length <> 2 then text else
let text10 = text1L.[0]
let text11 = text1L.[1]
let text10L = text10.Split ','
let text10L = text10L |> Array.mapi (fun i x -> if i = currentParameter then "<b>" + x + "</b>" else x)
textL.[0] + "(" + String.Join(",", text10L) + ")" + text11

/// Get the lower part of the text for the display of an overload
member x.GetDescription (overload:int, currentParameter:int) =
let meth = meths.Methods.[overload]
let text = TipFormatter.formatTip meth.Description
let lines = text.Split([| '\n';'\r' |])
if lines.Length <= 1 then "" else lines.[1..] |> String.concat "\n"
let lines = if lines.Length <= 1 then [| "" |] else lines.[1..]
let param =
meth.Parameters |> Array.mapi (fun i param ->
let paramDesc =
// Sometimes the parameter decription is hidden in the XML docs
match TipFormatter.extractParamTip param.Name meth.Description with
| Some tip -> tip
| None -> param.Description
let name = param.Name
let name = if i = currentParameter then "<b>" + name + "</b>" else name
let text = name + ": " + GLib.Markup.EscapeText paramDesc
text
)
String.Join("\n\n", Array.append lines param)

// Returns the text to use to represent the specified parameter
member x.GetParameterDescription (overload:int, paramIndex:int) =
let meth = meths.Methods.[overload]
let param = meth.Parameters.[paramIndex]
param.Description
//return GLib.Markup.EscapeText (function.Parameters[paramIndex]);
param.Name

// Returns the number of parameters of the specified method
member x.GetParameterCount (overload:int) =
let meth = meths.Methods.[overload]
meth.Parameters.Length

// @todo should return 'true' for param-list methods
member x.AllowParameterList (overload: int) =
false
//let meth = meths.Methods.[overload]
//meth.

/// Implements text editor extension for MonoDevelop that shows F# completion
type FSharpTextEditorCompletion() =
inherit CompletionTextEditorExtension()
Expand All @@ -83,6 +116,7 @@ type FSharpTextEditorCompletion() =

override x.Initialize() = base.Initialize()

/// Provide parameter and method overload information when you type '(', ',' or ')'
override x.HandleParameterCompletion(context:CodeCompletionContext, completionChar:char) : IParameterDataProvider =
try
if (completionChar <> '(' && completionChar <> ',' && completionChar <> ')' ) then null else
Expand Down Expand Up @@ -149,6 +183,7 @@ type FSharpTextEditorCompletion() =
result.Add(new FSharpErrorCompletionData(e))
result :> ICompletionDataList

// @todo find out what this is used for
override x.GetParameterCompletionCommandOffset(cpos:byref<int>) =
false

Expand Down
125 changes: 108 additions & 17 deletions monodevelop/MonoDevelop.FSharpBinding/Services/LanguageService.fs
Original file line number Diff line number Diff line change
Expand Up @@ -59,21 +59,65 @@ module ServiceSettings =
/// Formatting of tool-tip information displayed in F# IntelliSense
module internal TipFormatter =

/// A standard memoization function
let memoize f =
let d = new System.Collections.Generic.Dictionary<_,_>(HashIdentity.Structural)
fun x -> if d.ContainsKey x then d.[x] else let res = f x in d.[x] <- res; res

/// Memoize the objects that manage access to XML files.
///
/// @todo consider if this needs to be a weak table in some way
let xmlDocProvider = memoize (fun x -> ICSharpCode.NRefactory.Documentation.XmlDocumentationProvider(x))

/// Return the XmlDocumentationProvider for an assembly
let findXmlDocProviderForAssembly file =
let tryExists s = try if File.Exists s then Some s else None with _ -> None
let e =
match tryExists (Path.ChangeExtension(file,"xml")) with
| Some x -> Some x
| None -> tryExists (Path.ChangeExtension(file,"XML"))
match e with
| None -> None
| Some xmlFile ->
let docReader = xmlDocProvider xmlFile
if docReader = null then None else Some docReader


/// Format some of the data returned by the F# compiler
let private buildFormatComment cmt (sb:StringBuilder) =
match cmt with
| XmlCommentText(s) -> sb.AppendLine("<i>" + GLib.Markup.EscapeText(s) + "</i>")
| XmlCommentText(s) -> sb.AppendLine("<i>" + GLib.Markup.EscapeText(s) + "</i>") |> ignore
// For 'XmlCommentSignature' we could get documentation from 'xml'
// files, but I'm not sure whether these are available on Mono
| _ -> sb

// If 'isSingle' is true (meaning that this is the only tip displayed)
// then we add first line "Multiple overloads" because MD prints first
// int in bold (so that no overload is highlighted)
| XmlCommentSignature(file,key) ->
match findXmlDocProviderForAssembly file with
| None -> ()
| Some docReader ->
let doc = docReader.GetDocumentation(key)
if not (System.String.IsNullOrEmpty(doc)) then
let summary =
let tag1 = "<summary>"
let tag2 = "</summary>"
let idx1 = doc.IndexOf tag1
let idx2 = doc.IndexOf tag2
if (idx2 >= 0 && idx1 >= 0) then doc.Substring (idx1 + tag1.Length, idx2 - idx1 - tag1.Length)
elif (idx1 >= 0) then doc.Substring (idx1 + tag1.Length)
elif (idx2 >= 0) then doc.Substring (0, idx2 - 1)
else doc
sb.AppendLine("<i>" + GLib.Markup.EscapeText(summary) + "</i>") |> ignore
| _ -> ()

/// Format some of the data returned by the F# compiler
///
/// If 'isSingle' is true (meaning that this is the only tip displayed)
/// then we add first line "Multiple overloads" because MD prints first
/// int in bold (so that no overload is highlighted)
let private buildFormatElement isSingle el (sb:StringBuilder) =
match el with
| DataTipElementNone -> sb
| DataTipElementNone -> ()
| DataTipElement(it, comment) ->
sb.AppendLine(GLib.Markup.EscapeText(it)) |> buildFormatComment comment
sb.AppendLine(GLib.Markup.EscapeText(it)) |> ignore
buildFormatComment comment sb
| DataTipElementGroup(items) ->
let items, msg =
if items.Length > 10 then
Expand All @@ -82,24 +126,71 @@ module internal TipFormatter =
else items, null
if (isSingle && items.Length > 1) then
sb.AppendLine("Multiple overloads") |> ignore
for (it, comment) in items do
sb.AppendLine(GLib.Markup.EscapeText(it)) |> buildFormatComment comment |> ignore
if msg <> null then sb.AppendFormat(msg) else sb
items |> Seq.iteri (fun i (it,comment) ->
sb.AppendLine(GLib.Markup.EscapeText it) |> ignore
if i = 0 then
sb.Append(GLib.Markup.EscapeText "\n") |> ignore
buildFormatComment comment sb |> ignore
sb.Append(GLib.Markup.EscapeText "\n") |> ignore
)
if msg <> null then sb.Append(msg) |> ignore
| DataTipElementCompositionError(err) ->
sb.Append("Composition error: " + GLib.Markup.EscapeText(err))
sb.Append("Composition error: " + GLib.Markup.EscapeText(err)) |> ignore

/// Format some of the data returned by the F# compiler
let private buildFormatTip tip (sb:StringBuilder) =
match tip with
| DataTipText([single]) -> sb |> buildFormatElement true single
| DataTipText([single]) -> buildFormatElement true single sb
| DataTipText(its) ->
sb.AppendLine("Multiple items") |> ignore
its |> Seq.mapi (fun i it -> i = 0, it) |> Seq.fold (fun sb (first, item) ->
if not first then sb.AppendLine("\n--------------------\n") |> ignore
sb |> buildFormatElement false item) sb
its |> Seq.iteri (fun i item ->
if i <> 0 then sb.AppendLine("\n--------------------\n") |> ignore
buildFormatElement false item sb)

/// Format tool-tip that we get from the language service as string
let formatTip tip =
(buildFormatTip tip (new StringBuilder())).ToString().Trim('\n', '\r')
let sb = new StringBuilder()
buildFormatTip tip sb
sb.ToString().Trim('\n', '\r')

/// For elements with XML docs, the paramater descriptions are buried in the XML. Fetch it.
let private extractParamTipFromComment paramName comment =
match comment with
| XmlCommentText(s) -> None
// For 'XmlCommentSignature' we could get documentation from 'xml'
// files, but I'm not sure whether these are available on Mono
| XmlCommentSignature(file,key) ->
match findXmlDocProviderForAssembly file with
| None -> None
| Some docReader ->
let doc = docReader.GetDocumentation(key)
if System.String.IsNullOrEmpty(doc) then None else
// get the <param name="...">...</param> node
let summary =
let tag1 = "<param name=\"" + paramName + "\">"
let tag2 = "</param>"
let idx1 = doc.IndexOf tag1
let idx2 = doc.IndexOf(tag2, max idx1 0)
if (idx2 >= 0 && idx1 >= 0) then doc.Substring (idx1 + tag1.Length, idx2 - idx1 - tag1.Length)
elif (idx1 >= 0) then doc.Substring (idx1 + tag1.Length)
elif (idx2 >= 0) then doc.Substring (0, idx2 - 1)
else doc
// remove the <paramref name="..."> from the text
let summary = summary.Replace("<paramref name=\"","").Replace("\" />","").Replace("\"/>","")
Some (GLib.Markup.EscapeText(summary))
| _ -> None

/// For elements with XML docs, the paramater descriptions are buried in the XML. Fetch it.
let private extractParamTipFromElement paramName element =
match element with
| DataTipElementNone -> None
| DataTipElement(it, comment) -> extractParamTipFromComment paramName comment
| DataTipElementGroup(items) -> List.tryPick (snd >> extractParamTipFromComment paramName) items
| DataTipElementCompositionError(err) -> None

/// For elements with XML docs, the paramater descriptions are buried in the XML. Fetch it.
let extractParamTip paramName (DataTipText elements) =
List.tryPick (extractParamTipFromElement paramName) elements

/// Formats tool-tip and turns the first line into heading
/// MonoDevelop does this automatically for completion data,
Expand Down

0 comments on commit 557d5c3

Please sign in to comment.