blob: 3704425eb54de213e3dd42a2aeffd17624e631db [file] [log] [blame]
/*
* Copyright 2000-2014 JetBrains s.r.o.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.intellij.codeInsight.generation;
import com.intellij.codeInsight.CodeInsightUtilBase;
import com.intellij.codeInsight.CommentUtil;
import com.intellij.codeInsight.actions.MultiCaretCodeInsightActionHandler;
import com.intellij.featureStatistics.FeatureUsageTracker;
import com.intellij.ide.highlighter.custom.CustomFileTypeLexer;
import com.intellij.lang.Commenter;
import com.intellij.lang.Language;
import com.intellij.lang.LanguageCommenters;
import com.intellij.lexer.Lexer;
import com.intellij.openapi.editor.*;
import com.intellij.openapi.editor.ex.util.EditorUtil;
import com.intellij.openapi.fileEditor.FileDocumentManager;
import com.intellij.openapi.fileTypes.FileType;
import com.intellij.openapi.fileTypes.impl.AbstractFileType;
import com.intellij.openapi.fileTypes.impl.CustomSyntaxTableFileType;
import com.intellij.openapi.project.Project;
import com.intellij.openapi.util.Couple;
import com.intellij.openapi.util.TextRange;
import com.intellij.psi.*;
import com.intellij.psi.codeStyle.CodeStyleManager;
import com.intellij.psi.codeStyle.CodeStyleSettingsManager;
import com.intellij.psi.codeStyle.CommonCodeStyleSettings;
import com.intellij.psi.codeStyle.Indent;
import com.intellij.psi.templateLanguages.MultipleLangCommentProvider;
import com.intellij.psi.templateLanguages.OuterLanguageElement;
import com.intellij.psi.templateLanguages.TemplateLanguageFileViewProvider;
import com.intellij.psi.util.PsiTreeUtil;
import com.intellij.psi.util.PsiUtilBase;
import com.intellij.util.containers.IntArrayList;
import com.intellij.util.text.CharArrayUtil;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import java.util.ArrayList;
import java.util.List;
public class CommentByBlockCommentHandler extends MultiCaretCodeInsightActionHandler {
private Project myProject;
private Editor myEditor;
private Caret myCaret;
private @NotNull PsiFile myFile;
private Document myDocument;
private CommenterDataHolder mySelfManagedCommenterData;
@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 (myEditor.getSelectionModel().hasBlockSelection()) {
final LogicalPosition start = myEditor.getSelectionModel().getBlockStart();
final LogicalPosition end = myEditor.getSelectionModel().getBlockEnd();
assert start != null;
assert end != null;
int startColumn = Math.min(start.column, end.column);
int endColumn = Math.max(start.column, end.column);
int startLine = Math.min(start.line, end.line);
int endLine = Math.max(start.line, end.line);
for (int i = startLine; i <= endLine; i++) {
editor.getCaretModel().moveToLogicalPosition(new LogicalPosition(i, endColumn));
EditorModificationUtil.insertStringAtCaret(editor, suffix, true, true);
}
for (int i = startLine; i <= endLine; i++) {
editor.getCaretModel().moveToLogicalPosition(new LogicalPosition(i, startColumn));
EditorModificationUtil.insertStringAtCaret(editor, prefix, true, true);
}
}
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 static String trim(String s) {
return s == null ? null : s.trim();
}
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;
}
private boolean isInjectedWhiteSpace(@NotNull TextRange range, @NotNull OuterLanguageElement element) {
PsiElement psi = element.getContainingFile().getViewProvider().getPsi(element.getLanguage());
if (psi == null) {
return false;
}
List<PsiElement> injectedElements = PsiTreeUtil.getInjectedElements(element);
for (PsiElement el : injectedElements) {
if (!isWhiteSpaceOrComment(el, range)) {
return false;
}
}
return true;
}
private boolean isWhiteSpaceOrComment(@NotNull PsiElement element, @NotNull TextRange range) {
final TextRange textRange = element.getTextRange();
TextRange intersection = range.intersection(textRange);
if (intersection == null) {
return false;
}
intersection = TextRange.create(Math.max(intersection.getStartOffset() - textRange.getStartOffset(), 0),
Math.min(intersection.getEndOffset() - textRange.getStartOffset(), textRange.getLength()));
return isWhiteSpaceOrComment(element) ||
intersection.substring(element.getText()).trim().length() == 0;
}
private static boolean isWhiteSpaceOrComment(PsiElement element) {
return element instanceof PsiWhiteSpace ||
PsiTreeUtil.getParentOfType(element, PsiComment.class, false) != null;
}
@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;
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;
}
@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 static Commenter findCommenter(PsiFile file, Editor editor, Caret caret) {
final FileType fileType = file.getFileType();
if (fileType instanceof AbstractFileType) {
return ((AbstractFileType)fileType).getCommenter();
}
Language lang = PsiUtilBase.getLanguageInEditor(caret, file.getProject());
return getCommenter(file, editor, lang, lang);
}
@Nullable
public static Commenter getCommenter(final PsiFile file, final Editor editor,
final Language lineStartLanguage, final Language lineEndLanguage) {
final FileViewProvider viewProvider = file.getViewProvider();
for (MultipleLangCommentProvider provider : MultipleLangCommentProvider.EP_NAME.getExtensions()) {
if (provider.canProcess(file, viewProvider)) {
return provider.getLineCommenter(file, editor, lineStartLanguage, lineEndLanguage);
}
}
final Language fileLanguage = file.getLanguage();
Language lang = lineStartLanguage == null || LanguageCommenters.INSTANCE.forLanguage(lineStartLanguage) == null ||
fileLanguage.getBaseLanguage() == lineStartLanguage // file language is a more specific dialect of the line language
? fileLanguage
: lineStartLanguage;
if (viewProvider instanceof TemplateLanguageFileViewProvider &&
lang == ((TemplateLanguageFileViewProvider)viewProvider).getTemplateDataLanguage()) {
lang = viewProvider.getBaseLanguage();
}
return LanguageCommenters.INSTANCE.forLanguage(lang);
}
@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;
}
public void commentRange(int startOffset, int endOffset, String commentPrefix, String commentSuffix, Commenter commenter) {
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(chars, 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(chars, startOffset, endOffset, commentPrefix, commentSuffix, commenter);
myCaret.setSelection(range.getStartOffset(), range.getEndOffset());
LogicalPosition pos = new LogicalPosition(caretPosition.line, caretPosition.column + commentPrefix.length());
myCaret.moveToLogicalPosition(pos);
}
private int doBoundCommentingAndGetShift(int offset,
String commented,
int skipLength,
String toInsert,
boolean skipBrace,
TextRange selection) {
if (commented == null && (offset == selection.getStartOffset() || offset + (skipBrace ? skipLength : 0) == selection.getEndOffset())) {
return 0;
}
if (commented == null) {
myDocument.insertString(offset + (skipBrace ? skipLength : 0), toInsert);
return toInsert.length();
}
else {
myDocument.replaceString(offset, offset + skipLength, commented);
return commented.length() - skipLength;
}
}
private TextRange insertNestedComments(CharSequence chars,
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();
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();
}
return new TextRange(startOffset, endOffset + shift);
}
private static int getNearest(String text, String pattern, int position) {
int result = text.indexOf(pattern, position);
return result == -1 ? text.length() : result;
}
static void commentNestedComments(@NotNull Document document, TextRange range, Commenter commenter) {
final int offset = range.getStartOffset();
final IntArrayList toReplaceWithComments = new IntArrayList();
final IntArrayList prefixes = new IntArrayList();
if (range.getLength() < 0) return; // TODO: reproduce and fix this case
final String text = document.getCharsSequence().subSequence(range.getStartOffset(), range.getEndOffset()).toString();
final String commentedPrefix = commenter.getCommentedBlockCommentPrefix();
final String commentedSuffix = commenter.getCommentedBlockCommentSuffix();
final String commentPrefix = commenter.getBlockCommentPrefix();
final String commentSuffix = commenter.getBlockCommentSuffix();
int nearestSuffix = getNearest(text, commentedSuffix, 0);
int nearestPrefix = getNearest(text, commentedPrefix, 0);
int level = 0;
int lastSuffix = -1;
for (int i = Math.min(nearestPrefix, nearestSuffix); i < text.length(); i = Math.min(nearestPrefix, nearestSuffix)) {
if (i > nearestPrefix) {
nearestPrefix = getNearest(text, commentedPrefix, i);
continue;
}
if (i > nearestSuffix) {
nearestSuffix = getNearest(text, commentedSuffix, i);
continue;
}
if (i == nearestPrefix) {
if (level <= 0) {
if (lastSuffix != -1) {
toReplaceWithComments.add(lastSuffix);
}
level = 1;
lastSuffix = -1;
toReplaceWithComments.add(i);
prefixes.add(i);
}
else {
level++;
}
nearestPrefix = getNearest(text, commentedPrefix, nearestPrefix + 1);
}
else {
lastSuffix = i;
level--;
nearestSuffix = getNearest(text, commentedSuffix, nearestSuffix + 1);
}
}
if (lastSuffix != -1) {
toReplaceWithComments.add(lastSuffix);
}
int prefixIndex = prefixes.size() - 1;
for (int i = toReplaceWithComments.size() - 1; i >= 0; i--) {
int position = toReplaceWithComments.get(i);
if (prefixIndex >= 0 && position == prefixes.get(prefixIndex)) {
prefixIndex--;
document.replaceString(offset + position, offset + position + commentedPrefix.length(), commentPrefix);
}
else {
document.replaceString(offset + position, offset + position + commentedSuffix.length(), commentSuffix);
}
}
}
private TextRange expandRange(int delOffset1, int delOffset2) {
CharSequence chars = myDocument.getCharsSequence();
int offset1 = CharArrayUtil.shiftBackward(chars, delOffset1 - 1, " \t");
if (offset1 < 0 || chars.charAt(offset1) == '\n' || chars.charAt(offset1) == '\r') {
int offset2 = CharArrayUtil.shiftForward(chars, delOffset2, " \t");
if (offset2 == myDocument.getTextLength() || chars.charAt(offset2) == '\r' || chars.charAt(offset2) == '\n') {
delOffset1 = offset1 + 1;
if (offset2 < myDocument.getTextLength()) {
delOffset2 = offset2 + 1;
if (chars.charAt(offset2) == '\r' && offset2 + 1 < myDocument.getTextLength() && chars.charAt(offset2 + 1) == '\n') {
delOffset2++;
}
}
}
}
return new TextRange(delOffset1, delOffset2);
}
private Couple<TextRange> findCommentBlock(TextRange range, String commentPrefix, String commentSuffix) {
CharSequence chars = myDocument.getCharsSequence();
int startOffset = range.getStartOffset();
boolean endsProperly = CharArrayUtil.regionMatches(chars, range.getEndOffset() - commentSuffix.length(), commentSuffix);
TextRange start = expandRange(startOffset, startOffset + commentPrefix.length());
TextRange end;
if (endsProperly) {
end = expandRange(range.getEndOffset() - commentSuffix.length(), range.getEndOffset());
}
else {
end = new TextRange(range.getEndOffset(), range.getEndOffset());
}
return Couple.of(start, end);
}
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<Couple<TextRange>>();
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);
}
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);
}
}
}
}