public void commentRange( int startOffset, int endOffset, String commentPrefix, String commentSuffix, Commenter commenter) { final CharSequence chars = myDocument.getCharsSequence(); LogicalPosition caretPosition = myCaret.getLogicalPosition(); if (startOffset == 0 || chars.charAt(startOffset - 1) == '\n') { if (endOffset == myDocument.getTextLength() || endOffset > 0 && chars.charAt(endOffset - 1) == '\n') { CodeStyleManager codeStyleManager = CodeStyleManager.getInstance(myProject); CommonCodeStyleSettings settings = CodeStyleSettingsManager.getSettings(myProject).getCommonSettings(myFile.getLanguage()); String space; if (!settings.BLOCK_COMMENT_AT_FIRST_COLUMN) { final FileType fileType = myFile.getFileType(); int line1 = myEditor.offsetToLogicalPosition(startOffset).line; int line2 = myEditor.offsetToLogicalPosition(endOffset - 1).line; Indent minIndent = CommentUtil.getMinLineIndent(myProject, myDocument, line1, line2, fileType); if (minIndent == null) { minIndent = codeStyleManager.zeroIndent(); } space = codeStyleManager.fillIndent(minIndent, fileType); } else { space = ""; } final StringBuilder nestingPrefix = new StringBuilder(space).append(commentPrefix); if (!commentPrefix.endsWith("\n")) { nestingPrefix.append("\n"); } final StringBuilder nestingSuffix = new StringBuilder(space); nestingSuffix.append( commentSuffix.startsWith("\n") ? commentSuffix.substring(1) : commentSuffix); nestingSuffix.append("\n"); TextRange range = insertNestedComments( startOffset, endOffset, nestingPrefix.toString(), nestingSuffix.toString(), commenter); myCaret.setSelection(range.getStartOffset(), range.getEndOffset()); LogicalPosition pos = new LogicalPosition(caretPosition.line + 1, caretPosition.column); myCaret.moveToLogicalPosition(pos); return; } } TextRange range = insertNestedComments(startOffset, endOffset, commentPrefix, commentSuffix, commenter); myCaret.setSelection(range.getStartOffset(), range.getEndOffset()); LogicalPosition pos = new LogicalPosition(caretPosition.line, caretPosition.column + commentPrefix.length()); myCaret.moveToLogicalPosition(pos); }
@Nullable private TextRange getSelectedComments(CharSequence text, String prefix, String suffix) { TextRange commentedRange = null; if (myCaret.hasSelection()) { int selectionStart = myCaret.getSelectionStart(); selectionStart = CharArrayUtil.shiftForward(text, selectionStart, " \t\n"); int selectionEnd = myCaret.getSelectionEnd() - 1; selectionEnd = CharArrayUtil.shiftBackward(text, selectionEnd, " \t\n") + 1; if (selectionEnd - selectionStart >= prefix.length() + suffix.length() && CharArrayUtil.regionMatches(text, selectionStart, prefix) && CharArrayUtil.regionMatches(text, selectionEnd - suffix.length(), suffix)) { commentedRange = new TextRange(selectionStart, selectionEnd); } } return commentedRange; }
@Nullable private PsiElement findCommentAtCaret() { int offset = myCaret.getOffset(); TextRange range = new TextRange(myCaret.getSelectionStart(), myCaret.getSelectionEnd()); if (offset == range.getEndOffset()) { offset--; } if (offset <= range.getStartOffset()) { offset++; } PsiElement elt = myFile.getViewProvider().findElementAt(offset); if (elt == null) return null; PsiElement comment = PsiTreeUtil.getParentOfType(elt, PsiComment.class, false); if (comment == null || myCaret.hasSelection() && !range.contains(comment.getTextRange())) { return null; } return comment; }
private boolean testSelectionForNonComments() { if (!myCaret.hasSelection()) { return true; } TextRange range = new TextRange(myCaret.getSelectionStart(), myCaret.getSelectionEnd() - 1); for (PsiElement element = myFile.findElementAt(range.getStartOffset()); element != null && range.intersects(element.getTextRange()); element = element.getNextSibling()) { if (element instanceof OuterLanguageElement) { if (!isInjectedWhiteSpace(range, (OuterLanguageElement) element)) { return false; } } else { if (!isWhiteSpaceOrComment(element, range)) { return false; } } } return true; }
@Override public void invoke( @NotNull Project project, @NotNull Editor editor, @NotNull Caret caret, @NotNull PsiFile file) { if (!CodeInsightUtilBase.prepareEditorForWrite(editor)) return; myProject = project; myEditor = editor; myCaret = caret; myFile = file; myDocument = editor.getDocument(); if (!FileDocumentManager.getInstance().requestWriting(myDocument, project)) { return; } FeatureUsageTracker.getInstance().triggerFeatureUsed("codeassists.comment.block"); final Commenter commenter = findCommenter(myFile, myEditor, caret); if (commenter == null) return; final String prefix; final String suffix; if (commenter instanceof SelfManagingCommenter) { final SelfManagingCommenter selfManagingCommenter = (SelfManagingCommenter) commenter; mySelfManagedCommenterData = selfManagingCommenter.createBlockCommentingState( caret.getSelectionStart(), caret.getSelectionEnd(), myDocument, myFile); if (mySelfManagedCommenterData == null) { mySelfManagedCommenterData = SelfManagingCommenter.EMPTY_STATE; } prefix = selfManagingCommenter.getBlockCommentPrefix( caret.getSelectionStart(), myDocument, mySelfManagedCommenterData); suffix = selfManagingCommenter.getBlockCommentSuffix( caret.getSelectionEnd(), myDocument, mySelfManagedCommenterData); } else { prefix = commenter.getBlockCommentPrefix(); suffix = commenter.getBlockCommentSuffix(); } if (prefix == null || suffix == null) return; TextRange commentedRange = findCommentedRange(commenter); if (commentedRange != null) { final int commentStart = commentedRange.getStartOffset(); final int commentEnd = commentedRange.getEndOffset(); int selectionStart = commentStart; int selectionEnd = commentEnd; if (myCaret.hasSelection()) { selectionStart = myCaret.getSelectionStart(); selectionEnd = myCaret.getSelectionEnd(); } if ((commentStart < selectionStart || commentStart >= selectionEnd) && (commentEnd <= selectionStart || commentEnd > selectionEnd)) { commentRange(selectionStart, selectionEnd, prefix, suffix, commenter); } else { uncommentRange(commentedRange, trim(prefix), trim(suffix), commenter); } } else { if (myCaret.hasSelection()) { int selectionStart = myCaret.getSelectionStart(); int selectionEnd = myCaret.getSelectionEnd(); if (commenter instanceof IndentedCommenter) { final Boolean value = ((IndentedCommenter) commenter).forceIndentedLineComment(); if (value != null && value == Boolean.TRUE) { selectionStart = myDocument.getLineStartOffset(myDocument.getLineNumber(selectionStart)); selectionEnd = myDocument.getLineEndOffset(myDocument.getLineNumber(selectionEnd)); } } commentRange(selectionStart, selectionEnd, prefix, suffix, commenter); } else { EditorUtil.fillVirtualSpaceUntilCaret(editor); int caretOffset = myCaret.getOffset(); if (commenter instanceof IndentedCommenter) { final Boolean value = ((IndentedCommenter) commenter).forceIndentedLineComment(); if (value != null && value == Boolean.TRUE) { final int lineNumber = myDocument.getLineNumber(caretOffset); final int start = myDocument.getLineStartOffset(lineNumber); final int end = myDocument.getLineEndOffset(lineNumber); commentRange(start, end, prefix, suffix, commenter); return; } } myDocument.insertString(caretOffset, prefix + suffix); myCaret.moveToOffset(caretOffset + prefix.length()); } } }
@Nullable private TextRange findCommentedRange(final Commenter commenter) { final CharSequence text = myDocument.getCharsSequence(); final FileType fileType = myFile.getFileType(); if (fileType instanceof CustomSyntaxTableFileType) { Lexer lexer = new CustomFileTypeLexer(((CustomSyntaxTableFileType) fileType).getSyntaxTable()); final int caretOffset = myCaret.getOffset(); int commentStart = CharArrayUtil.lastIndexOf(text, commenter.getBlockCommentPrefix(), caretOffset); if (commentStart == -1) return null; lexer.start(text, commentStart, text.length()); if (lexer.getTokenType() == CustomHighlighterTokenType.MULTI_LINE_COMMENT && lexer.getTokenEnd() >= caretOffset) { return new TextRange(commentStart, lexer.getTokenEnd()); } return null; } final String prefix; final String suffix; // Custom uncommenter is able to find commented block inside of selected text final String selectedText = myCaret.getSelectedText(); if ((commenter instanceof CustomUncommenter) && selectedText != null) { final TextRange commentedRange = ((CustomUncommenter) commenter).findMaximumCommentedRange(selectedText); if (commentedRange == null) { return null; } // Uncommenter returns range relative to text start, so we need to shift it to make abosolute. return commentedRange.shiftRight(myCaret.getSelectionStart()); } if (commenter instanceof SelfManagingCommenter) { SelfManagingCommenter selfManagingCommenter = (SelfManagingCommenter) commenter; prefix = selfManagingCommenter.getBlockCommentPrefix( myCaret.getSelectionStart(), myDocument, mySelfManagedCommenterData); suffix = selfManagingCommenter.getBlockCommentSuffix( myCaret.getSelectionEnd(), myDocument, mySelfManagedCommenterData); } else { prefix = trim(commenter.getBlockCommentPrefix()); suffix = trim(commenter.getBlockCommentSuffix()); } if (prefix == null || suffix == null) return null; TextRange commentedRange; if (commenter instanceof SelfManagingCommenter) { commentedRange = ((SelfManagingCommenter) commenter) .getBlockCommentRange( myCaret.getSelectionStart(), myCaret.getSelectionEnd(), myDocument, mySelfManagedCommenterData); } else { if (!testSelectionForNonComments()) { return null; } commentedRange = getSelectedComments(text, prefix, suffix); } if (commentedRange == null) { PsiElement comment = findCommentAtCaret(); if (comment != null) { String commentText = comment.getText(); if (commentText.startsWith(prefix) && commentText.endsWith(suffix)) { commentedRange = comment.getTextRange(); } } } return commentedRange; }