Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
.vs/
bin/
obj/
1 change: 1 addition & 0 deletions CppTripleSlash.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -102,6 +102,7 @@
</COMReference>
</ItemGroup>
<ItemGroup>
<Compile Include="Function.cs" />
<Compile Include="Guids.cs" />
<Compile Include="CppTripleSlashPackage.cs" />
<Compile Include="Properties\AssemblyInfo.cs" />
Expand Down
202 changes: 202 additions & 0 deletions Function.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,202 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Text.RegularExpressions;

namespace CppTripleSlash
{
public static class StringExtensions
{
public static string SuperTrim(this string str)
{
var sb = new StringBuilder(str.Length);
var space = false;
for (var i = 0; i < str.Length; i++)
{
if (!space && str[i] == ' ')
{
space = true;
sb.Append(' ');
}
else
{
space = false;
sb.Append(str[i]);
}
}
return sb.ToString().Trim();
}

public static string ReplaceWord(this string str, string pattern, string replacement)
{
return Regex.Replace(str, "\\b" + pattern + "\\b", replacement);
}
}

class Function
{
class ParseException : Exception
{
public ParseException(string message) : base(message) { }
}

public string ReturnType;
public List<string> Arguments = new List<string>();

private static string GetArgumentName(string arg)
{
if (arg == "...") //variadic
{
return arg;
}
if (arg.Contains("=")) //default value => "int n = 42"
{
int eqIndex = arg.IndexOf('=');
return GetArgumentName(arg.Substring(0, eqIndex));
}
Match match = Regex.Match(arg, @"\( *\* *([a-zA-Z0-9_]+) *\)");
if (match.Success) //function parameter => "int (*name)(...)"
{
return match.Groups[1].Value;
}
//char name[size]
var blockOpen = arg.IndexOf('[');
if (blockOpen != -1)
arg = arg.Substring(0, blockOpen);
//normal argument
arg = arg
.Replace('&', ' ')
.Replace('*', ' ')
.ReplaceWord("const", "")
.ReplaceWord("volatile", "")
.SuperTrim()
//http://en.cppreference.com/w/cpp/language/types
.ReplaceWord("unsigned long long int", "int")
.ReplaceWord("signed long long int", "int")
.ReplaceWord("unsigned long long", "int")
.ReplaceWord("unsigned short int", "int")
.ReplaceWord("unsigned long int", "int")
.ReplaceWord("signed long long", "int")
.ReplaceWord("signed short int", "int")
.ReplaceWord("signed long int", "int")
.ReplaceWord("unsigned short", "int")
.ReplaceWord("unsigned long", "int")
.ReplaceWord("long long int", "int")
.ReplaceWord("unsigned int", "int")
.ReplaceWord("signed short", "int")
.ReplaceWord("signed long", "int")
.ReplaceWord("long double", "int")
.ReplaceWord("signed int", "int")
.ReplaceWord("short int", "int")
.ReplaceWord("long long", "int")
.ReplaceWord("long int", "int")
.ReplaceWord("unsigned", "int")
.ReplaceWord("signed", "int")
.ReplaceWord("short", "int")
.ReplaceWord("long", "int");
int lastSpace = arg.LastIndexOf(' ');
if (lastSpace == -1)
{
return "";
}
return arg.Substring(lastSpace + 1).SuperTrim();
}

private static IEnumerable<string> SplitArgs(string args)
{
List<string> result = new List<string>();
int parenDepth = 0;
int templateDepth = 0;
StringBuilder sb = new StringBuilder();
foreach (char ch in args)
{
switch (ch)
{
case '<':
templateDepth++;
break;
case '>':
templateDepth--;
if (templateDepth == 0)
{
sb.Append(' ');
}
break;
case '(':
parenDepth++;
if (templateDepth == 0)
{
sb.Append(ch);
}
break;
case ')':
parenDepth--;
if (templateDepth == 0)
{
sb.Append(ch);
}
break;
default:
if (parenDepth == 0 && templateDepth == 0 && ch == ',')
{
result.Add(sb.ToString());
sb.Clear();
}
else if (templateDepth == 0)
{
sb.Append(ch);
}
break;
}
}
result.Add(sb.ToString());
return result.Where(s => !string.IsNullOrEmpty(s)).Select(s => s.SuperTrim());
}

public void Parse(string decl)
{
ReturnType = null;
Arguments.Clear();
if (string.IsNullOrEmpty(decl))
{
throw new ParseException("NullOrEmpty");
}
decl = decl.SuperTrim();
if (!decl.EndsWith(";"))
{
throw new ParseException("NotEndsWithSemicolon");
}
int firstParen = decl.IndexOf('(');
if (firstParen == -1)
{
throw new ParseException("NoFirstParen");
}
int lastParen = decl.LastIndexOf(')');
if (lastParen == -1)
{
throw new ParseException("NoLastParen");
}
string nameReturnType = decl.Substring(0, firstParen).SuperTrim();
int lastSpace = nameReturnType.LastIndexOf(' ');
string args = decl.Substring(firstParen + 1, lastParen - firstParen - 1).SuperTrim();
foreach (string arg in SplitArgs(args))
{
Arguments.Add(GetArgumentName(arg).SuperTrim());
}
ReturnType = nameReturnType.Substring(0, lastSpace).Replace("static", "").SuperTrim();
}

public override string ToString()
{
var sb = new StringBuilder(ReturnType + "(");
for (var i = 0; i < Arguments.Count; i++)
{
if (i > 0)
sb.Append(',');
sb.Append($"\"{Arguments[i]}\"");
}
return sb.ToString() + ")";
}
}
}
74 changes: 44 additions & 30 deletions TripleSlashCompletionCommandHandler.cs
Original file line number Diff line number Diff line change
Expand Up @@ -68,59 +68,69 @@ public int Exec(ref Guid pguidCmdGroup, uint nCmdID, uint nCmdexecopt, IntPtr pv
// check for the triple slash
if (typedChar == '/' && m_dte != null)
{
string currentLine = m_textView.TextSnapshot.GetLineFromPosition(
m_textView.Caret.Position.BufferPosition.Position).GetText();
string currentLine = GetCurrentLine();
if ((currentLine + "/").Trim() == "///")
{
// Calculate how many spaces
string spaces = currentLine.Replace(currentLine.TrimStart(), "");
string prefix = currentLine.Replace(currentLine.TrimStart(), "") + "/// ";
TextSelection ts = m_dte.ActiveDocument.Selection as TextSelection;
int oldLine = ts.ActivePoint.Line;
int oldOffset = ts.ActivePoint.LineCharOffset;
ts.LineDown();
ts.EndOfLine();

// Try to retrieve the CodeElement
CodeElement codeElement = null;
FileCodeModel fcm = m_dte.ActiveDocument.ProjectItem.FileCodeModel;
if (fcm != null)
{
codeElement = fcm.CodeElementFromPoint(ts.ActivePoint, vsCMElement.vsCMElementFunction);
}

if (codeElement != null && codeElement is CodeFunction)
// Process the function
Function function = new Function();
if (codeElement is CodeFunction codeFunction)
{
CodeFunction function = codeElement as CodeFunction;
StringBuilder sb = new StringBuilder("/ <summary>\r\n" + spaces + "/// \r\n" + spaces + "/// </summary>");
foreach (CodeElement child in codeElement.Children)
{
CodeParameter parameter = child as CodeParameter;
if (parameter != null)
if (child is CodeParameter parameter)
{
sb.AppendFormat("\r\n" + spaces + "/// <param name=\"{0}\"></param>", parameter.Name);
function.Arguments.Add(parameter.Name);
}
}

if (function.Type.AsString != "void")
function.ReturnType = codeFunction.Type.AsString;
}
else
{
try
{
function.Parse(GetCurrentLine());
}
catch
{
sb.AppendFormat("\r\n" + spaces + "/// <returns></returns>");
}

ts.MoveToLineAndOffset(oldLine, oldOffset);
ts.Insert(sb.ToString());
ts.MoveToLineAndOffset(oldLine, oldOffset);
ts.LineDown();
ts.EndOfLine();
return VSConstants.S_OK;
}
else

// Add the XML comments to the file
StringBuilder sb = new StringBuilder($"/ <summary>\r\n{prefix}\r\n{prefix}</summary>");
if(!string.IsNullOrEmpty(function.ReturnType))
{
ts.MoveToLineAndOffset(oldLine, oldOffset);
ts.Insert("/ <summary>\r\n" + spaces + "/// \r\n" + spaces + "/// </summary>");
ts.MoveToLineAndOffset(oldLine, oldOffset);
ts.LineDown();
ts.EndOfLine();
return VSConstants.S_OK;
foreach (string argument in function.Arguments)
{
sb.Append($"\r\n{prefix}<param name=\"{argument}\"></param>");
}
if (function.ReturnType != "void")
{
sb.AppendFormat($"\r\n{prefix}<returns></returns>");
}
}

ts.MoveToLineAndOffset(oldLine, oldOffset);
ts.Insert(sb.ToString());
ts.MoveToLineAndOffset(oldLine, oldOffset);
ts.LineDown();
ts.EndOfLine();
return VSConstants.S_OK;
}
}

Expand Down Expand Up @@ -215,8 +225,7 @@ public int Exec(ref Guid pguidCmdGroup, uint nCmdID, uint nCmdexecopt, IntPtr pv
{
if (pguidCmdGroup == VSConstants.VSStd2K && nCmdID == (uint)VSConstants.VSStd2KCmdID.RETURN)
{
string currentLine = m_textView.TextSnapshot.GetLineFromPosition(
m_textView.Caret.Position.BufferPosition.Position).GetText();
string currentLine = GetCurrentLine();
if (currentLine.TrimStart().StartsWith("///"))
{
TextSelection ts = m_dte.ActiveDocument.Selection as TextSelection;
Expand All @@ -231,8 +240,7 @@ public int Exec(ref Guid pguidCmdGroup, uint nCmdID, uint nCmdexecopt, IntPtr pv
int retVal = m_nextCommandHandler.Exec(ref pguidCmdGroup, nCmdID, nCmdexecopt, pvaIn, pvaOut);
if (typedChar == '<')
{
string currentLine = m_textView.TextSnapshot.GetLineFromPosition(
m_textView.Caret.Position.BufferPosition.Position).GetText();
string currentLine = GetCurrentLine();
if (currentLine.TrimStart().StartsWith("///"))
{
if (m_session == null || m_session.IsDismissed) // If there is no active session, bring up completion
Expand Down Expand Up @@ -311,5 +319,11 @@ private void OnSessionDismissed(object sender, EventArgs e)
m_session = null;
}
}

private string GetCurrentLine()
{
return m_textView.TextSnapshot.GetLineFromPosition(
m_textView.Caret.Position.BufferPosition.Position).GetText();
}
}
}