in vsintegration/src/FSharp.LanguageService.Base/Source.cs [2374:2481]
public void Refresh(MethodTipMiscellany_DEPRECATED methodTipMiscellany)
{
var wpfTextView = FSharpSourceBase_DEPRECATED.GetWpfTextViewFromVsTextView(textView);
var ranges = methods.GetParameterRanges();
Debug.Assert(ranges != null && ranges.Length > 0);
// Don't do anything for open parens and commas that aren't intrinsic to a method call
if (!this.displayed && methodTipMiscellany == MethodTipMiscellany_DEPRECATED.JustPressedCloseParen)
{
return; // close paren must never cause it to appear
}
if (!this.displayed && methodTipMiscellany == MethodTipMiscellany_DEPRECATED.JustPressedOpenParen)
{
// the only good open paren is the start of the first param, don't want to cause a tip to be displayed $here$: foo(x, $($y+z), w)
if (!ranges[0].GetSpan(wpfTextView.TextSnapshot).Start.Equals(wpfTextView.Caret.Position.BufferPosition))
return;
}
if (!this.displayed && methodTipMiscellany == MethodTipMiscellany_DEPRECATED.JustPressedComma)
{
// the only good commas will be, interestingly, at the start of the following param span
// this by virtue of fact that we assume comma is one character after where the previous parameter range ends (and thus the first character of the next range)
var ok = false;
for (int i = 1; i < ranges.Length; ++i)
{
if (ranges[i].GetSpan(wpfTextView.TextSnapshot).Start.Equals(wpfTextView.Caret.Position.BufferPosition))
{
ok = true;
break;
}
}
if (!ok)
return;
}
// Figure out which parameter we are in based on location of caret within the parse
var caretPoint = wpfTextView.Caret.Position.BufferPosition;
var endOfLinePoint = wpfTextView.Caret.ContainingTextViewLine.End;
bool caretIsAtEndOfLine = caretPoint == endOfLinePoint;
this.currentParameter = -1;
for (int i = ranges.Length - 1; i >= 0; --i) // need to traverse backwards, so that "f(1," at end of line sets curParam to 1, not 0
{
if (ranges[i].GetSpan(wpfTextView.TextSnapshot).Contains(caretPoint)
// ranges are half-open [...), so end-of-line is a special case...
|| caretIsAtEndOfLine
&& ranges[i].GetSpan(wpfTextView.TextSnapshot).End == caretPoint
&& !(i == ranges.Length - 1 && this.methods.IsThereACloseParen())) // ...except on the closing paren (when caret at close paren, should dismiss tip, even if at EOL)
{
this.currentParameter = i;
break;
}
}
if (this.currentParameter == -1)
{
// a bit of a kludge; if they just backspaced over the last comma and there's no close parenthesis, the caret is just to the right of all the param
// ranges, but we don't want to dismiss the tip. so look just left of the caret and see if that would be inside the final param
if (methodTipMiscellany == MethodTipMiscellany_DEPRECATED.JustPressedBackspace
&& ranges[ranges.Length - 1].GetSpan(wpfTextView.TextSnapshot).Contains(wpfTextView.Caret.Position.BufferPosition.Subtract(1)))
{
this.currentParameter = ranges.Length - 1;
}
else
{
// the caret moved left of the open parenthesis or right of the close parenthesis
this.currentParameter = 0;
this.Dismiss();
}
}
// possibly select a method from overload list based on current num of params the user has
if (methodTipMiscellany == MethodTipMiscellany_DEPRECATED.ExplicitlyInvokedViaCtrlShiftSpace
|| methodTipMiscellany == MethodTipMiscellany_DEPRECATED.JustPressedComma)
{
Debug.Assert(this.methods != null, "this can happen if we just called Dismiss() because we erroneously decided the caret moves outside the parens");
var actualParamNames = System.Linq.Enumerable.ToList(System.Linq.Enumerable.Where(this.methods.GetParameterNames(), s => null != s));
int numOfParamsUserHasSoFar = methods.GetNoteworthyParamInfoLocations().Length - 3; // -3 because first 3 ranges are [LID.start, LID.end, Paren], rest are params
// however note that this is never zero, "foo(" and "foo(x" both report 1
int curMeth = this.currentMethod; // start wherever the user already is. the methods are already ordered in increasing order-of-params; only can increase of user has longer param list.
while (curMeth < this.methods.GetCount()
&& this.methods.GetParameterCount(curMeth) < numOfParamsUserHasSoFar
// if we're on a zero-arg overload, don't go past it when user has one arg; be like C#, good for e.g. Console.WriteLine(
&& !(numOfParamsUserHasSoFar == 1 && this.methods.GetParameterCount(curMeth) == 0))
{
curMeth++;
}
if (curMeth == this.methods.GetCount())
{
// if they have 'too many' parameters, always just start at the beginning of the list
curMeth = 0;
}
else if (actualParamNames.Count > 0)
{
// there are named parameters, additionally ensure the selected overload has those names, or keep advancing until it does
while (curMeth < this.methods.GetCount()
&& !FormalParamNames(this.methods, curMeth).IsSupersetOf(actualParamNames))
{
curMeth++;
}
}
this.currentMethod = curMeth;
}
else
{
// in other cases, don't update which overload we're on, the user is in control
}
this.UpdateView();
}