public void uncommentRange( TextRange range, String commentPrefix, String commentSuffix, Commenter commenter) { if (commenter instanceof SelfManagingCommenter) { final SelfManagingCommenter selfManagingCommenter = (SelfManagingCommenter) commenter; selfManagingCommenter.uncommentBlockComment( range.getStartOffset(), range.getEndOffset(), myDocument, mySelfManagedCommenterData); return; } String text = myDocument .getCharsSequence() .subSequence(range.getStartOffset(), range.getEndOffset()) .toString(); int startOffset = range.getStartOffset(); // boolean endsProperly = CharArrayUtil.regionMatches(chars, range.getEndOffset() - // commentSuffix.length(), commentSuffix); List<Couple<TextRange>> ranges = new ArrayList<>(); if (commenter instanceof CustomUncommenter) { /* In case of custom uncommenter, we need to ask it for list of [commentOpen-start,commentOpen-end], [commentClose-start,commentClose-end] and shift if according to current offset */ CustomUncommenter customUncommenter = (CustomUncommenter) commenter; for (Couple<TextRange> coupleFromCommenter : customUncommenter.getCommentRangesToDelete(text)) { TextRange openComment = coupleFromCommenter.first.shiftRight(startOffset); TextRange closeComment = coupleFromCommenter.second.shiftRight(startOffset); ranges.add(Couple.of(openComment, closeComment)); } } else { // If commenter is not custom, we need to get this list by our selves int position = 0; while (true) { int start = getNearest(text, commentPrefix, position); if (start == text.length()) { break; } position = start; int end = getNearest(text, commentSuffix, position + commentPrefix.length()) + commentSuffix.length(); position = end; Couple<TextRange> pair = findCommentBlock( new TextRange(start + startOffset, end + startOffset), commentPrefix, commentSuffix); ranges.add(pair); } } RangeMarker marker = myDocument.createRangeMarker(range); try { for (int i = ranges.size() - 1; i >= 0; i--) { Couple<TextRange> toDelete = ranges.get(i); myDocument.deleteString(toDelete.first.getStartOffset(), toDelete.first.getEndOffset()); int shift = toDelete.first.getEndOffset() - toDelete.first.getStartOffset(); myDocument.deleteString( toDelete.second.getStartOffset() - shift, toDelete.second.getEndOffset() - shift); if (commenter.getCommentedBlockCommentPrefix() != null) { commentNestedComments( myDocument, new TextRange( toDelete.first.getEndOffset() - shift, toDelete.second.getStartOffset() - shift), commenter); } } processDocument(myDocument, marker, commenter, false); } finally { marker.dispose(); } }
private TextRange insertNestedComments( int startOffset, int endOffset, String commentPrefix, String commentSuffix, Commenter commenter) { if (commenter instanceof SelfManagingCommenter) { final SelfManagingCommenter selfManagingCommenter = (SelfManagingCommenter) commenter; return selfManagingCommenter.insertBlockComment( startOffset, endOffset, myDocument, mySelfManagedCommenterData); } String normalizedPrefix = commentPrefix.trim(); String normalizedSuffix = commentSuffix.trim(); IntArrayList nestedCommentPrefixes = new IntArrayList(); IntArrayList nestedCommentSuffixes = new IntArrayList(); String commentedPrefix = commenter.getCommentedBlockCommentPrefix(); String commentedSuffix = commenter.getCommentedBlockCommentSuffix(); CharSequence chars = myDocument.getCharsSequence(); for (int i = startOffset; i < endOffset; ++i) { if (CharArrayUtil.regionMatches(chars, i, normalizedPrefix)) { nestedCommentPrefixes.add(i); } else { if (CharArrayUtil.regionMatches(chars, i, normalizedSuffix)) { nestedCommentSuffixes.add(i); } } } int shift = 0; if (!(commentedSuffix == null && !nestedCommentSuffixes.isEmpty() && nestedCommentSuffixes.get(nestedCommentSuffixes.size() - 1) + commentSuffix.length() == endOffset)) { myDocument.insertString(endOffset, commentSuffix); shift += commentSuffix.length(); } // process nested comments in back order int i = nestedCommentPrefixes.size() - 1; int j = nestedCommentSuffixes.size() - 1; final TextRange selection = new TextRange(startOffset, endOffset); while (i >= 0 && j >= 0) { final int prefixIndex = nestedCommentPrefixes.get(i); final int suffixIndex = nestedCommentSuffixes.get(j); if (prefixIndex > suffixIndex) { shift += doBoundCommentingAndGetShift( prefixIndex, commentedPrefix, normalizedPrefix.length(), commentSuffix, false, selection); --i; } else { // if (insertPos < myDocument.getTextLength() && // Character.isWhitespace(myDocument.getCharsSequence().charAt(insertPos))) { // insertPos = suffixIndex + commentSuffix.length(); // } shift += doBoundCommentingAndGetShift( suffixIndex, commentedSuffix, normalizedSuffix.length(), commentPrefix, true, selection); --j; } } while (i >= 0) { final int prefixIndex = nestedCommentPrefixes.get(i); shift += doBoundCommentingAndGetShift( prefixIndex, commentedPrefix, normalizedPrefix.length(), commentSuffix, false, selection); --i; } while (j >= 0) { final int suffixIndex = nestedCommentSuffixes.get(j); shift += doBoundCommentingAndGetShift( suffixIndex, commentedSuffix, normalizedSuffix.length(), commentPrefix, true, selection); --j; } if (!(commentedPrefix == null && !nestedCommentPrefixes.isEmpty() && nestedCommentPrefixes.get(0) == startOffset)) { myDocument.insertString(startOffset, commentPrefix); shift += commentPrefix.length(); } RangeMarker marker = myDocument.createRangeMarker(startOffset, endOffset + shift); try { return processDocument(myDocument, marker, commenter, true); } finally { marker.dispose(); } }
@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; }