using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.ComponentModel.Composition;
using System.Runtime.InteropServices;
using Microsoft.VisualStudio.Language.Intellisense;
using Microsoft.VisualStudio.Text;
using Microsoft.VisualStudio.Text.Editor;
using Microsoft.VisualStudio.Utilities;
using Microsoft.VisualStudio.Editor;
using Microsoft.VisualStudio.Text.Operations;
using Microsoft.VisualStudio;
using Microsoft.VisualStudio.TextManager.Interop;
using Microsoft.VisualStudio.OLE.Interop;
public TestParameter(string documentation, Span locus, string name, ISignature signature)
{
Documentation = documentation;
Locus = locus;
Name = name;
Signature = signature;
}
Public Sub New(ByVal documentation As String, ByVal locus As Span, ByVal name As String, ByVal signature As ISignature)
Me.privateDocumentation = documentation
Me.privateLocus = locus
Me.privateName = name
Me.privateSignature = signature
End Sub
public string Documentation { get; private set; }
public Span Locus { get; private set; }
public string Name { get; private set; }
public ISignature Signature { get; private set; }
public Span PrettyPrintedLocus { get; private set; }
Private privateDocumentation As String
ReadOnly Property Documentation() As String Implements IParameter.Documentation
Get
Return privateDocumentation
End Get
End Property
Private privateLocus As Span
ReadOnly Property Locus() As Span Implements IParameter.Locus
Get
Return privateLocus
End Get
End Property
Private privateName As String
ReadOnly Property Name() As String Implements IParameter.Name
Get
Return privateName
End Get
End Property
Private privateSignature As ISignature
ReadOnly Property Signature() As ISignature Implements IParameter.Signature
Get
Return privateSignature
End Get
End Property
Private privatePrettyPrintedLocus As Span
ReadOnly Property PrettyPrintedLocus() As Span Implements IParameter.PrettyPrintedLocus
Get
Return privatePrettyPrintedLocus
End Get
End Property
Private m_subjectBuffer As ITextBuffer
Private m_currentParameter As IParameter
Private m_content As String
Private m_documentation As String
Friend m_applicableToSpan As ITrackingSpan
Friend m_parameters As ReadOnlyCollection(Of IParameter)
Private m_printContent As String
Private Sub RaiseCurrentParameterChanged(ByVal prevCurrentParameter As IParameter, ByVal newCurrentParameter As IParameter)
Dim tempHandler As EventHandler(Of CurrentParameterChangedEventArgs) = Me.CurrentParameterChangedEvent
If tempHandler IsNot Nothing Then
tempHandler(Me, New CurrentParameterChangedEventArgs(prevCurrentParameter, newCurrentParameter))
End If
End Sub
internal void ComputeCurrentParameter()
{
if (Parameters.Count == 0)
{
this.CurrentParameter = null;
return;
}
//the number of commas in the string is the index of the current parameter
string sigText = ApplicableToSpan.GetText(m_subjectBuffer.CurrentSnapshot);
int currentIndex = 0;
int commaCount = 0;
while (currentIndex < sigText.Length)
{
int commaIndex = sigText.IndexOf(',', currentIndex);
if (commaIndex == -1)
{
break;
}
commaCount++;
currentIndex = commaIndex + 1;
}
if (commaCount < Parameters.Count)
{
this.CurrentParameter = Parameters[commaCount];
}
else
{
//too many commas, so use the last parameter as the current one.
this.CurrentParameter = Parameters[Parameters.Count - 1];
}
}
Friend Sub ComputeCurrentParameter()
If Parameters.Count = 0 Then
Me.m_currentParameter = Nothing
Return
End If
'the number of commas in the string is the index of the current parameter
Dim sigText As String = ApplicableToSpan.GetText(m_subjectBuffer.CurrentSnapshot)
Dim currentIndex As Integer = 0
Dim commaCount As Integer = 0
Do While currentIndex < sigText.Length
Dim commaIndex As Integer = sigText.IndexOf(","c, currentIndex)
If commaIndex = -1 Then
Exit Do
End If
commaCount += 1
currentIndex = commaIndex + 1
Loop
If commaCount < Parameters.Count Then
Me.m_currentParameter = Parameters(commaCount)
Else
'too many commas, so use the last parameter as the current one.
Me.m_currentParameter = Parameters(Parameters.Count - 1)
End If
End Sub
public string Content
{
get { return (m_content); }
internal set { m_content = value; }
}
public string Documentation
{
get { return (m_documentation); }
internal set { m_documentation = value; }
}
public ReadOnlyCollection<IParameter> Parameters
{
get { return (m_parameters); }
internal set { m_parameters = value; }
}
public string PrettyPrintedContent
{
get { return (m_printContent); }
internal set { m_printContent = value; }
}
ReadOnly Property Content() As String Implements ISignature.Content
Get
Return (m_content)
End Get
End Property
ReadOnly Property Documentation() As String Implements ISignature.Documentation
Get
Return (m_documentation)
End Get
End Property
ReadOnly Property Parameters() As ReadOnlyCollection(Of IParameter) Implements ISignature.Parameters
Get
Return (m_parameters)
End Get
End Property
ReadOnly Property PrettyPrintedContent() As String Implements ISignature.PrettyPrintedContent
Get
Return (m_printContent)
End Get
End Property
public void AugmentSignatureHelpSession(ISignatureHelpSession session, IList<ISignature> signatures)
{
ITextSnapshot snapshot = m_textBuffer.CurrentSnapshot;
int position = session.GetTriggerPoint(m_textBuffer).GetPosition(snapshot);
ITrackingSpan applicableToSpan = m_textBuffer.CurrentSnapshot.CreateTrackingSpan(
new Span(position, 0), SpanTrackingMode.EdgeInclusive, 0);
signatures.Add(CreateSignature(m_textBuffer, "add(int firstInt, int secondInt)", "Documentation for adding integers.", applicableToSpan));
signatures.Add(CreateSignature(m_textBuffer, "add(double firstDouble, double secondDouble)", "Documentation for adding doubles.", applicableToSpan));
}
Public Sub AugmentSignatureHelpSession(ByVal session As ISignatureHelpSession, ByVal signatures As IList(Of ISignature)) Implements ISignatureHelpSource.AugmentSignatureHelpSession
Dim snapshot As ITextSnapshot = m_textBuffer.CurrentSnapshot
Dim position As Integer = session.GetTriggerPoint(m_textBuffer).GetPosition(snapshot)
Dim applicableToSpan As ITrackingSpan = m_textBuffer.CurrentSnapshot.CreateTrackingSpan(New Span(position, 0), SpanTrackingMode.EdgeInclusive, 0)
signatures.Add(CreateSignature(m_textBuffer, "add(int firstInt, int secondInt)", "Documentation for adding integers.", applicableToSpan))
signatures.Add(CreateSignature(m_textBuffer, "add(double firstDouble, double secondDouble)", "Documentation for adding doubles.", applicableToSpan))
End Sub
private TestSignature CreateSignature(ITextBuffer textBuffer, string methodSig, string methodDoc, ITrackingSpan span)
{
TestSignature sig = new TestSignature(textBuffer, methodSig, methodDoc, null);
textBuffer.Changed += new EventHandler<TextContentChangedEventArgs>(sig.OnSubjectBufferChanged);
//find the parameters in the method signature (expect methodname(one, two)
string[] pars = methodSig.Split(new char[] { '(', ',', ')' });
List<IParameter> paramList = new List<IParameter>();
int locusSearchStart = 0;
for (int i = 1; i < pars.Length; i++)
{
string param = pars[i].Trim();
if (string.IsNullOrEmpty(param))
continue;
//find where this parameter is located in the method signature
int locusStart = methodSig.IndexOf(param, locusSearchStart);
if (locusStart >= 0)
{
Span locus = new Span(locusStart, param.Length);
locusSearchStart = locusStart + param.Length;
paramList.Add(new TestParameter("Documentation for the parameter.", locus, param, sig));
}
}
sig.Parameters = new ReadOnlyCollection<IParameter>(paramList);
sig.ApplicableToSpan = span;
sig.ComputeCurrentParameter();
return sig;
}
Private Function CreateSignature(ByVal textBuffer As ITextBuffer, ByVal methodSig As String, ByVal methodDoc As String, ByVal span As ITrackingSpan) As TestSignature
Dim sig As New TestSignature(textBuffer, methodSig, methodDoc, Nothing)
AddHandler textBuffer.Changed, AddressOf sig.OnSubjectBufferChanged
'find the parameters in the method signature (expect methodname(one, two)
Dim pars() As String = methodSig.Split(New Char() {"("c, ","c, ")"c})
Dim paramList As New List(Of IParameter)()
Dim locusSearchStart As Integer = 0
For i As Integer = 1 To pars.Length - 1
Dim param As String = pars(i).Trim()
If String.IsNullOrEmpty(param) Then
Continue For
End If
'find where this parameter is located in the method signature
Dim locusStart As Integer = methodSig.IndexOf(param, locusSearchStart)
If locusStart >= 0 Then
Dim locus As New Span(locusStart, param.Length)
locusSearchStart = locusStart + param.Length
paramList.Add(New TestParameter("Documentation for the parameter.", locus, param, sig))
End If
Next i
sig.m_Parameters = New ReadOnlyCollection(Of IParameter)(paramList)
sig.m_ApplicableToSpan = span
sig.ComputeCurrentParameter()
Return sig
End Function
public ISignature GetBestMatch(ISignatureHelpSession session)
{
if (session.Signatures.Count > 0)
{
ITrackingSpan applicableToSpan = session.Signatures[0].ApplicableToSpan;
string text = applicableToSpan.GetText(applicableToSpan.TextBuffer.CurrentSnapshot);
if (text.Trim().Equals("add")) //get only "add"
return session.Signatures[0];
}
return null;
}
Public Function GetBestMatch(ByVal session As ISignatureHelpSession) As ISignature Implements ISignatureHelpSource.GetBestMatch
If session.Signatures.Count > 0 Then
Dim applicableToSpan As ITrackingSpan = session.Signatures(0).ApplicableToSpan
Dim text As String = applicableToSpan.GetText(applicableToSpan.TextBuffer.CurrentSnapshot)
If text.Trim().Equals("add") Then 'get only "add"
Return session.Signatures(0)
End If
End If
Return Nothing
End Function
private bool m_isDisposed;
public void Dispose()
{
if (!m_isDisposed)
{
GC.SuppressFinalize(this);
m_isDisposed = true;
}
}
Private m_isDisposed As Boolean
Public Sub Dispose() Implements IDisposable.Dispose
If Not m_isDisposed Then
GC.SuppressFinalize(Me)
m_isDisposed = True
End If
End Sub
public ISignatureHelpSource TryCreateSignatureHelpSource(ITextBuffer textBuffer)
{
return new TestSignatureHelpSource(textBuffer);
}
Public Function TryCreateSignatureHelpSource(ByVal textBuffer As ITextBuffer) As ISignatureHelpSource Implements ISignatureHelpSourceProvider.TryCreateSignatureHelpSource
Return New TestSignatureHelpSource(textBuffer)
End Function
Private m_nextCommandHandler As IOleCommandTarget
Private m_textView As ITextView
Private m_broker As ISignatureHelpBroker
Private m_session As ISignatureHelpSession
Private m_navigator As ITextStructureNavigator
internal TestSignatureHelpCommandHandler(IVsTextView textViewAdapter, ITextView textView, ITextStructureNavigator nav, ISignatureHelpBroker broker)
{
this.m_textView = textView;
this.m_broker = broker;
this.m_navigator = nav;
//add this to the filter chain
textViewAdapter.AddCommandFilter(this, out m_nextCommandHandler);
}
Friend Sub New(ByVal textViewAdapter As IVsTextView, ByVal textView As ITextView, ByVal nav As ITextStructureNavigator, ByVal broker As ISignatureHelpBroker)
Me.m_textView = textView
Me.m_broker = broker
Me.m_navigator = nav
'add this to the filter chain
textViewAdapter.AddCommandFilter(Me, m_nextCommandHandler)
End Sub
public int Exec(ref Guid pguidCmdGroup, uint nCmdID, uint nCmdexecopt, IntPtr pvaIn, IntPtr pvaOut)
{
char typedChar = char.MinValue;
if (pguidCmdGroup == VSConstants.VSStd2K && nCmdID == (uint)VSConstants.VSStd2KCmdID.TYPECHAR)
{
typedChar = (char)(ushort)Marshal.GetObjectForNativeVariant(pvaIn);
if (typedChar.Equals('('))
{
//move the point back so it's in the preceding word
SnapshotPoint point = m_textView.Caret.Position.BufferPosition - 1;
TextExtent extent = m_navigator.GetExtentOfWord(point);
string word = extent.Span.GetText();
if (word.Equals("add"))
m_session = m_broker.TriggerSignatureHelp(m_textView);
}
else if (typedChar.Equals(')') && m_session != null)
{
m_session.Dismiss();
m_session = null;
}
}
return m_nextCommandHandler.Exec(ref pguidCmdGroup, nCmdID, nCmdexecopt, pvaIn, pvaOut);
}
Public Function Exec(ByRef pguidCmdGroup As Guid, ByVal nCmdID As UInteger, ByVal nCmdexecopt As UInteger, ByVal pvaIn As IntPtr, ByVal pvaOut As IntPtr) As Integer Implements IOleCommandTarget.Exec
Dim typedChar As Char = Char.MinValue
If pguidCmdGroup = VSConstants.VSStd2K AndAlso nCmdID = CUInt(VSConstants.VSStd2KCmdID.TYPECHAR) Then
typedChar = CChar(ChrW(CUShort(Marshal.GetObjectForNativeVariant(pvaIn))))
If typedChar.Equals("("c) Then
'move the point back so it's in the preceding word
Dim point As SnapshotPoint = m_textView.Caret.Position.BufferPosition - 1
Dim extent As TextExtent = m_navigator.GetExtentOfWord(point)
Dim word As String = extent.Span.GetText()
If word.Equals("add") Then
m_session = m_broker.TriggerSignatureHelp(m_textView)
End If
ElseIf typedChar.Equals(")"c) AndAlso m_session IsNot Nothing Then
m_session.Dismiss()
m_session = Nothing
End If
End If
Return m_nextCommandHandler.Exec(pguidCmdGroup, nCmdID, nCmdexecopt, pvaIn, pvaOut)
End Function
public int QueryStatus(ref Guid pguidCmdGroup, uint cCmds, OLECMD[] prgCmds, IntPtr pCmdText)
{
return m_nextCommandHandler.QueryStatus(ref pguidCmdGroup, cCmds, prgCmds, pCmdText);
}
Public Function QueryStatus(ByRef pguidCmdGroup As Guid, ByVal cCmds As UInteger, ByVal prgCmds() As OLECMD, ByVal pCmdText As IntPtr) As Integer Implements IOleCommandTarget.QueryStatus
Return m_nextCommandHandler.QueryStatus(pguidCmdGroup, cCmds, prgCmds, pCmdText)
End Function
public void VsTextViewCreated(IVsTextView textViewAdapter)
{
ITextView textView = AdapterService.GetWpfTextView(textViewAdapter);
if (textView == null)
return;
textView.Properties.GetOrCreateSingletonProperty(
() => new TestSignatureHelpCommandHandler(textViewAdapter,
textView,
NavigatorService.GetTextStructureNavigator(textView.TextBuffer),
SignatureHelpBroker));
}
Public Sub VsTextViewCreated(ByVal textViewAdapter As IVsTextView) Implements IVsTextViewCreationListener.VsTextViewCreated
Dim textView As ITextView = AdapterService.GetWpfTextView(textViewAdapter)
If textView Is Nothing Then
Return
End If
textView.Properties.GetOrCreateSingletonProperty(Function() New TestSignatureHelpCommandHandler(textViewAdapter, textView, NavigatorService.GetTextStructureNavigator(textView.TextBuffer), SignatureHelpBroker))
End Sub