/// ------------------------------------------------------------------------------------ /// <summary> /// Moves the up before comment. /// </summary> /// <param name="sel">The sel.</param> /// ------------------------------------------------------------------------------------ private void MoveUpBeforeComment(TextSelection sel) { TextRanges textRanges = null; for (; true;) { if (sel.FindPattern("\\<summary\\>|/// ---", (int)(vsFindOptions.vsFindOptionsBackwards | vsFindOptions.vsFindOptionsRegularExpression), ref textRanges)) { // GhostDoc messes up dashed lines from inherited comments from base class, // so delete those if (sel.Text.StartsWith("/// ---")) { sel.EndOfLine(true); sel.WordRight(true, 1); sel.Delete(1); } else if (sel.Text.StartsWith("<summary>")) { while (true) { sel.MoveToLineAndOffset(sel.ActivePoint.Line - 1, 1, false); sel.StartOfLine(vsStartOfLineOptions.vsStartOfLineOptionsFirstText, false); sel.EndOfLine(true); if (!sel.Text.StartsWith("///")) { if (sel.Text.Length > 0) { // there is a non-empty comment line. We want to start at the end // of it sel.EndOfLine(false); } else { sel.StartOfLine(vsStartOfLineOptions.vsStartOfLineOptionsFirstText, false); } break; } // GhostDoc messes up dashed lines from inherited comments from base class, // so delete those if (sel.Text.StartsWith("/// -----")) { sel.WordRight(true, 1); sel.Delete(1); } } return; } } else { return; } } }
private void JoinLine(TextSelection textSelection) { // If the selection has no length, try to pick up the next line. if (textSelection.IsEmpty) { textSelection.LineDown(true); textSelection.EndOfLine(true); } const string fluentPattern = @"[ \t]*\r?\n[ \t]*\."; const string pattern = @"[ \t]*\r?\n[ \t]*"; var selection = textSelection.Text; // do regex replace for fluent style selection = Regex.Replace(selection, fluentPattern, "."); // do regex replace for everything else selection = Regex.Replace(selection, pattern, " "); textSelection.Text = selection; // Move the cursor forward, clearing the selection. textSelection.CharRight(); }
/// <summary>Implements the Exec method of the IDTCommandTarget interface. This is called when the command is invoked.</summary> /// <param term='commandName'>The name of the command to execute.</param> /// <param term='executeOption'>Describes how the command should be run.</param> /// <param term='varIn'>Parameters passed from the caller to the command handler.</param> /// <param term='varOut'>Parameters passed from the command handler to the caller.</param> /// <param term='handled'>Informs the caller if the command was handled or not.</param> /// <seealso class='Exec' /> public void Exec(string commandName, vsCommandExecOption executeOption, ref object varIn, ref object varOut, ref bool handled) { handled = false; if (executeOption == vsCommandExecOption.vsCommandExecOptionDoDefault) { if (commandName == "FormatVariableDefine.Connect.FormatVariableDefine" || commandName == "FormatVariableDefine.Connect.FormatVariableDefineRightClick") { TextSelection select = ((TextSelection)_applicationObject.ActiveDocument.Selection); int nTopLine = select.TopLine; int nBottomLine = select.BottomLine; bool bLastLineEmpty = select.BottomPoint.AtStartOfLine; select.GotoLine(nTopLine, true); select.LineDown(true, nBottomLine - nTopLine); select.EndOfLine(true); if (bLastLineEmpty) { select.StartOfLine(vsStartOfLineOptions.vsStartOfLineOptionsFirstColumn, true); } string selectedCode = select.Text; string outCode = CodeSmart.AlignText(selectedCode); //对齐选中文本 select.Insert(outCode, (int)vsInsertFlags.vsInsertFlagsCollapseToEnd); handled = true; return; } } }
/// <summary> /// 選択中の行を行選択状態にします。 /// </summary> private static void SelectLines(TextSelection selection) { var bottom = selection.BottomLine; selection.MoveToDisplayColumn(selection.TopLine, 1, false); selection.MoveToDisplayColumn(bottom, 0, true); selection.EndOfLine(true); }
private string GetLineText(TextSelection objTextSelection) { try { VirtualPoint objActive = objTextSelection.ActivePoint; objTextSelection.StartOfLine((EnvDTE.vsStartOfLineOptions)(0), true); string text = objTextSelection.Text; objTextSelection.EndOfLine(true); var result = text + objTextSelection.Text; objTextSelection.EndOfLine(false); return(result); } catch (Exception ex) { ShowMessageBox("GetLineText()", ex.Message); } return(string.Empty); }
/// <summary> /// Joins the text within the specified text selection. /// </summary> /// <param name="textSelection">The text selection.</param> private void JoinText(TextSelection textSelection) { // If the selection has no length, try to pick up the next line. if (textSelection.IsEmpty) { textSelection.LineDown(true); textSelection.EndOfLine(true); } const string pattern = @"[ \t]*\r?\n[ \t]*"; const string replacement = @" "; // Substitute all new lines (and optional surrounding whitespace) with a single space. TextDocumentHelper.SubstituteAllStringMatches(textSelection, pattern, replacement); // Move the cursor forward, clearing the selection. textSelection.CharRight(); }
/// <summary> /// 选择当前行 /// </summary> public void SelectLine() { try { // Retrieve document selection TextSelection sel = (TextSelection)_dte.ActiveWindow.Document.Selection; // Move to line sel.MoveToDisplayColumn(sel.CurrentLine, 1, false); // Select from start to end sel.StartOfLine(vsStartOfLineOptions.vsStartOfLineOptionsFirstText, false); sel.EndOfLine(true); } catch (ArgumentException) { } }
/// <summary> /// 智能分号: /// * 如果在一行的中间任何位置按下分号,则跳到行结尾添加分号,如果行未已经有一个分号则不重复添加 /// * 再按一次分号,则删除在行尾添加的分号,回到刚才的位置插入一个分号 /// </summary> /// <param name="keypress"></param> /// <param name="selection"></param> /// <param name="inStatementCompletion"></param> /// <param name="cancelKeypress"></param> public bool BeforeKeyPress(string keypress, TextSelection selection, bool inStatementCompletion, ref bool cancelKeypress) { if (!selection.IsEmpty || keypress != ";") { _smartSemicolonFallback = false; return(false); } if (_smartSemicolonFallback && selection.CurrentLine == _smartSemicolonLine && selection.ActivePoint.AtEndOfLine && selection.ActivePoint.CreateEditPoint().GetText(-1) == ";") { // 重复输入分号, 删除原来行尾的分号,并退回到原位置插入分号 if (_smartSemicolonDeleteLineEnd) { selection.DeleteLeft(1); } selection.MoveTo(_smartSemicolonLine, _smartSemicolonColumn, false); _smartSemicolonFallback = false; cancelKeypress = false; } else { // 智能分号,记录位置并移动到行尾插入分号 _smartSemicolonFallback = true; _smartSemicolonLine = selection.ActivePoint.Line; _smartSemicolonColumn = selection.ActivePoint.DisplayColumn; selection.EndOfLine(); var caret = selection.ActivePoint.CreateEditPoint(); if (caret.GetText(-1) == ";") { cancelKeypress = true; _smartSemicolonDeleteLineEnd = false; } else { cancelKeypress = false; _smartSemicolonDeleteLineEnd = true; } } return(true); }
/// ------------------------------------------------------------------------------------ /// <summary> /// Moves the down after comment. /// </summary> /// <param name="sel">The sel.</param> /// ------------------------------------------------------------------------------------ private void MoveDownAfterComment(TextSelection sel) { // Go to the beginning of the line and move down until we find a line that doesn't // start with ///. while (true) { sel.StartOfLine(vsStartOfLineOptions.vsStartOfLineOptionsFirstText, false); sel.EndOfLine(true); if (!sel.Text.StartsWith("///")) { break; } // GhostDoc messes up dashed lines from inherited comments from base class, // so delete those if (sel.Text.StartsWith("/// -----")) { sel.WordRight(true, 1); sel.Delete(1); sel.LineUp(false, 1); } sel.MoveToLineAndOffset(sel.ActivePoint.Line + 1, 1, false); } }
private void UpdateComment(CodeFunction function, string oldDoc, int docLine, TextSelection ts, int oldLine, int offset, string lineEnding) { ThreadHelper.ThrowIfNotOnUIThread(); // Get the old parameters and return valus var _oldParameters = GetDocLinesOfType(lineEnding, oldDoc, DocLineType._params); var oldParameters = _oldParameters.Keys.Except(new[] { "__default__" }).ToList(); var oldReturns = GetDocLinesOfType(lineEnding, oldDoc, DocLineType._return); string oldReturn = null; if (oldReturns.ContainsKey("return")) { oldReturn = oldReturns["return"]; } // Get all new function parameters and return value var updatedParams = GetFunctionParams(function); var updatedType = function.Type.AsString; // Create new comment step by step... string newDoc = oldDoc; // Remove all old parameters foreach (var param in oldParameters) { // Delete the line(s) var pattern = Regex.Escape(_oldParameters[param]) + "( .*" + lineEnding + "|" + lineEnding + ")"; newDoc = Regex.Replace(newDoc, pattern, ""); } // Remove old return statement if (oldReturn != null) { // Delete the line(s) var pattern = Regex.Escape(oldReturns["return"]) + "( .*" + lineEnding + "|" + lineEnding + ")"; newDoc = Regex.Replace(newDoc, pattern, ""); } // Then add all parameters of the new function var _oldParams = oldParameters.ToList(); var _uncertainNewParams = new List <string>(); var newParams = new Dictionary <string, string>(); foreach (var param in updatedParams) { // If there was the exact same parameter, add it again if (_oldParams.Contains(param)) { _oldParams.Remove(param); newParams.Add(param, _oldParameters[param]); } else { newParams.Add(param, null); _uncertainNewParams.Add(param); } } // Check for special cases if (_uncertainNewParams.Count > 0 && _oldParams.Count == 0) { _uncertainNewParams.Clear(); } // If there are no special cases, but still open issues, ask the user else if (_uncertainNewParams.Count > 0) { var dialog = new UpdateDoxygenCommentDialog(_oldParams, _uncertainNewParams); if (dialog.ShowDialog() == DialogResult.OK) { // Add the selected parameters to the new parameter list var selectedParams = dialog.finalParams; foreach (var param in _uncertainNewParams) { var oldParam = selectedParams[param]; if (oldParam != null) { newParams[param] = _oldParameters[oldParam].Replace(oldParam, param); } } } } // Add the new params to the documentation foreach (var param in newParams) { newDoc = AddParamToDoc(lineEnding, newDoc, param.Key, param.Value, _oldParameters); } // Then add the return statement if (!updatedType.Equals("void")) { newDoc = AddReturnToDoc(lineEnding, newDoc, oldReturn, oldReturns); } var oldDocLines = oldDoc.Split('\n'); var lineDiff = newDoc.Split('\n').Length - oldDocLines.Length; ts.MoveToLineAndOffset(docLine + 1, 1); ts.MoveToLineAndOffset(docLine + oldDocLines.Length - 1, 1, true); ts.EndOfLine(true); ts.Insert(newDoc.TrimSuffix(lineEnding)); ts.MoveToLineAndOffset(oldLine + lineDiff, offset); }
private bool CheckIfInDocumentetFunction(TextSelection ts, int currentLine, int currentOffset, out CodeFunction codeFunction, out int functionLine) { ThreadHelper.ThrowIfNotOnUIThread(); // Get comment beginnings string commentFormat = m_settings.FunctionFormat; string[] formatLines = commentFormat.Split(new[] { '\n' }, StringSplitOptions.RemoveEmptyEntries); string commentStart = formatLines.First().Trim().Split(' ').First().Trim(); string commentMiddle = formatLines[1].Trim().Split(' ').First().Trim(); string commentEnd = formatLines.Last().Trim().Split(' ').First().Trim(); // Check if in comment bool isComment = false; functionLine = currentLine; string line = m_textView.TextSnapshot.GetLineFromPosition(m_textView.Caret.Position.BufferPosition.Position).GetText().Trim(); if (line.StartsWith(commentStart) || line.StartsWith(commentMiddle) || line.StartsWith(commentEnd)) { while (!ts.ActivePoint.AtEndOfDocument) { // Get the line of the curser string _line = m_textView.TextSnapshot.GetLineFromLineNumber(ts.ActivePoint.Line - 1).GetText(); // Check if the comment ends if (_line.Contains(commentEnd)) { functionLine = ts.ActivePoint.Line + 1; isComment = true; break; } // If the end of the comment was not there, go one line down ts.LineDown(); } // If there is a comment start, but no correct ending, this is not a doxygen documentation if (functionLine == currentLine) { codeFunction = null; return(false); } } codeFunction = null; CodeElement codeElement = null; FileCodeModel fcm = m_dte.ActiveDocument.ProjectItem.FileCodeModel; if (fcm != null) { // Move to end of the current line ts.MoveToLineAndOffset(functionLine, 1); ts.EndOfLine(); // Check if there is a function for (int lineNumber = functionLine; lineNumber <= functionLine + 3; lineNumber++) { codeElement = fcm.CodeElementFromPoint(ts.ActivePoint, vsCMElement.vsCMElementFunction); if (codeElement != null && codeElement.Kind == vsCMElement.vsCMElementFunction) { functionLine = lineNumber; break; } // Only search in the next line if the cursor was in the documentation if (!isComment) { break; } string _line = m_textView.TextSnapshot.GetLineFromLineNumber(lineNumber - 1).GetText().Trim(); // If there was an empty line, check next one if (_line.Length == 0) { ts.LineDown(); ts.EndOfLine(); } // Otherwise the comment is not for a function else { return(false); } } } bool isFunction = codeElement != null && codeElement.Kind == vsCMElement.vsCMElementFunction; if (isFunction) { codeFunction = codeElement as CodeFunction; return(codeFunction != null); } // Return if the line containes a function or not return(isFunction); }
/// ------------------------------------------------------------------------------------ /// <summary> /// Moves the up before comment. /// </summary> /// <param name="sel">The sel.</param> /// ------------------------------------------------------------------------------------ private void MoveUpBeforeComment(TextSelection sel) { TextRanges textRanges = null; for (; true; ) { if (sel.FindPattern("\\<summary\\>|/// ---", (int)(vsFindOptions.vsFindOptionsBackwards | vsFindOptions.vsFindOptionsRegularExpression), ref textRanges)) { // GhostDoc messes up dashed lines from inherited comments from base class, // so delete those if (sel.Text.StartsWith("/// ---")) { sel.EndOfLine(true); sel.WordRight(true, 1); sel.Delete(1); } else if (sel.Text.StartsWith("<summary>")) { while (true) { sel.MoveToLineAndOffset(sel.ActivePoint.Line - 1, 1, false); sel.StartOfLine(vsStartOfLineOptions.vsStartOfLineOptionsFirstText, false); sel.EndOfLine(true); if (!sel.Text.StartsWith("///")) { if (sel.Text.Length > 0) { // there is a non-empty comment line. We want to start at the end // of it sel.EndOfLine(false); } else sel.StartOfLine(vsStartOfLineOptions.vsStartOfLineOptionsFirstText, false); break; } // GhostDoc messes up dashed lines from inherited comments from base class, // so delete those if (sel.Text.StartsWith("/// -----")) { sel.WordRight(true, 1); sel.Delete(1); } } return; } } else return; } }
/// ------------------------------------------------------------------------------------ /// <summary> /// Moves the down after comment. /// </summary> /// <param name="sel">The sel.</param> /// ------------------------------------------------------------------------------------ private void MoveDownAfterComment(TextSelection sel) { // Go to the beginning of the line and move down until we find a line that doesn't // start with ///. while (true) { sel.StartOfLine(vsStartOfLineOptions.vsStartOfLineOptionsFirstText, false); sel.EndOfLine(true); if (!sel.Text.StartsWith("///")) break; // GhostDoc messes up dashed lines from inherited comments from base class, // so delete those if (sel.Text.StartsWith("/// -----")) { sel.WordRight(true, 1); sel.Delete(1); sel.LineUp(false, 1); } sel.MoveToLineAndOffset(sel.ActivePoint.Line + 1, 1, false); } }
/// <summary> /// This function is the callback used to execute the command when the menu item is clicked. /// See the constructor to see how the menu item is associated with this function using /// OleMenuCommandService service and MenuCommand class. /// </summary> /// <param name="sender">Event sender.</param> /// <param name="e">Event args.</param> private void Execute(object sender, EventArgs e) { ThreadHelper.ThrowIfNotOnUIThread(); var dte = Package.GetGlobalService(typeof(DTE)) as DTE; if (dte == null || dte.ActiveDocument == null) { return; } TextSelection ts = dte.ActiveDocument.Selection as TextSelection; // Check if we're at the beginning of the document and should generate a file comment. if (ts.ActivePoint.Line == 1) { string fileComment = m_generator.GenerateFileComment(dte, out int selectedLine); ts.DeleteLeft(2); // Removing the // part here. ts.Insert(fileComment); // Move the caret. ts.MoveToLineAndOffset(selectedLine + 1, 1); ts.EndOfLine(); return; } ts.EndOfLine(); // Scroll down until we find a non-comment line. if (!ScrollToCodeStart(ts)) { return; } // Save the position so that we know where to place the comment. ts.StartOfLine(); var funcPoint = ts.ActivePoint.CreateEditPoint(); int oldLine = ts.ActivePoint.Line; int oldOffset = ts.ActivePoint.LineCharOffset; ts.EndOfLine(); // Determine indentation level. string currentLine = ts.ActivePoint.CreateEditPoint().GetLines(ts.ActivePoint.Line, ts.ActivePoint.Line + 1); string spaces = currentLine.Replace(currentLine.TrimStart(), ""); // Search for the associated code element. CodeElement codeElement = null; FileCodeModel fcm = dte.ActiveDocument.ProjectItem.FileCodeModel; if (fcm != null) { while (codeElement == null) { codeElement = fcm.CodeElementFromPoint(ts.ActivePoint, vsCMElement.vsCMElementFunction); if (ts.ActivePoint.AtEndOfDocument) { break; } if (codeElement == null || !(codeElement is CodeFunction)) { codeElement = null; ts.LineDown(); ts.EndOfLine(); } } } // Extract existing comment if found. ts.MoveToLineAndOffset(oldLine, oldOffset); int startLine = ExtractComment(ts, out string existingDoxyComment); // Delete old comment from the text. if (startLine >= 0) { ts.ActivePoint.CreateEditPoint().Delete(funcPoint); oldLine = ts.ActivePoint.Line; oldOffset = ts.ActivePoint.LineCharOffset; } // Generate new comment. string doxyComment = m_generator.GenerateComment(spaces, codeElement, existingDoxyComment); // Write the doxygen comment to the correct position. ts.MoveToLineAndOffset(oldLine, oldOffset); ts.LineUp(); // If the upper line is empty, we should go to the start of the line. Otherwise go to the end of the line. currentLine = ts.ActivePoint.CreateEditPoint().GetLines(ts.ActivePoint.Line, ts.ActivePoint.Line + 1); if (currentLine.Trim().Length == 0) { ts.StartOfLine(); } else { ts.EndOfLine(); } ts.Insert("\r\n" + spaces + doxyComment); // If this is a new comment, move to the main comment position immediately. if (startLine < 0) { ts.MoveToLineAndOffset(oldLine, oldOffset); ts.LineDown(); ts.EndOfLine(); } }
private static void addContentToProject(SolutionData solutionData, Project project, DTE2 dte2) { #region CPP Writing if (solutionData.projectType == ProjectType.CppEmpty) //CPP Example { Directory.CreateDirectory(solutionData.directoryPath + "\\Source Files"); Directory.CreateDirectory(solutionData.directoryPath + "\\Header Files"); foreach (ClassData classData in solutionData.classes) { #region Class if (classData.classType == ClassType.Class) { Document doc = dte2.ItemOperations.NewFile("General\\Text File", classData.className).Document; TextSelection txtsel = (TextSelection)doc.Selection; txtsel.Text = ""; txtsel.Insert("#include \"" + classData.className + ".h\"\n\n" + classData.className + "::" + classData.className + "()\n{\n}\n\n" + classData.className + "::~" + classData.className + "()\n{\n}"); doc.Save(solutionData.directoryPath + "\\Source Files\\" + classData.className + ".cpp"); project.ProjectItems.AddFromFile(solutionData.directoryPath + "\\Source Files\\" + classData.className + ".cpp"); Document doc2 = dte2.ItemOperations.NewFile("General\\Text File", classData.className).Document; TextSelection txtsel2 = (TextSelection)doc2.Selection; txtsel2.Text = ""; txtsel2.Insert("#pragma once"); if (classData.superClassName != "") { txtsel2.Insert("\n#include \"" + classData.superClassName + "\""); } foreach (string interfaceName in classData.interfaceNames) { txtsel2.Insert("\n#include \"" + interfaceName + "\""); } txtsel2.Insert("\n\nclass " + classData.className); if (classData.superClassName != "") { txtsel2.Insert(" : public " + classData.superClassName); foreach (string interfaceName in classData.interfaceNames) { txtsel2.Insert(", " + interfaceName); } } else if (classData.interfaceNames.Count != 0) { txtsel2.Insert(" : " + classData.interfaceNames[0]); for (int i = 1; i < classData.interfaceNames.Count; ++i) { txtsel2.Insert(", " + classData.interfaceNames[i]); } } txtsel2.Insert("\n{\npublic:\n\t" + classData.className + "();\n\t~" + classData.className + "();\n\nprivate:\n\n};"); doc2.Save(solutionData.directoryPath + "\\Header Files\\" + classData.className + ".h"); project.ProjectItems.AddFromFile(solutionData.directoryPath + "\\Header Files\\" + classData.className + ".h"); } #endregion #region Enum else if (classData.classType == ClassType.Enum) { EnvDTE.Document doc2 = dte2.ItemOperations.NewFile("General\\Text File", classData.className).Document; TextSelection txtsel2 = (TextSelection)doc2.Selection; txtsel2.Text = ""; txtsel2.Insert("#pragma once\n\nenum " + classData.className + "\n{\n\n};"); doc2.Save(solutionData.directoryPath + "\\Header Files\\" + classData.className + ".h"); project.ProjectItems.AddFromFile(solutionData.directoryPath + "\\Header Files\\" + classData.className + ".h"); } #endregion } } #endregion #region C# Writing else //C# Example { foreach (ProjectItem pItem in project.ProjectItems) { if (pItem.Name == "Form1.cs") { pItem.Remove(); } } foreach (ClassData classData in solutionData.classes) { if (classData.classType == ClassType.Enum || classData.classType == ClassType.Class || classData.classType == ClassType.Interface) { project.ProjectItems.AddFromTemplate(@"C:\Program Files (x86)\Microsoft Visual Studio " + dte2.Version + @"\Common7\IDE\ItemTemplates\CSharp\Code\1033\Class\Class.vstemplate", classData.className + ".cs"); } else if (classData.classType == ClassType.Form) { project.ProjectItems.AddFromTemplate(@"c:\Program Files (x86)\Microsoft Visual Studio " + dte2.Version + @"\Common7\IDE\ItemTemplates\CSharp\Windows Forms\1033\Form\windowsform.vstemplate", classData.className + ".cs"); } ProjectItem projectItem = null; foreach (ProjectItem pItem in project.ProjectItems) { if (pItem.Name == classData.className + ".cs") { projectItem = pItem; break; } } projectItem.Save(); TextSelection txtsel = (TextSelection)projectItem.Document.Selection; #region Class if (classData.classType == ClassType.Class) { txtsel.GotoLine(9); txtsel.EndOfLine(); if (classData.superClassName != "") { txtsel.Insert(" : " + classData.superClassName); foreach (string interfaceName in classData.interfaceNames) { txtsel.Insert(", " + interfaceName); } } else if (classData.interfaceNames.Count != 0) { txtsel.Insert(" : " + classData.interfaceNames[0]); for (int i = 1; i < classData.interfaceNames.Count; ++i) { txtsel.Insert(", " + classData.interfaceNames[i]); } } } #endregion #region Enum else if (classData.classType == ClassType.Enum) { txtsel.GotoLine(9); txtsel.StartOfLine(); txtsel.CharRight(false, 4); txtsel.DestructiveInsert("enum"); txtsel.Delete(); } #endregion #region Interface else if (classData.classType == ClassType.Interface) { txtsel.GotoLine(9); txtsel.StartOfLine(); txtsel.CharRight(false, 4); txtsel.Insert("interface"); txtsel.Delete(5); } #endregion } } #endregion }
/// <summary> /// Creates and adds documentation comment blocks when the user types a triple slash. /// </summary> private void HandleTripleSlash() { ThreadHelper.ThrowIfNotOnUIThread(); // Get the original placement of the cursor in the code editor TextSelection ts = (TextSelection)Dte.ActiveDocument.Selection; int oldLine = ts.ActivePoint.Line; int oldOffset = ts.ActivePoint.LineCharOffset; // Check to see if the previous line starts with a triple-slash; if it does, we should probably // just return because there's most likely a docstring already in place. ts.LineUp(); ts.StartOfLine(); string previousLine = TextView.TextSnapshot.GetLineFromPosition( TextView.Caret.Position.BufferPosition.Position).GetText(); if (previousLine.TrimStart().StartsWith("///")) { ts.MoveToLineAndOffset(oldLine, oldOffset); ts.Insert("/"); // Add the slash that the user just typed return; } // Get the contents of the next line (the one following the original line) ts.LineDown(); ts.LineDown(); ts.StartOfLine(); int currentCharIndex = TextView.Caret.Position.BufferPosition.Position; string fullText = TextView.TextSnapshot.GetText(); // Check if we just triple-slashed a method (a function or an operation) Match methodMatch = GetMethodSignatureMatch(currentCharIndex, fullText); if (methodMatch != null) { Logger.Debug($"Found a potential method match: [{methodMatch.Value}]"); string signatureString = methodMatch.Groups["Signature"].Value; string leadingSpaces = methodMatch.Groups["Spaces"].Value; // Build the summary section, which is going to go in no matter what StringBuilder commentBuilder = new StringBuilder(); commentBuilder.AppendLine("/ # Summary"); commentBuilder.Append(leadingSpaces + "/// "); // Ask the Q# parser application to pull out all of the method details so we know what to // put into the documentation comments, and add them if parsing succeeded Logger.Debug("Sending a parse request to the Q# parser..."); try { MethodSignatureResponse signature = Messenger.RequestMethodSignatureParse(signatureString); if (signature != null) { Logger.Debug($"Parsing succeeded, method name = [{signature.Name}], " + $"{signature.ParameterNames.Count} parameters, returns something = {signature.HasReturnType}."); BuildMethodCommentBlock(signature, commentBuilder, leadingSpaces); } } catch (Exception ex) { Logger.Error($"Error during method signature request: {ex.GetType().Name} - {ex.Message}"); Logger.Trace(ex.StackTrace); } // Move to the original cursor position and add the comment block to the code ts.MoveToLineAndOffset(oldLine, oldOffset); ts.Insert(commentBuilder.ToString()); ts.MoveToLineAndOffset(oldLine, oldOffset); ts.LineDown(); ts.EndOfLine(); return; } // Check if we just triple-slashed a new type Match newtypeMatch = GetNewTypeMatch(currentCharIndex, fullText); if (newtypeMatch != null) { Logger.Debug($"Found a newtype match: [{newtypeMatch.Value}]"); string leadingSpaces = newtypeMatch.Groups["Spaces"].Value; // Build the summary section StringBuilder commentBuilder = new StringBuilder(); commentBuilder.AppendLine("/ # Summary"); commentBuilder.Append(leadingSpaces + "/// "); // Move to the original cursor position and add the comment block to the code ts.MoveToLineAndOffset(oldLine, oldOffset); ts.Insert(commentBuilder.ToString()); ts.MoveToLineAndOffset(oldLine, oldOffset); ts.LineDown(); ts.EndOfLine(); return; } // If this was a triple slash on something else, just add the slash and return. ts.MoveToLineAndOffset(oldLine, oldOffset); ts.Insert("/"); }
/// ------------------------------------------------------------------------------------ /// <summary> /// Inserts the method header. /// </summary> /// ------------------------------------------------------------------------------------ public void InsertMethodHeader() { TextSelection sel = (TextSelection)m_applicationObject.ActiveDocument.Selection; CodeElement codeElement = GetMethodOrProperty(sel); if (codeElement == null) { codeElement = sel.ActivePoint.get_CodeElement(vsCMElement.vsCMElementClass); if (codeElement == null) { codeElement = sel.ActivePoint.get_CodeElement(vsCMElement.vsCMElementInterface); } if (codeElement == null || codeElement.StartPoint.Line != sel.ActivePoint.Line) { // not a function or property, so just insert /// <summary/> sel.LineUp(false, 1); if (!IsXmlCommentLine) { sel.EndOfLine(false); sel.NewLine(1); sel.Text = "///"; sel.LineDown(true, 1); sel.Delete(1); sel.LineUp(false, 1); sel.EndOfLine(false); sel.WordRight(true, 2); sel.Delete(1); } else { sel.LineDown(false, 1); } return; } } sel.MoveToPoint(codeElement.StartPoint, false); // Figure out indentation and build dashed line sel.StartOfLine(vsStartOfLineOptions.vsStartOfLineOptionsFirstColumn, false); sel.StartOfLine(vsStartOfLineOptions.vsStartOfLineOptionsFirstText, true); string indent = sel.Text; sel.StartOfLine(vsStartOfLineOptions.vsStartOfLineOptionsFirstText, false); string dashedLine = indent + "/// " + new string('-', kLineLen - sel.ActivePoint.VirtualDisplayColumn - 4); bool fGhostDoc = true; try { // Use GhostDoc if available string addinName = string.Empty; foreach (AddIn addin in m_applicationObject.AddIns) { if (addin.Name == "GhostDoc") { addinName = (addin.ProgID == "SubMain.GhostDoc.Connect") ? "Tools.SubMain.GhostDoc.DocumentThis" : "Weigelt.GhostDoc.AddIn.DocumentThis"; break; } } if (addinName != string.Empty) { m_applicationObject.ExecuteCommand(addinName, string.Empty); } else { fGhostDoc = false; } } catch { fGhostDoc = false; } if (fGhostDoc) { int nLine = sel.ActivePoint.Line; int nLineOffset = sel.ActivePoint.LineCharOffset; // Check to see if we're in the middle of the comment or at the beginning of // the method. sel.StartOfLine(vsStartOfLineOptions.vsStartOfLineOptionsFirstText, false); if (GetMethodOrProperty(sel) == null) { // we're in the middle of the comment - move to the end of the comment MoveDownAfterComment(sel); // we're inserting one line (//---) above nLine++; } else { // We are at the beginning of the method. // Check to see if the line above the current line is an attribute. If it is we want to // start there, otherwise we start at the current line. sel.LineUp(false, 1); sel.CharRight(false, 1); if (sel.ActivePoint.get_CodeElement(vsCMElement.vsCMElementAttribute) == null) { sel.MoveToLineAndOffset(nLine, 1, false); } // we're inserting two lines above nLine += 2; } // In case the line is wrapped, we want to go to the real beginning of the line sel.MoveToLineAndOffset(sel.ActivePoint.Line, 1, false); sel.StartOfLine(vsStartOfLineOptions.vsStartOfLineOptionsFirstColumn, false); // Insert a new line and then insert our dashed line. sel.Insert(dashedLine + Environment.NewLine, (int)vsInsertFlags.vsInsertFlagsCollapseToEnd); sel.LineUp(false, 1); sel.StartOfLine(vsStartOfLineOptions.vsStartOfLineOptionsFirstText, false); MoveUpBeforeComment(sel); sel.Insert(Environment.NewLine + dashedLine, (int)vsInsertFlags.vsInsertFlagsCollapseToEnd); sel.StartOfLine(vsStartOfLineOptions.vsStartOfLineOptionsFirstText, false); // put IP at previous location sel.MoveToLineAndOffset(nLine, nLineOffset, false); } else { // check if we already have a comment sel.LineUp(false, 1); if (!IsXmlCommentLine) { // Insert comment sel.EndOfLine(false); sel.NewLine(1); sel.Text = "///"; } // Insert line above MoveUpBeforeComment(sel); sel.EndOfLine(false); sel.NewLine(1); sel.Text = dashedLine; int upperLine = sel.ActivePoint.Line; sel.LineDown(false, 1); // reformat text for (; IsXmlCommentLine;) { int curLine = sel.CurrentLine; // go through all words in this line for (; sel.CurrentLine == curLine; sel.WordRight(false, 1)) { if (sel.ActivePoint.VirtualDisplayColumn > kLineLen) { // we have to break before this word sel.WordLeft(true, 1); // skip all punctuation characters for (; sel.Text.Length == 1 && char.IsPunctuation(sel.Text[0]);) { sel.CharLeft(false, 1); // removes selection sel.WordLeft(true, 1); } sel.CharLeft(false, 1); // removes selection // break the line sel.NewLine(1); // join next line with remainder of current line sel.EndOfLine(false); sel.LineDown(true, 1); sel.StartOfLine(vsStartOfLineOptions.vsStartOfLineOptionsFirstText, true); sel.WordRight(true, 1); sel.Delete(1); // insert a space between the two lines sel.Text = " "; } } } // Insert line below sel.GotoLine(upperLine + 1, false); MoveDownAfterComment(sel); sel.StartOfLine(vsStartOfLineOptions.vsStartOfLineOptionsFirstText, false); sel.NewLine(1); sel.LineUp(false, 1); sel.Text = dashedLine; sel.StartOfLine(vsStartOfLineOptions.vsStartOfLineOptionsFirstText, false); sel.LineDown(false, 1); } }
private void GetTypeAndNameForOperatorCompletion(ref TextSelection ts, out String typeName, out String varName) { ts.StartOfLine(vsStartOfLineOptions.vsStartOfLineOptionsFirstColumn, false); ts.EndOfLine(true); String lineText = SPTrim(ts.Text); int lastGtPos = lineText.IndexOf('>'); if(lastGtPos == -1) { // non-template int lastSpPos = lineText.IndexOf(' '); typeName = SPTrim(lineText.Substring(0, lastSpPos)); lineText = SPTrim(lineText.Substring(lastSpPos + 1)); } else { // template code int prevGtPos; do { prevGtPos = lastGtPos; lastGtPos = lineText.IndexOf('>', lastGtPos + 1); } while(lastGtPos >= 0); typeName = SPTrim(lineText.Substring(0, prevGtPos + 1)); lineText = SPTrim(lineText.Substring(prevGtPos + 1)); } int spPos = lineText.IndexOf(' '); if(spPos < 0) { varName = SPTrim(lineText); } else { varName = SPTrim(lineText.Substring(0, spPos)); } }
/// <summary> /// Generates a Doxygen comment block to the current caret location. /// </summary> private void GenerateComment() { var currentILine = m_textView.TextSnapshot.GetLineFromPosition(m_textView.Caret.Position.BufferPosition.Position); int len = m_textView.Caret.Position.BufferPosition.Position - currentILine.Start.Position; string currentLine = m_textView.TextSnapshot.GetText(currentILine.Start.Position, len); string spaces = currentLine.Replace(currentLine.TrimStart(), ""); ThreadHelper.ThrowIfNotOnUIThread(); TextSelection ts = m_dte.ActiveDocument.Selection as TextSelection; // Save current care position. int oldLine = ts.ActivePoint.Line; int oldOffset = ts.ActivePoint.LineCharOffset; // Check if we're at the beginning of the document and should generate a file comment. if (oldLine == 1) { string fileComment = m_generator.GenerateFileComment(m_dte, out int selectedLine); ts.DeleteLeft(2); // Removing the // part here. ts.Insert(fileComment); // Move the caret. ts.MoveToLineAndOffset(selectedLine + 1, 1); ts.EndOfLine(); return; } // Search for the associated code element for which to generate the comment. CodeElement codeElement = null; ts.LineDown(); ts.EndOfLine(); FileCodeModel fcm = m_dte.ActiveDocument.ProjectItem.FileCodeModel; if (fcm != null) { while (codeElement == null) { codeElement = fcm.CodeElementFromPoint(ts.ActivePoint, vsCMElement.vsCMElementFunction); if (ts.ActivePoint.AtEndOfDocument) { break; } if (codeElement == null) { ts.LineDown(); } } } // Generate the comment and add it to the document. string doxyComment = m_generator.GenerateComment(spaces, codeElement, ""); ts.MoveToLineAndOffset(oldLine, oldOffset); ts.DeleteLeft(2); // Removing the // part here. ts.Insert(doxyComment); // Move caret to the position where the main comment will be written. ts.MoveToLineAndOffset(oldLine, oldOffset); ts.LineDown(); ts.EndOfLine(); }
public int Exec(ref Guid pguidCmdGroup, uint nCmdID, uint nCmdexecopt, IntPtr pvaIn, IntPtr pvaOut) { try { if (VsShellUtilities.IsInAutomationFunction(m_provider.ServiceProvider)) { return(m_nextCommandHandler.Exec(ref pguidCmdGroup, nCmdID, nCmdexecopt, pvaIn, pvaOut)); } // make a copy of this so we can look at it after forwarding some commands uint commandID = nCmdID; char typedChar = char.MinValue; // make sure the input is a char before getting it if (pguidCmdGroup == VSConstants.VSStd2K && nCmdID == (uint)VSConstants.VSStd2KCmdID.TYPECHAR) { typedChar = (char)(ushort)Marshal.GetObjectForNativeVariant(pvaIn); } // check for the triple slash if (typedChar == '/' && m_dte != null) { string currentLine = m_textView.TextSnapshot.GetLineFromPosition( m_textView.Caret.Position.BufferPosition.Position).GetText(); if ((currentLine + "/").Trim() == "///") { // Calculate how many spaces string spaces = 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(); 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) { 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) { sb.AppendFormat("\r\n" + spaces + "/// <param name=\"{0}\"></param>", parameter.Name); } } if (function.Type.AsString != "void") { 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 { 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); } } } if (m_session != null && !m_session.IsDismissed) { // check for a commit character if (nCmdID == (uint)VSConstants.VSStd2KCmdID.RETURN || nCmdID == (uint)VSConstants.VSStd2KCmdID.TAB || typedChar == '>') { // check for a selection // if the selection is fully selected, commit the current session if (m_session.SelectedCompletionSet.SelectionStatus.IsSelected) { string selectedCompletion = m_session.SelectedCompletionSet.SelectionStatus.Completion.DisplayText; m_session.Commit(); TextSelection ts = m_dte.ActiveDocument.Selection as TextSelection; switch (selectedCompletion) { case "<!-->": ts.CharLeft(false, 3); break; case "<![CDATA[>": ts.CharLeft(false, 3); break; case "<c>": ts.CharLeft(false, 4); break; case "<code>": ts.CharLeft(false, 7); break; case "<example>": ts.CharLeft(false, 10); break; case "<exception>": ts.CharLeft(false, 14); break; case "<include>": ts.CharLeft(false, 21); break; case "<list>": ts.CharLeft(false, 7); break; case "<para>": ts.CharLeft(false, 7); break; case "<param>": ts.CharLeft(false, 10); break; case "<paramref>": ts.CharLeft(false, 13); break; case "<permission>": ts.CharLeft(false, 15); break; case "<remarks>": ts.CharLeft(false, 10); break; case "<returns>": ts.CharLeft(false, 10); break; case "<see>": ts.CharLeft(false, 3); break; case "<seealso>": ts.CharLeft(false, 3); break; case "<typeparam>": ts.CharLeft(false, 14); break; case "<typeparamref>": ts.CharLeft(false, 3); break; case "<value>": ts.CharLeft(false, 8); break; default: break; } // also, don't add the character to the buffer return(VSConstants.S_OK); } else { // if there is no selection, dismiss the session m_session.Dismiss(); } } } else { if (pguidCmdGroup == VSConstants.VSStd2K && nCmdID == (uint)VSConstants.VSStd2KCmdID.RETURN) { string currentLine = m_textView.TextSnapshot.GetLineFromPosition( m_textView.Caret.Position.BufferPosition.Position).GetText(); if (currentLine.TrimStart().StartsWith("///")) { TextSelection ts = m_dte.ActiveDocument.Selection as TextSelection; string spaces = currentLine.Replace(currentLine.TrimStart(), ""); ts.Insert("\r\n" + spaces + "/// "); return(VSConstants.S_OK); } } } // pass along the command so the char is added to the buffer 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(); if (currentLine.TrimStart().StartsWith("///")) { if (m_session == null || m_session.IsDismissed) // If there is no active session, bring up completion { if (this.TriggerCompletion()) { m_session.SelectedCompletionSet.SelectBestMatch(); m_session.SelectedCompletionSet.Recalculate(); return(VSConstants.S_OK); } } } } else if ( commandID == (uint)VSConstants.VSStd2KCmdID.BACKSPACE || commandID == (uint)VSConstants.VSStd2KCmdID.DELETE || char.IsLetter(typedChar)) { if (m_session != null && !m_session.IsDismissed) // the completion session is already active, so just filter { m_session.SelectedCompletionSet.SelectBestMatch(); m_session.SelectedCompletionSet.Recalculate(); return(VSConstants.S_OK); } } return(retVal); } catch { } return(VSConstants.E_FAIL); }
public int Exec(ref Guid pguidCmdGroup, uint nCmdID, uint nCmdexecopt, IntPtr pvaIn, IntPtr pvaOut) { try { ThreadHelper.ThrowIfNotOnUIThread(); if (VsShellUtilities.IsInAutomationFunction(m_provider.ServiceProvider)) { return(m_nextCommandHandler.Exec(ref pguidCmdGroup, nCmdID, nCmdexecopt, pvaIn, pvaOut)); } // make a copy of this so we can look at it after forwarding some commands uint commandID = nCmdID; char typedChar = char.MinValue; // make sure the input is a char before getting it if (pguidCmdGroup == VSConstants.VSStd2K && nCmdID == (uint)VSConstants.VSStd2KCmdID.TYPECHAR) { typedChar = (char)(ushort)Marshal.GetObjectForNativeVariant(pvaIn); } // Check if it is a commit character, to generate a multiline comment bool isCommitChar = nCmdID == (uint)VSConstants.VSStd2KCmdID.RETURN || nCmdID == (uint)VSConstants.VSStd2KCmdID.TAB; bool showCompletion = nCmdID == (uint)VSConstants.VSStd2KCmdID.COMPLETEWORD; // Handle only typed characters or in case of an active completion also deletions if (typedChar == '\0' && !isCommitChar && !showCompletion) { return(m_nextCommandHandler.Exec(ref pguidCmdGroup, nCmdID, nCmdexecopt, pvaIn, pvaOut)); } // check if the last character of one of the supported shortcuts is typed if (!m_provider.CompletionBroker.IsCompletionActive(m_textView) && m_dte != null && (typedChar == m_header_char || isCommitChar || typedChar == '!')) { var currentILine = m_textView.TextSnapshot.GetLineFromPosition( m_textView.Caret.Position.BufferPosition.Position); int len = m_textView.Caret.Position.BufferPosition.Position - currentILine.Start.Position; string currentLine = m_textView.TextSnapshot.GetText(currentILine.Start.Position, len); string currentLineFull = currentILine.GetText(); string typed_shortcut = (currentLine + typedChar).Trim(); if (typed_shortcut.Trim().Length >= 3) { // Get the current text properties TextSelection ts = m_dte.ActiveDocument.Selection as TextSelection; int oldLine = ts.ActivePoint.Line; int oldOffset = ts.ActivePoint.LineCharOffset; string lineEnding = GetLineEnding(); // First use only single line comment // Single line comments are not supported in the first line, because of the header comment if (typedChar == '*' && typed_shortcut == "/**" && oldLine > 1) { ts.Insert(typedChar + " "); if (!currentLineFull.Contains("*/")) { ts.Insert("*/"); } ts.MoveToLineAndOffset(oldLine, oldOffset + 2); return(VSConstants.S_OK); } // If it is a commit character check if there is a comment to expand if (isCommitChar && ShouldExpand(ts, currentLineFull, oldLine, oldOffset, out var commentFormat, out var codeElement, out var shortcut)) { // Replace all possible comment characters to get the raw brief string currentText = Regex.Replace(currentLineFull.Replace(shortcut, ""), @"\/\*+|\*+\/|\/\/+", "").Trim(); // Delete current comment int lenToDelete = Regex.Replace(currentLineFull, @".*\/\*|^[^\/]*\/\/", "").Length; ts.MoveToLineAndOffset(oldLine, oldOffset); ts.EndOfLine(); ts.DeleteLeft(lenToDelete); // Create new multiline comment currentLine = currentLineFull.Substring(0, currentLineFull.Length - lenToDelete); currentLineFull = currentLine; oldOffset = ts.ActivePoint.LineCharOffset; return(InsertMultilineComment(commentFormat, codeElement, ts, currentLine, lineEnding, oldLine, oldOffset, currentText)); } // The header can be used without single line format else if (oldLine == 1) { var headerShortcut = m_settings.HeaderFormat.Substring(0, 3); if (typed_shortcut == headerShortcut || typed_shortcut == "/**" || typed_shortcut == "/*!" || typed_shortcut == "///") { // Delete current end comment chars ts.EndOfLine(); int lenToDelete = ts.ActivePoint.LineCharOffset - oldOffset; ts.DeleteLeft(lenToDelete); return(InsertMultilineComment(CommentFormat.header, null, ts, currentLine, lineEnding, oldLine, oldOffset, "")); } } // '/*!' is a always active shortcut without single line // This is for an eseaier beginning and for the same workflow as older versions else if (typed_shortcut == "/*!") { var _commentFormat = GetCommentFormat(ts, oldLine, oldOffset, out var _codeElement); // Delete current end comment chars ts.EndOfLine(); int lenToDelete = ts.ActivePoint.LineCharOffset - oldOffset; ts.DeleteLeft(lenToDelete); return(InsertMultilineComment(_commentFormat, _codeElement, ts, currentLine, lineEnding, oldLine, oldOffset, "")); } } } if (m_session != null && !m_session.IsDismissed) { // check for a commit character if (nCmdID == (uint)VSConstants.VSStd2KCmdID.RETURN || nCmdID == (uint)VSConstants.VSStd2KCmdID.TAB) { // check for a selection // if the selection is fully selected, commit the current session if (m_session.SelectedCompletionSet.SelectionStatus.IsSelected) { m_session.Commit(); // also, don't add the character to the buffer return(VSConstants.S_OK); } else { // if there is no selection, dismiss the session m_session.Dismiss(); } } } else if (!m_provider.CompletionBroker.IsCompletionActive(m_textView)) { if (pguidCmdGroup == VSConstants.VSStd2K && nCmdID == (uint)VSConstants.VSStd2KCmdID.RETURN) { ITextSnapshotLine currentILine = m_textView.TextSnapshot.GetLineFromPosition( m_textView.Caret.Position.BufferPosition.Position); string currentLine = currentILine.GetText(); // TODO: check for being inside a comment block // Insert a '*' when creating a new line in a mutline comment if (currentLine.TrimStart().StartsWith("*") && !currentLine.Contains("*/")) { TextSelection ts = m_dte.ActiveDocument.Selection as TextSelection; string spaces = currentLine.Replace(currentLine.TrimStart(), ""); string lineEnding = GetLineEnding(); ts.Insert(lineEnding + spaces + "* "); return(VSConstants.S_OK); } // Insert a '///' when creating a new line in a mutline comment if (currentLine.TrimStart().StartsWith("///")) { TextSelection ts = m_dte.ActiveDocument.Selection as TextSelection; string spaces = currentLine.Replace(currentLine.TrimStart(), ""); string lineEnding = GetLineEnding(); ts.Insert(lineEnding + spaces + "/// "); return(VSConstants.S_OK); } } } // pass along the command so the char is added to the buffer int retVal = m_nextCommandHandler.Exec(ref pguidCmdGroup, nCmdID, nCmdexecopt, pvaIn, pvaOut); if (typedChar == '\\' || typedChar == '@' || showCompletion) { string currentLine = m_textView.TextSnapshot.GetLineFromPosition( m_textView.Caret.Position.BufferPosition.Position).GetText(); if (currentLine.TrimStart().StartsWith("*") || currentLine.TrimStart().StartsWith("///")) { if (m_session == null || m_session.IsDismissed) // If there is no active session, bring up completion { if (this.TriggerCompletion()) { m_session.SelectedCompletionSet.SelectBestMatch(); m_session.SelectedCompletionSet.Recalculate(); return(VSConstants.S_OK); } } } } else if ( commandID == (uint)VSConstants.VSStd2KCmdID.BACKSPACE || commandID == (uint)VSConstants.VSStd2KCmdID.DELETE || char.IsLetter(typedChar)) { if (m_session != null && !m_session.IsDismissed) // the completion session is already active, so just filter { m_session.SelectedCompletionSet.SelectBestMatch(); m_session.SelectedCompletionSet.Recalculate(); return(VSConstants.S_OK); } } return(retVal); }
/// <summary> /// Tries to detect the top level function declaration and adds comment if it was a function. /// </summary> /// <param name="textSelection">The <see cref="TextSelection"/>.</param> /// <returns>True if successful; otherwise false.</returns> private bool TryAddCommentForTopLevelFunctionDeclaration(TextSelection textSelection, string lineBreak, string indent, int lineNum, int offset) { // Create an edit point var editPoint = textSelection.ActivePoint.CreateEditPoint(); // Get the function declaration string functionLine = editPoint.GetLines(editPoint.Line, editPoint.Line + 1); // Check if it's empty or there is no left parentheses if (String.IsNullOrEmpty(functionLine) || functionLine.IndexOf('(') < 0) { return(false); } // If there is no semi-colon, then find the semi-colon if (functionLine.TrimEnd().EndsWith(";")) { editPoint.EndOfLine(); editPoint.CharLeft(); } else { editPoint.EndOfLine(); editPoint = this.FindFirstMatch(editPoint, ";"); } if (editPoint == null) { return(false); } // Get the end of the text var endPoint = editPoint.CreateEditPoint(); endPoint.EndOfLine(); string text = editPoint.GetText(endPoint); // Remove the text and append {} editPoint.Delete(endPoint); editPoint.Insert("{}"); var codeElement = editPoint.CodeElement[vsCMElement.vsCMElementFunction]; if (codeElement == null || !(codeElement is CodeFunction)) { // Remove the text and append original text editPoint.CharLeft(2); editPoint.Delete(2); editPoint.Insert(text); textSelection.MoveToLineAndOffset(lineNum, offset); return(false); } // Generate comment for function StringBuilder sb = new StringBuilder("/ <summary>" + lineBreak + indent + "/// " + lineBreak + indent + "/// </summary>"); foreach (CodeElement child in codeElement.Children) { CodeParameter parameter = child as CodeParameter; if (parameter != null) { sb.AppendFormat("{0}{1}/// <param name=\"{2}\"></param>", lineBreak, indent, parameter.Name); } } // If there is the return type is not void, generate a returns element var function = codeElement as CodeFunction; if (function.Type.AsString != "void") { sb.AppendFormat("{0}{1}/// <returns></returns>", lineBreak, indent); } // Insert comment textSelection.MoveToLineAndOffset(lineNum, offset); textSelection.Insert(sb.ToString()); // Remove the text and append original text editPoint.CharLeft(2); editPoint.Delete(2); editPoint.Insert(text); // Move the caret to the summary section textSelection.MoveToLineAndOffset(lineNum + 1, offset); textSelection.EndOfLine(); // Return success return(true); }
private void DoFormat(bool selected) { if (_dte == null || _dte.ActiveDocument == null) { return; } string text = ""; string initIndent = ""; EditPoint sp; EditPoint sd; if (selected) { TextSelection sel = (TextSelection)_dte.ActiveDocument.Selection; sp = sel.TopPoint.CreateEditPoint(); sd = sel.BottomPoint.CreateEditPoint(); sel.MoveToLineAndOffset(sp.Line, 1); sel.MoveToLineAndOffset(sd.Line, 1, true); sel.EndOfLine(true); sp = sel.TopPoint.CreateEditPoint(); sd = sel.BottomPoint.CreateEditPoint(); text = sel.Text; foreach (var ch in text) { if (ch != ' ' && ch != '\t') { break; } initIndent += ch; } } else { TextDocument doc = (TextDocument)_dte.ActiveDocument.Object("TextDocument"); sp = doc.StartPoint.CreateEditPoint(); sd = doc.EndPoint.CreateEditPoint(); text = sp.GetText(sd); } if (String.IsNullOrEmpty(text)) { return; } string eol = GetEol(text); int lang = GetLanguage(); int sizeOut = 0; int sizeMsg = 0; if (NativeMethods.DoFormatter(lang, text, null, ref sizeOut, null, ref sizeMsg, 0, eol, initIndent)) { StringBuilder textOut = new StringBuilder(sizeOut); StringBuilder textMsg = new StringBuilder(sizeMsg); if (NativeMethods.DoFormatter(lang, text, textOut, ref sizeOut, textMsg, ref sizeMsg, 0, eol, initIndent)) { ShowOutput(textMsg.ToString()); sp.ReplaceText(sd, textOut.ToString(), (int)vsEPReplaceTextOptions.vsEPReplaceTextKeepMarkers); } } }
public static void ApplyPrefeence(DTE2 dte, string region, string controller, string field, string property, string preferredValue) { //string preferredValue = GetMostPreferredValue(region, controller, field, property); List <PreviousPropertyValue> listDefault = new List <PreviousPropertyValue>(); foreach (ProjectItem pi in dte.Solution.Projects.Item(1).ProjectItems) { if (pi.ProjectItems != null) { foreach (ProjectItem p in pi.ProjectItems) { if (p.Name.EndsWith(".Designer.cs")) { p.Open(EnvDTE.Constants.vsViewKindCode); p.Document.Activate(); TextSelection ts = (TextSelection)dte.ActiveDocument.Selection; TextSelection ts2 = (TextSelection)dte.ActiveDocument.Selection; string srchPattern1 = "new System.Windows.Forms.Button();"; EnvDTE.TextRanges textRanges = null; ts.StartOfDocument(false); int count = 0; string nameLine = ""; string name = ""; string[] np = new string[50]; while (ts.FindPattern(srchPattern1, 0, ref textRanges)) { ts.SelectLine(); nameLine = ts.Text; count++; string[] sp = nameLine.Split('.'); string spi = sp[1]; string[] sp2 = spi.Split('='); name = sp2[0]; np[count] = name; } int i = 1; while (ts2.FindPattern(".BackColor = System.Drawing.Color", 0, ref textRanges)) { PreviousPropertyValue def = new PreviousPropertyValue(); ts2.SelectLine(); string codeLine = ts2.Text; codeLine = codeLine.Trim(); foreach (string s in np) { string ss = s; if (ss != null) { ss = ss.Trim(); if (codeLine.Contains(ss) == true) { ts2.ReplacePattern(codeLine, "this." + s + ".BackColor = System.Drawing.Color." + preferredValue + ";", 0, ref textRanges); np = np.Where(w => w != s).ToArray(); def.FileName = p.Name; def.ControllerType = controller; def.Property = property; def.ControllerName = ss; def.DefaultValue = codeLine; listDefault.Add(def); } //else //{ // ts2.LineDown(); // ts2.NewLine(); // ts2.Insert("this." + np[i] + ".BackColor = System.Drawing.Color." + preferredValue + ";"); //} //def.FileName = p.Name; //def.ControllerType = controller; //def.Property = property; //def.ControllerName = ss; //def.DefaultValue = codeLine; //listDefault.Add(def); } } //i++; } if (np != null) { foreach (string s in np) { if (s != null) { ts2.EndOfLine(); ts2.NewLine(); ts2.Insert("this." + np[i] + ".BackColor = System.Drawing.Color." + preferredValue + ";"); np = np.Where(w => w != s).ToArray(); } } } SaveDefaultValues(listDefault); dte.ActiveDocument.Save(p.Document.FullName); dte.ActiveDocument.Close(vsSaveChanges.vsSaveChangesNo); } } } } }
public int Exec(ref Guid pguidCmdGroup, uint nCmdID, uint nCmdexecopt, IntPtr pvaIn, IntPtr pvaOut) { try { if (VsShellUtilities.IsInAutomationFunction(m_provider.ServiceProvider)) { return(m_nextCommandHandler.Exec(ref pguidCmdGroup, nCmdID, nCmdexecopt, pvaIn, pvaOut)); } // make a copy of this so we can look at it after forwarding some commands uint commandID = nCmdID; char typedChar = char.MinValue; // make sure the input is a char before getting it if (pguidCmdGroup == VSConstants.VSStd2K && nCmdID == (uint)VSConstants.VSStd2KCmdID.TYPECHAR) { typedChar = (char)(ushort)Marshal.GetObjectForNativeVariant(pvaIn); } // check for the triple slash if (typedChar == '!' && m_dte != null) { var currentILine = m_textView.TextSnapshot.GetLineFromPosition( m_textView.Caret.Position.BufferPosition.Position); int len = m_textView.Caret.Position.BufferPosition.Position - currentILine.Start.Position; string currentLine = m_textView.TextSnapshot.GetText(currentILine.Start.Position, len); string currentLineFull = currentILine.GetText(); if ((currentLine + typedChar).Trim() == "/*!") { // Calculate how many spaces string spaces = currentLine.Replace(currentLine.TrimStart(), ""); TextSelection ts = m_dte.ActiveDocument.Selection as TextSelection; int oldLine = ts.ActivePoint.Line; int oldOffset = ts.ActivePoint.LineCharOffset; if (!currentLineFull.Contains("*/")) { ts.Insert("*/"); } ts.LineDown(); ts.EndOfLine(); CodeElement codeElement = null; FileCodeModel fcm = m_dte.ActiveDocument.ProjectItem.FileCodeModel; if (fcm != null) { while (codeElement == null) { codeElement = fcm.CodeElementFromPoint(ts.ActivePoint, vsCMElement.vsCMElementFunction); if (codeElement == null) { ts.LineDown(); } } } var cls = codeElement as VCCodeClass; var cls2 = codeElement as CodeClass; var fnc = codeElement as VCCodeFunction; var kind = codeElement.Kind; if (codeElement != null && codeElement is CodeFunction) { VCCodeFunction function = codeElement as VCCodeFunction; StringBuilder sb = new StringBuilder("!\r\n" + spaces + " * \r\n" + spaces + " * "); foreach (CodeElement child in codeElement.Children) { CodeParameter parameter = child as CodeParameter; if (parameter != null) { sb.AppendFormat("\r\n" + spaces + " * \\param {0}", parameter.Name); } } if (function.Type.AsString != "void") { sb.AppendFormat("\r\n" + spaces + " * \\return "); } sb.AppendFormat("\r\n" + spaces + " "); ts.MoveToLineAndOffset(oldLine, oldOffset); ts.Insert(sb.ToString()); ts.MoveToLineAndOffset(oldLine, oldOffset); ts.LineDown(); ts.EndOfLine(); return(VSConstants.S_OK); } else if (codeElement != null && codeElement is VCCodeClass) { VCCodeClass function = codeElement as VCCodeClass; StringBuilder sb = new StringBuilder("!\r\n" + spaces + " * \r\n" + spaces + " * "); foreach (CodeElement child in function.TemplateParameters) { CodeParameter parameter = child as CodeParameter; if (parameter != null) { sb.AppendFormat("\r\n" + spaces + " * \\tparam {0}", parameter.Name); } } sb.AppendFormat("\r\n" + spaces + " "); ts.MoveToLineAndOffset(oldLine, oldOffset); ts.Insert(sb.ToString()); ts.MoveToLineAndOffset(oldLine, oldOffset); ts.LineDown(); ts.EndOfLine(); return(VSConstants.S_OK); } else { ts.MoveToLineAndOffset(oldLine, oldOffset); ts.Insert("!\r\n" + spaces + " * \r\n" + spaces + " * \r\n" + spaces + " "); ts.MoveToLineAndOffset(oldLine, oldOffset); ts.LineDown(); ts.EndOfLine(); return(VSConstants.S_OK); } } } if (m_session != null && !m_session.IsDismissed) { // check for a commit character if (nCmdID == (uint)VSConstants.VSStd2KCmdID.RETURN || nCmdID == (uint)VSConstants.VSStd2KCmdID.TAB) { // check for a selection // if the selection is fully selected, commit the current session if (m_session.SelectedCompletionSet.SelectionStatus.IsSelected) { string selectedCompletion = m_session.SelectedCompletionSet.SelectionStatus.Completion.DisplayText; m_session.Commit(); // also, don't add the character to the buffer return(VSConstants.S_OK); } else { // if there is no selection, dismiss the session m_session.Dismiss(); } } } else { if (pguidCmdGroup == VSConstants.VSStd2K && nCmdID == (uint)VSConstants.VSStd2KCmdID.RETURN) { string currentLine = m_textView.TextSnapshot.GetLineFromPosition( m_textView.Caret.Position.BufferPosition.Position).GetText(); if (currentLine.TrimStart().StartsWith("*")) { TextSelection ts = m_dte.ActiveDocument.Selection as TextSelection; string spaces = currentLine.Replace(currentLine.TrimStart(), ""); ts.Insert("\r\n" + spaces + "* "); return(VSConstants.S_OK); } } } // pass along the command so the char is added to the buffer 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(); if (currentLine.TrimStart().StartsWith("*")) { if (m_session == null || m_session.IsDismissed) // If there is no active session, bring up completion { if (this.TriggerCompletion()) { m_session.SelectedCompletionSet.SelectBestMatch(); m_session.SelectedCompletionSet.Recalculate(); return(VSConstants.S_OK); } } } } else if ( commandID == (uint)VSConstants.VSStd2KCmdID.BACKSPACE || commandID == (uint)VSConstants.VSStd2KCmdID.DELETE || char.IsLetter(typedChar)) { if (m_session != null && !m_session.IsDismissed) // the completion session is already active, so just filter { m_session.SelectedCompletionSet.SelectBestMatch(); m_session.SelectedCompletionSet.Recalculate(); return(VSConstants.S_OK); } } return(retVal); } catch { } return(VSConstants.E_FAIL); }
/// <summary> /// Generates a Doxygen comment block to the current caret location. /// </summary> private void GenerateComment() { var currentILine = m_textView.TextSnapshot.GetLineFromPosition(m_textView.Caret.Position.BufferPosition.Position); int len = m_textView.Caret.Position.BufferPosition.Position - currentILine.Start.Position; string currentLine = m_textView.TextSnapshot.GetText(currentILine.Start.Position, len); string spaces = currentLine.Replace(currentLine.TrimStart(), ""); string next2char = m_textView.TextSnapshot.GetText(currentILine.Start.Position + len, 2); ThreadHelper.ThrowIfNotOnUIThread(); TextSelection ts = m_dte.ActiveDocument.Selection as TextSelection; // Save current care position. int oldLine = ts.ActivePoint.Line; int oldOffset = ts.ActivePoint.LineCharOffset; // Removing the auto inserted "*/" if (next2char == "*/") { ts.Delete(2); } // Check if we're at the beginning of the document and should generate a file comment. if (oldLine == 1) { string fileComment = m_generator.GenerateFileComment(m_dte, out int selectedLine); ts.DeleteLeft(2); // Removing the // part here. ts.Insert(fileComment); // Move the caret. ts.MoveToLineAndOffset(selectedLine + 1, 1); ts.EndOfLine(); return; } // Search for the associated code element for which to generate the comment. CodeElement codeElement = null; ts.LineDown(); ts.EndOfLine(); FileCodeModel fcm = this.GetFileCodeModel(m_dte.ActiveDocument); if (fcm != null) { while (codeElement == null) { codeElement = CodeElementFromPoint(fcm, ts.ActivePoint, vsCMElement.vsCMElementNamespace, vsCMElement.vsCMElementClass, vsCMElement.vsCMElementStruct, vsCMElement.vsCMElementEnum, vsCMElement.vsCMElementFunction, vsCMElement.vsCMElementUnion); if (ts.ActivePoint.AtEndOfDocument) { break; } if (codeElement == null) { ts.LineDown(); } } // if active line is in function body, set codeElement to null if (codeElement is CodeFunction function && oldLine > codeElement.StartPoint.Line && oldLine < codeElement.EndPoint.Line) { codeElement = null; } } // Generate the comment and add it to the document. string doxyComment = m_generator.GenerateComment(spaces, codeElement, ""); ts.MoveToLineAndOffset(oldLine, oldOffset); ts.DeleteLeft(2); // Removing the // part here. ts.Insert(doxyComment); if (!m_generator.UseSingleLineComment(codeElement)) { // Move caret to the position where the main comment will be written. ts.MoveToLineAndOffset(oldLine, oldOffset); ts.LineDown(); ts.EndOfLine(); } else { ts.MoveToLineAndOffset(oldLine, oldOffset + 2); } }
public int Exec(ref Guid pguidCmdGroup, uint nCmdID, uint nCmdexecopt, IntPtr pvaIn, IntPtr pvaOut) { try { if (VsShellUtilities.IsInAutomationFunction(m_provider.ServiceProvider)) { return(m_nextCommandHandler.Exec(ref pguidCmdGroup, nCmdID, nCmdexecopt, pvaIn, pvaOut)); } // make a copy of this so we can look at it after forwarding some commands uint commandID = nCmdID; char typedChar = char.MinValue; // make sure the input is a char before getting it if (pguidCmdGroup == VSConstants.VSStd2K && nCmdID == (uint)VSConstants.VSStd2KCmdID.TYPECHAR) { typedChar = (char)(ushort)Marshal.GetObjectForNativeVariant(pvaIn); } if (m_dte != null) { string currentLine = m_textView.TextSnapshot.GetLineFromPosition( m_textView.Caret.Position.BufferPosition.Position).GetText(); // check for the Javadoc slash and two asterisk pattern while compensating for visual studio's block comment closing generation if (typedChar == '*' && currentLine.Trim() == "/**/") { // Calculate how many spaces string spaces = currentLine.Replace(currentLine.TrimStart(), ""); TextSelection ts = m_dte.ActiveDocument.Selection as TextSelection; //Remember where the cursor was when command was triggered int oldLine = ts.ActivePoint.Line; int oldOffset = ts.ActivePoint.LineCharOffset; ts.LineDown(); ts.EndOfLine(); ts.SelectLine(); //Detect and skip over Unreal Engine Function Macros string trimmedFuncLine = ts.Text.Trim(); if (trimmedFuncLine != "" && trimmedFuncLine.StartsWith("UFUNCTION(")) { ts.EndOfLine(); } else { ts.MoveToLineAndOffset(oldLine, oldOffset); ts.LineDown(); ts.EndOfLine(); } 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) { CodeFunction function = codeElement as CodeFunction; StringBuilder sb = new StringBuilder("*"); bool isNoArgsNoReturn = true; foreach (CodeElement child in codeElement.Children) { CodeParameter parameter = child as CodeParameter; if (parameter != null) { sb.AppendFormat("\r\n" + spaces + " * @param {0} ", parameter.Name); isNoArgsNoReturn = false; } } if (function.Type.AsString != "void") { isNoArgsNoReturn = false; if (function.Type.AsString == "bool") { sb.AppendFormat("\r\n" + spaces + " * @return true \r\n" + spaces + " * @return false "); } else { sb.AppendFormat("\r\n" + spaces + " * @return "); } } //If function has a return type or parameters then we generate them and return, otherwise we skip to generate a single line comment if (!isNoArgsNoReturn) { sb.Insert(1, "\r\n" + spaces + " * "); sb.AppendFormat("\r\n" + spaces + " "); ts.MoveToLineAndOffset(oldLine, oldOffset); ts.Insert(sb.ToString()); ts.MoveToLineAndOffset(oldLine, oldOffset); ts.LineDown(); ts.EndOfLine(); return(VSConstants.S_OK); } } //For variables and void functions with no parameters we can do a single line comment ts.MoveToLineAndOffset(oldLine, oldOffset); ts.Insert("* "); ts.MoveToLineAndOffset(oldLine, oldOffset + 2); return(VSConstants.S_OK); } else if (nCmdID == (uint)VSConstants.VSStd2KCmdID.RETURN) { //Get text on current line before and after cursor TextSelection ts = m_dte.ActiveDocument.Selection as TextSelection; int oldLine = ts.ActivePoint.Line; int oldOffset = ts.ActivePoint.LineCharOffset; ts.EndOfLine(true); string afterCursor = ts.Text; ts.MoveToLineAndOffset(oldLine, oldOffset); ts.StartOfLine(vsStartOfLineOptions.vsStartOfLineOptionsFirstColumn, true); string beforeCursor = ts.Text; string beforeCursorTrimmed = beforeCursor.TrimStart(); ts.MoveToLineAndOffset(oldLine, oldOffset); // Calculate how many spaces string spaces = beforeCursorTrimmed == "" ? beforeCursor : beforeCursor.Replace(beforeCursorTrimmed, ""); bool hasAsteriskBeforeCursor = beforeCursorTrimmed == "" ? false : beforeCursorTrimmed.StartsWith("* "); bool hasBlockTerminatorAfterCursor = afterCursor == "" ? false : afterCursor.EndsWith("*/"); bool hasBlockTerminatorDirectlyAfterCursor = hasBlockTerminatorAfterCursor && afterCursor.Trim() == "*/"; //Add a space to maintain correct asterisk alignment if needed if (beforeCursorTrimmed != "" && beforeCursorTrimmed.StartsWith("/*")) { hasAsteriskBeforeCursor = true; spaces += " "; } if (hasAsteriskBeforeCursor) { ts.Insert("\r\n" + spaces); if (!hasBlockTerminatorAfterCursor) { ts.Insert("* "); } else if (hasBlockTerminatorDirectlyAfterCursor) { ts.Delete(afterCursor.Length); ts.Insert("*/"); ts.MoveToLineAndOffset(ts.ActivePoint.Line, ts.ActivePoint.LineCharOffset - 2); } return(VSConstants.S_OK); } else if (hasBlockTerminatorAfterCursor) { ts.Insert("* \r\n" + spaces); if (hasBlockTerminatorDirectlyAfterCursor) { ts.Delete(afterCursor.Length); ts.Insert("*/"); ts.MoveToLineAndOffset(ts.ActivePoint.Line, ts.ActivePoint.LineCharOffset - 2); } return(VSConstants.S_OK); } } } // pass along the command so the char is added to the buffer return(m_nextCommandHandler.Exec(ref pguidCmdGroup, nCmdID, nCmdexecopt, pvaIn, pvaOut)); } catch { } return(VSConstants.E_FAIL); }
protected int ShowEventHandler(string document, string codeBehind, string codeBehindFile, string className, string objectTypeName, string eventName, string eventHandlerName) { var projectItem = GetProjectItem(document, codeBehind, codeBehindFile); var binder = GetBinder(projectItem); if (binder == null) { return(NativeMethods.E_FAIL); } projectItem.Open(EnvDTE.Constants.vsViewKindCode); var function = binder.FindEventHandler(className, objectTypeName, eventName, eventHandlerName); if (function != null) { bool prevLineIsEmpty = true; EditPoint point = function.EndPoint.CreateEditPoint(); point.LineUp(1); string lines = point.GetLines(point.Line, (int)(point.Line + 1)); for (int i = 0; i < lines.Length; i++) { if (!char.IsWhiteSpace(lines[i])) { prevLineIsEmpty = false; break; } } Document document2 = projectItem.Document; if (document2 != null) { Window activeWindow = document2.ActiveWindow; if (activeWindow != null) { TextSelection selection = activeWindow.Selection as TextSelection; if (selection != null) { selection.MoveToPoint(function.EndPoint, false); if (prevLineIsEmpty) { selection.StartOfLine(vsStartOfLineOptions.vsStartOfLineOptionsFirstText, false); int virtualCharOffset = selection.AnchorPoint.VirtualCharOffset; selection.LineUp(false, 1); if (selection.AnchorPoint.VirtualCharOffset <= virtualCharOffset) { int indentSize = 4; TextDocument document3 = document2 as TextDocument; if (document3 != null) { indentSize = document3.IndentSize; } selection.MoveToLineAndOffset(selection.AnchorPoint.Line, (int)(virtualCharOffset + indentSize), false); } } else { selection.LineUp(false, 1); //selection.StartOfLine(vsStartOfLineOptions.vsStartOfLineOptionsFirstColumn, false); selection.EndOfLine(false); } } } } } return(NativeMethods.S_OK); }