/// <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));
        }
예제 #2
0
        /// <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));
        }
예제 #3
0
        /// <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);
            }
예제 #6
0
        /// <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);
            }
        }
예제 #7
0
        /// <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();
            }
        }