/// <summary> /// Creates a new comment line based on the position of the caret and Doxygen configuration. /// </summary> /// <param name="currentLine">Current line for reference.</param> private void NewCommentLine(string currentLine) { ThreadHelper.ThrowIfNotOnUIThread(); string startSpaces = currentLine.Replace(currentLine.TrimStart(), ""); string endSpaces = currentLine.Replace(currentLine.TrimEnd(), ""); TextSelection ts = m_dte.ActiveDocument.Selection as TextSelection; // Try to also guess proper indentation level based on the current line. int oldLine = ts.ActivePoint.Line; int oldOffset = ts.ActivePoint.LineCharOffset; int extraIndent = 0; while (!currentLine.StartsWith("/*!")) { if (m_regexTagSection.IsMatch(currentLine)) { extraIndent = m_configService.Config.TagIndentation; break; } ts.LineUp(); currentLine = ts.ActivePoint.CreateEditPoint().GetLines(ts.ActivePoint.Line, ts.ActivePoint.Line + 1); currentLine = currentLine.TrimStart(); } // Remove extra spaces from the previous line and add tag start line. ts.MoveToLineAndOffset(oldLine, oldOffset); ts.DeleteLeft(endSpaces.Length); // TODO: This adds trailing space. Get rid of it similarly to SmartIndent(). ts.Insert(m_generator.GenerateTagStartLine(startSpaces) + new string(' ', extraIndent)); }
/// <summary> /// Creates a new comment line based on the position of the caret and Doxygen configuration. /// </summary> /// <param name="currentLine">Current line for reference.</param> private void NewCommentLine() { ThreadHelper.ThrowIfNotOnUIThread(); int lineNumber = m_textView.Caret.Position.BufferPosition.GetContainingLine().LineNumber; string currentLine = m_textView.TextSnapshot.GetLineFromLineNumber(lineNumber).GetText(); string startSpaces = currentLine.Replace(currentLine.TrimStart(), ""); string endSpaces = currentLine.Replace(currentLine.TrimEnd(), ""); // Try to also guess proper indentation level based on the current line. int extraIndent = 0; int i = lineNumber; string loopLine = currentLine; while (!loopLine.StartsWith("/*!") || !loopLine.StartsWith("/**")) { if (m_regexTagSection.IsMatch(loopLine)) { extraIndent = m_config.TagIndentation; break; } --i; if (i < 0 || i > m_textView.TextSnapshot.LineCount - 1) { break; } loopLine = m_textView.TextSnapshot.GetLineFromLineNumber(i).GetText().TrimStart(); } TextSelection ts = m_dte.ActiveDocument.Selection as TextSelection; ts.DeleteLeft(endSpaces.Length); if (currentLine.EndsWith("*/")) { ts.MoveToLineAndOffset(ts.CurrentLine, currentLine.Length - 1); } else { ts.MoveToLineAndOffset(ts.CurrentLine, currentLine.Length + 1); } // TODO: This adds trailing space. Get rid of it similarly to SmartIndent(). ts.Insert(m_generator.GenerateTagStartLine(startSpaces) + new string(' ', extraIndent)); }
/// <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> /// 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 { 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> /// 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); } }
/// <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(); } }