summaryrefslogtreecommitdiff
path: root/platform/lang-impl/src/com/intellij/codeInsight/generation/CommentByLineCommentHandler.java
diff options
context:
space:
mode:
Diffstat (limited to 'platform/lang-impl/src/com/intellij/codeInsight/generation/CommentByLineCommentHandler.java')
-rw-r--r--platform/lang-impl/src/com/intellij/codeInsight/generation/CommentByLineCommentHandler.java623
1 files changed, 324 insertions, 299 deletions
diff --git a/platform/lang-impl/src/com/intellij/codeInsight/generation/CommentByLineCommentHandler.java b/platform/lang-impl/src/com/intellij/codeInsight/generation/CommentByLineCommentHandler.java
index ba73ac14d715..d62f51bb2e02 100644
--- a/platform/lang-impl/src/com/intellij/codeInsight/generation/CommentByLineCommentHandler.java
+++ b/platform/lang-impl/src/com/intellij/codeInsight/generation/CommentByLineCommentHandler.java
@@ -1,5 +1,5 @@
/*
- * Copyright 2000-2013 JetBrains s.r.o.
+ * 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.
@@ -16,12 +16,13 @@
package com.intellij.codeInsight.generation;
-import com.intellij.codeInsight.CodeInsightActionHandler;
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.SyntaxTable;
import com.intellij.injected.editor.EditorWindow;
+import com.intellij.injected.editor.InjectedCaret;
import com.intellij.lang.Commenter;
import com.intellij.lang.Language;
import com.intellij.lang.LanguageCommenters;
@@ -38,7 +39,6 @@ import com.intellij.openapi.util.Comparing;
import com.intellij.openapi.util.TextRange;
import com.intellij.openapi.util.registry.Registry;
import com.intellij.openapi.util.text.StringUtil;
-import com.intellij.psi.PsiDocumentManager;
import com.intellij.psi.PsiElement;
import com.intellij.psi.PsiFile;
import com.intellij.psi.codeStyle.CodeStyleManager;
@@ -53,181 +53,245 @@ import gnu.trove.THashMap;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
+import java.util.ArrayList;
+import java.util.List;
import java.util.Map;
-public class CommentByLineCommentHandler implements CodeInsightActionHandler {
-
+public class CommentByLineCommentHandler extends MultiCaretCodeInsightActionHandler {
private Project myProject;
- private PsiFile myFile;
- private Document myDocument;
- private Editor myEditor;
- private int myStartOffset;
- private int myEndOffset;
- private int myStartLine;
- private int myEndLine;
- private int[] myStartOffsets;
- private int[] myEndOffsets;
- private Commenter[] myCommenters;
- private Map<SelfManagingCommenter, CommenterDataHolder> myCommenterStateMap;
private CodeStyleManager myCodeStyleManager;
+ private final List<Block> myBlocks = new ArrayList<Block>();
+
@Override
- public void invoke(@NotNull Project project, @NotNull Editor editor, @NotNull PsiFile file) {
+ // first pass - adjacent carets are grouped into blocks
+ public void invoke(@NotNull Project project, @NotNull Editor editor, @NotNull Caret caret, @NotNull PsiFile file) {
if (!CodeInsightUtilBase.prepareEditorForWrite(editor)) return;
myProject = project;
- myFile = file.getViewProvider().getPsi(file.getViewProvider().getBaseLanguage());
- myEditor = editor;
+ file = file.getViewProvider().getPsi(file.getViewProvider().getBaseLanguage());
- PsiElement context = InjectedLanguageManager.getInstance(myFile.getProject()).getInjectionHost(myFile);
+ PsiElement context = InjectedLanguageManager.getInstance(file.getProject()).getInjectionHost(file);
if (context != null && (context.textContains('\'') || context.textContains('\"'))) {
String s = context.getText();
if (StringUtil.startsWith(s, "\"") || StringUtil.startsWith(s, "\'")) {
- myFile = context.getContainingFile();
- myEditor = editor instanceof EditorWindow ? ((EditorWindow)editor).getDelegate() : editor;
+ file = context.getContainingFile();
+ editor = editor instanceof EditorWindow ? ((EditorWindow)editor).getDelegate() : editor;
+ caret = caret instanceof InjectedCaret ? ((InjectedCaret)caret).getDelegate() : caret;
}
}
- myDocument = myEditor.getDocument();
- if (!FileDocumentManager.getInstance().requestWriting(myDocument, project)) {
+ Document document = editor.getDocument();
+ if (!FileDocumentManager.getInstance().requestWriting(document, project)) {
return;
}
- PsiDocumentManager.getInstance(project).commitDocument(myDocument);
-
- FeatureUsageTracker.getInstance().triggerFeatureUsed("codeassists.comment.line");
-
- myCodeStyleManager = CodeStyleManager.getInstance(myProject);
-
- final SelectionModel selectionModel = myEditor.getSelectionModel();
-
- boolean hasSelection = selectionModel.hasSelection();
- myStartOffset = selectionModel.getSelectionStart();
- myEndOffset = selectionModel.getSelectionEnd();
+ boolean hasSelection = caret.hasSelection();
+ int startOffset = caret.getSelectionStart();
+ int endOffset = caret.getSelectionEnd();
- FoldRegion fold = myEditor.getFoldingModel().getCollapsedRegionAtOffset(myStartOffset);
- if (fold != null && fold.shouldNeverExpand() && fold.getStartOffset() == myStartOffset && fold.getEndOffset() == myEndOffset) {
- // Foldings that never expand are automatically selected, so the fact it is selected must not interfer with commenter's logic
+ FoldRegion fold = editor.getFoldingModel().getCollapsedRegionAtOffset(startOffset);
+ if (fold != null && fold.shouldNeverExpand() && fold.getStartOffset() == startOffset && fold.getEndOffset() == endOffset) {
+ // Foldings that never expand are automatically selected, so the fact it is selected must not interfere with commenter's logic
hasSelection = false;
}
- if (myDocument.getTextLength() == 0) return;
+ if (document.getTextLength() == 0) return;
while (true) {
- int lastLineEnd = myDocument.getLineEndOffset(myDocument.getLineNumber(myEndOffset));
- FoldRegion collapsedAt = myEditor.getFoldingModel().getCollapsedRegionAtOffset(lastLineEnd);
+ int lastLineEnd = document.getLineEndOffset(document.getLineNumber(endOffset));
+ FoldRegion collapsedAt = editor.getFoldingModel().getCollapsedRegionAtOffset(lastLineEnd);
if (collapsedAt != null) {
- final int endOffset = collapsedAt.getEndOffset();
- if (endOffset <= myEndOffset) {
+ final int regionEndOffset = collapsedAt.getEndOffset();
+ if (regionEndOffset <= endOffset) {
break;
}
- myEndOffset = endOffset;
+ endOffset = regionEndOffset;
}
else {
break;
}
}
- boolean wholeLinesSelected = !hasSelection ||
- myStartOffset == myDocument.getLineStartOffset(myDocument.getLineNumber(myStartOffset)) &&
- myEndOffset == myDocument.getLineEndOffset(myDocument.getLineNumber(myEndOffset - 1)) + 1;
-
- boolean startingNewLineComment = !hasSelection && isLineEmpty(myDocument.getLineNumber(myStartOffset)) && !Comparing
- .equal(IdeActions.ACTION_COMMENT_LINE, ActionManagerEx.getInstanceEx().getPrevPreformedActionId());
- doComment();
-
- if (startingNewLineComment) {
- final Commenter commenter = myCommenters[0];
- if (commenter != null) {
- String prefix;
- if (commenter instanceof SelfManagingCommenter) {
- prefix = ((SelfManagingCommenter)commenter).getCommentPrefix(
- myStartLine,
- myDocument,
- myCommenterStateMap.get((SelfManagingCommenter)commenter)
- );
- if (prefix == null) prefix = ""; // TODO
- }
- else {
- prefix = commenter.getLineCommentPrefix();
- if (prefix == null) prefix = commenter.getBlockCommentPrefix();
- }
+ int startLine = document.getLineNumber(startOffset);
+ int endLine = document.getLineNumber(endOffset);
- int lineStart = myDocument.getLineStartOffset(myStartLine);
- lineStart = CharArrayUtil.shiftForward(myDocument.getCharsSequence(), lineStart, " \t");
- lineStart += prefix.length();
- lineStart = CharArrayUtil.shiftForward(myDocument.getCharsSequence(), lineStart, " \t");
- if (lineStart > myDocument.getTextLength()) lineStart = myDocument.getTextLength();
- myEditor.getCaretModel().moveToOffset(lineStart);
- myEditor.getScrollingModel().scrollToCaret(ScrollType.RELATIVE);
- }
+ if (endLine > startLine && document.getLineStartOffset(endLine) == endOffset) {
+ endLine--;
+ }
+
+ Block lastBlock = myBlocks.isEmpty() ? null : myBlocks.get(myBlocks.size() - 1);
+ Block currentBlock;
+ if (lastBlock == null || lastBlock.editor != editor || lastBlock.psiFile != file || endLine < (lastBlock.startLine - 1)) {
+ currentBlock = new Block();
+ currentBlock.editor = editor;
+ currentBlock.psiFile = file;
+ currentBlock.endLine = endLine;
+ myBlocks.add(currentBlock);
}
else {
- if (!hasSelection) {
- // Don't tweak caret position if we're already located on the last document line.
- LogicalPosition position = myEditor.getCaretModel().getLogicalPosition();
- if (position.line < myDocument.getLineCount() - 1) {
- int verticalShift = 1 + myEditor.getSoftWrapModel().getSoftWrapsForLine(position.line).size()
- - position.softWrapLinesOnCurrentLogicalLine;
- myEditor.getCaretModel().moveCaretRelatively(0, verticalShift, false, false, true);
- }
- }
- else {
- if (wholeLinesSelected) {
- selectionModel.setSelection(myStartOffset, selectionModel.getSelectionEnd());
- }
- }
+ currentBlock = lastBlock;
}
- }
+ currentBlock.carets.add(caret);
+ currentBlock.startLine = startLine;
- private boolean isLineEmpty(final int line) {
- final CharSequence chars = myDocument.getCharsSequence();
- int start = myDocument.getLineStartOffset(line);
- int end = Math.min(myDocument.getLineEndOffset(line), myDocument.getTextLength() - 1);
- for (int i = start; i <= end; i++) {
- if (!Character.isWhitespace(chars.charAt(i))) return false;
+ boolean wholeLinesSelected = !hasSelection ||
+ startOffset == document.getLineStartOffset(document.getLineNumber(startOffset)) &&
+ endOffset == document.getLineEndOffset(document.getLineNumber(endOffset - 1)) + 1;
+ boolean startingNewLineComment = !hasSelection
+ && isLineEmpty(document, document.getLineNumber(startOffset))
+ && !Comparing.equal(IdeActions.ACTION_COMMENT_LINE,
+ ActionManagerEx.getInstanceEx().getPrevPreformedActionId());
+ currentBlock.caretUpdate = startingNewLineComment ? CaretUpdate.PUT_AT_COMMENT_START :
+ !hasSelection ? CaretUpdate.SHIFT_DOWN :
+ wholeLinesSelected ? CaretUpdate.RESTORE_SELECTION : null;
}
- return true;
- }
@Override
- public boolean startInWriteAction() {
- return true;
- }
+ public void postInvoke() {
+ FeatureUsageTracker.getInstance().triggerFeatureUsed("codeassists.comment.line");
+
+ myCodeStyleManager = CodeStyleManager.getInstance(myProject);
- private void doComment() {
- myStartLine = myDocument.getLineNumber(myStartOffset);
- myEndLine = myDocument.getLineNumber(myEndOffset);
+ // second pass - determining whether we need to comment or to uncomment
+ boolean allLinesCommented = true;
+ for (Block block : myBlocks) {
+ int startLine = block.startLine;
+ int endLine = block.endLine;
+ Document document = block.editor.getDocument();
+ PsiFile psiFile = block.psiFile;
+
+ block.startOffsets = new int[endLine - startLine + 1];
+ block.endOffsets = new int[endLine - startLine + 1];
+ block.commenters = new Commenter[endLine - startLine + 1];
+ block.commenterStateMap = new THashMap<SelfManagingCommenter, CommenterDataHolder>();
+ CharSequence chars = document.getCharsSequence();
+
+ boolean singleline = startLine == endLine;
+ int offset = document.getLineStartOffset(startLine);
+ offset = CharArrayUtil.shiftForward(chars, offset, " \t");
- if (myEndLine > myStartLine && myDocument.getLineStartOffset(myEndLine) == myEndOffset) {
- myEndLine--;
- }
+ int endOffset = CharArrayUtil.shiftBackward(chars, document.getLineEndOffset(endLine), " \t\n");
- myStartOffsets = new int[myEndLine - myStartLine + 1];
- myEndOffsets = new int[myEndLine - myStartLine + 1];
- myCommenters = new Commenter[myEndLine - myStartLine + 1];
- myCommenterStateMap = new THashMap<SelfManagingCommenter, CommenterDataHolder>();
- CharSequence chars = myDocument.getCharsSequence();
+ block.blockSuitableCommenter = getBlockSuitableCommenter(psiFile, offset, endOffset);
+ block.commentWithIndent =
+ !CodeStyleSettingsManager.getSettings(myProject).getCommonSettings(psiFile.getLanguage()).LINE_COMMENT_AT_FIRST_COLUMN;
+
+ for (int line = startLine; line <= endLine; line++) {
+ Commenter commenter = block.blockSuitableCommenter != null ? block.blockSuitableCommenter : findCommenter(block.editor, psiFile, line);
+ if (commenter == null || commenter.getLineCommentPrefix() == null
+ && (commenter.getBlockCommentPrefix() == null || commenter.getBlockCommentSuffix() == null)) {
+ block.skip = true;
+ break;
+ }
- boolean singleline = myStartLine == myEndLine;
- int offset = myDocument.getLineStartOffset(myStartLine);
- offset = CharArrayUtil.shiftForward(myDocument.getCharsSequence(), offset, " \t");
+ if (commenter instanceof SelfManagingCommenter && block.commenterStateMap.get(commenter) == null) {
+ final SelfManagingCommenter selfManagingCommenter = (SelfManagingCommenter)commenter;
+ CommenterDataHolder state = selfManagingCommenter.createLineCommentingState(startLine, endLine, document, psiFile);
+ if (state == null) state = SelfManagingCommenter.EMPTY_STATE;
+ block.commenterStateMap.put(selfManagingCommenter, state);
+ }
- int endOffset = CharArrayUtil.shiftBackward(myDocument.getCharsSequence(), myDocument.getLineEndOffset(myEndLine), " \t\n");
+ block.commenters[line - startLine] = commenter;
+ if (!isLineCommented(block, line, commenter) && (singleline || !isLineEmpty(document, line))) {
+ allLinesCommented = false;
+ if (commenter instanceof IndentedCommenter) {
+ final Boolean value = ((IndentedCommenter)commenter).forceIndentedLineComment();
+ if (value != null) {
+ block.commentWithIndent = value;
+ }
+ }
+ break;
+ }
+ }
+ }
+ boolean moveCarets = true;
+ for (Block block : myBlocks) {
+ if (block.carets.size() > 1 && block.startLine != block.endLine) {
+ moveCarets = false;
+ break;
+ }
+ }
+ // third pass - actual change
+ for (Block block : myBlocks) {
+ if (!block.skip) {
+ if (!allLinesCommented) {
+ if (!block.commentWithIndent) {
+ doDefaultCommenting(block);
+ }
+ else {
+ doIndentCommenting(block);
+ }
+ }
+ else {
+ for (int line = block.endLine; line >= block.startLine; line--) {
+ uncommentLine(block, line);
+ }
+ }
+ }
+
+ if (!moveCarets || block.caretUpdate == null) {
+ continue;
+ }
+ Document document = block.editor.getDocument();
+ for (Caret caret : block.carets) {
+ switch (block.caretUpdate) {
+ case PUT_AT_COMMENT_START:
+ final Commenter commenter = block.commenters[0];
+ if (commenter != null) {
+ String prefix;
+ if (commenter instanceof SelfManagingCommenter) {
+ prefix = ((SelfManagingCommenter)commenter).getCommentPrefix(block.startLine,
+ document,
+ block.commenterStateMap.get((SelfManagingCommenter)commenter));
+ if (prefix == null) prefix = ""; // TODO
+ }
+ else {
+ prefix = commenter.getLineCommentPrefix();
+ if (prefix == null) prefix = commenter.getBlockCommentPrefix();
+ }
+
+ int lineStart = document.getLineStartOffset(block.startLine);
+ lineStart = CharArrayUtil.shiftForward(document.getCharsSequence(), lineStart, " \t");
+ lineStart += prefix.length();
+ lineStart = CharArrayUtil.shiftForward(document.getCharsSequence(), lineStart, " \t");
+ if (lineStart > document.getTextLength()) lineStart = document.getTextLength();
+ caret.moveToOffset(lineStart);
+ }
+ break;
+ case SHIFT_DOWN:
+ // Don't tweak caret position if we're already located on the last document line.
+ LogicalPosition position = caret.getLogicalPosition();
+ if (position.line < document.getLineCount() - 1) {
+ int verticalShift = 1 + block.editor.getSoftWrapModel().getSoftWrapsForLine(position.line).size()
+ - position.softWrapLinesOnCurrentLogicalLine;
+ caret.moveCaretRelatively(0, verticalShift, false, true);
+ }
+ break;
+ case RESTORE_SELECTION:
+ caret.setSelection(document.getLineStartOffset(document.getLineNumber(caret.getSelectionStart())), caret.getSelectionEnd());
+ }
+ }
+ }
+ }
+
+ private static Commenter getBlockSuitableCommenter(final PsiFile file, int offset, int endOffset) {
final Language languageSuitableForCompleteFragment;
if (offset >= endOffset) { // we are on empty line
- PsiElement element = myFile.findElementAt(offset);
+ PsiElement element = file.findElementAt(offset);
if (element != null) languageSuitableForCompleteFragment = element.getParent().getLanguage();
else languageSuitableForCompleteFragment = null;
- } else {
- languageSuitableForCompleteFragment = PsiUtilBase.reallyEvaluateLanguageInRange(offset, endOffset, myFile);
}
+ else {
+ languageSuitableForCompleteFragment = PsiUtilBase.reallyEvaluateLanguageInRange(offset, endOffset, file);
+ }
+
Commenter blockSuitableCommenter =
- languageSuitableForCompleteFragment == null ? LanguageCommenters.INSTANCE.forLanguage(myFile.getLanguage()) : null;
- if (blockSuitableCommenter == null && myFile.getFileType() instanceof CustomSyntaxTableFileType) {
+ languageSuitableForCompleteFragment == null ? LanguageCommenters.INSTANCE.forLanguage(file.getLanguage()) : null;
+ if (blockSuitableCommenter == null && file.getFileType() instanceof CustomSyntaxTableFileType) {
blockSuitableCommenter = new Commenter() {
- final SyntaxTable mySyntaxTable = ((CustomSyntaxTableFileType)myFile.getFileType()).getSyntaxTable();
+ final SyntaxTable mySyntaxTable = ((CustomSyntaxTableFileType)file.getFileType()).getSyntaxTable();
@Override
@Nullable
@@ -259,96 +323,30 @@ public class CommentByLineCommentHandler implements CodeInsightActionHandler {
};
}
- boolean allLineCommented = true;
- boolean commentWithIndent =
- !CodeStyleSettingsManager.getSettings(myProject).getCommonSettings(myFile.getLanguage()).LINE_COMMENT_AT_FIRST_COLUMN;
-
- for (int line = myStartLine; line <= myEndLine; line++) {
- Commenter commenter = blockSuitableCommenter != null ? blockSuitableCommenter : findCommenter(line);
- if (commenter == null) return;
- if (commenter.getLineCommentPrefix() == null &&
- (commenter.getBlockCommentPrefix() == null || commenter.getBlockCommentSuffix() == null)) {
- return;
- }
-
- if (commenter instanceof SelfManagingCommenter &&
- myCommenterStateMap.get(commenter) == null) {
- final SelfManagingCommenter selfManagingCommenter = (SelfManagingCommenter)commenter;
- CommenterDataHolder state =
- selfManagingCommenter.createLineCommentingState(myStartLine, myEndLine, myDocument, myFile);
- if (state == null) state = SelfManagingCommenter.EMPTY_STATE;
- myCommenterStateMap.put(selfManagingCommenter, state);
- }
-
- myCommenters[line - myStartLine] = commenter;
- if (!isLineCommented(line, chars, commenter) && (singleline || !isLineEmpty(line))) {
- allLineCommented = false;
- if (commenter instanceof IndentedCommenter) {
- final Boolean value = ((IndentedCommenter)commenter).forceIndentedLineComment();
- if (value != null) {
- commentWithIndent = value;
- }
- }
- break;
- }
- }
+ return blockSuitableCommenter;
+ }
- if (!allLineCommented) {
- if (!commentWithIndent) {
- doDefaultCommenting(blockSuitableCommenter);
- }
- else {
- doIndentCommenting(blockSuitableCommenter);
- }
- }
- else {
- for (int line = myEndLine; line >= myStartLine; line--) {
- uncommentLine(line);
- //int offset1 = myStartOffsets[line - myStartLine];
- //int offset2 = myEndOffsets[line - myStartLine];
- //if (offset1 == offset2) continue;
- //Commenter commenter = myCommenters[line - myStartLine];
- //String prefix = commenter.getBlockCommentPrefix();
- //if (prefix == null || !myDocument.getText().substring(offset1, myDocument.getTextLength()).startsWith(prefix)) {
- // prefix = commenter.getLineCommentPrefix();
- //}
- //
- //String suffix = commenter.getBlockCommentSuffix();
- //if (suffix == null && prefix != null) suffix = "";
- //
- //if (prefix != null && suffix != null) {
- // final int suffixLen = suffix.length();
- // final int prefixLen = prefix.length();
- // if (offset2 >= 0) {
- // if (!CharArrayUtil.regionMatches(chars, offset1 + prefixLen, prefix)) {
- // myDocument.deleteString(offset2 - suffixLen, offset2);
- // }
- // }
- // if (offset1 >= 0) {
- // for (int i = offset2 - suffixLen - 1; i > offset1 + prefixLen; --i) {
- // if (CharArrayUtil.regionMatches(chars, i, suffix)) {
- // myDocument.deleteString(i, i + suffixLen);
- // }
- // else if (CharArrayUtil.regionMatches(chars, i, prefix)) {
- // myDocument.deleteString(i, i + prefixLen);
- // }
- // }
- // myDocument.deleteString(offset1, offset1 + prefixLen);
- // }
- //}
- }
+ private static boolean isLineEmpty(Document document, final int line) {
+ final CharSequence chars = document.getCharsSequence();
+ int start = document.getLineStartOffset(line);
+ int end = Math.min(document.getLineEndOffset(line), document.getTextLength() - 1);
+ for (int i = start; i <= end; i++) {
+ if (!Character.isWhitespace(chars.charAt(i))) return false;
}
+ return true;
}
- private boolean isLineCommented(final int line, final CharSequence chars, final Commenter commenter) {
+ private static boolean isLineCommented(Block block, final int line, final Commenter commenter) {
boolean commented;
int lineEndForBlockCommenting = -1;
- int lineStart = myDocument.getLineStartOffset(line);
+ Document document = block.editor.getDocument();
+ int lineStart = document.getLineStartOffset(line);
+ CharSequence chars = document.getCharsSequence();
lineStart = CharArrayUtil.shiftForward(chars, lineStart, " \t");
if (commenter instanceof SelfManagingCommenter) {
final SelfManagingCommenter selfManagingCommenter = (SelfManagingCommenter)commenter;
- commented = selfManagingCommenter.isLineCommented(line, lineStart, myDocument, myCommenterStateMap.get(selfManagingCommenter));
+ commented = selfManagingCommenter.isLineCommented(line, lineStart, document, block.commenterStateMap.get(selfManagingCommenter));
}
else {
String prefix = commenter.getLineCommentPrefix();
@@ -359,8 +357,8 @@ public class CommentByLineCommentHandler implements CodeInsightActionHandler {
else {
prefix = commenter.getBlockCommentPrefix();
String suffix = commenter.getBlockCommentSuffix();
- final int textLength = myDocument.getTextLength();
- lineEndForBlockCommenting = myDocument.getLineEndOffset(line);
+ final int textLength = document.getTextLength();
+ lineEndForBlockCommenting = document.getLineEndOffset(line);
if (lineEndForBlockCommenting == textLength) {
final int shifted = CharArrayUtil.shiftBackward(chars, textLength - 1, " \t");
if (shifted < textLength - 1) lineEndForBlockCommenting = shifted;
@@ -368,59 +366,61 @@ public class CommentByLineCommentHandler implements CodeInsightActionHandler {
else {
lineEndForBlockCommenting = CharArrayUtil.shiftBackward(chars, lineEndForBlockCommenting, " \t");
}
- commented = lineStart == lineEndForBlockCommenting && myStartLine != myEndLine ||
+ commented = lineStart == lineEndForBlockCommenting && block.startLine != block.endLine ||
CharArrayUtil.regionMatches(chars, lineStart, prefix)
&& CharArrayUtil.regionMatches(chars, lineEndForBlockCommenting - suffix.length(), suffix);
}
}
if (commented) {
- myStartOffsets[line - myStartLine] = lineStart;
- myEndOffsets[line - myStartLine] = lineEndForBlockCommenting;
+ block.startOffsets[line - block.startLine] = lineStart;
+ block.endOffsets[line - block.startLine] = lineEndForBlockCommenting;
}
return commented;
}
@Nullable
- private Commenter findCommenter(final int line) {
- final FileType fileType = myFile.getFileType();
+ private static Commenter findCommenter(Editor editor, PsiFile file, final int line) {
+ final FileType fileType = file.getFileType();
if (fileType instanceof AbstractFileType) {
return ((AbstractFileType)fileType).getCommenter();
}
- int lineStartOffset = myDocument.getLineStartOffset(line);
- int lineEndOffset = myDocument.getLineEndOffset(line) - 1;
- final CharSequence charSequence = myDocument.getCharsSequence();
+ Document document = editor.getDocument();
+ int lineStartOffset = document.getLineStartOffset(line);
+ int lineEndOffset = document.getLineEndOffset(line) - 1;
+ final CharSequence charSequence = document.getCharsSequence();
lineStartOffset = CharArrayUtil.shiftForward(charSequence, lineStartOffset, " \t");
lineEndOffset = CharArrayUtil.shiftBackward(charSequence, lineEndOffset < 0 ? 0 : lineEndOffset, " \t");
- final Language lineStartLanguage = PsiUtilCore.getLanguageAtOffset(myFile, lineStartOffset);
- final Language lineEndLanguage = PsiUtilCore.getLanguageAtOffset(myFile, lineEndOffset);
- return CommentByBlockCommentHandler.getCommenter(myFile, myEditor, lineStartLanguage, lineEndLanguage);
+ final Language lineStartLanguage = PsiUtilCore.getLanguageAtOffset(file, lineStartOffset);
+ final Language lineEndLanguage = PsiUtilCore.getLanguageAtOffset(file, lineEndOffset);
+ return CommentByBlockCommentHandler.getCommenter(file, editor, lineStartLanguage, lineEndLanguage);
}
- private Indent computeMinIndent(int line1, int line2, CharSequence chars, CodeStyleManager codeStyleManager, FileType fileType) {
- Indent minIndent = CommentUtil.getMinLineIndent(myProject, myDocument, line1, line2, fileType);
+ private Indent computeMinIndent(Editor editor, PsiFile psiFile, int line1, int line2, FileType fileType) {
+ Document document = editor.getDocument();
+ Indent minIndent = CommentUtil.getMinLineIndent(myProject, document, line1, line2, fileType);
if (line1 > 0) {
- int commentOffset = getCommentStart(line1 - 1);
+ int commentOffset = getCommentStart(editor, psiFile, line1 - 1);
if (commentOffset >= 0) {
- int lineStart = myDocument.getLineStartOffset(line1 - 1);
- String space = chars.subSequence(lineStart, commentOffset).toString();
- Indent indent = codeStyleManager.getIndent(space, fileType);
+ int lineStart = document.getLineStartOffset(line1 - 1);
+ String space = document.getCharsSequence().subSequence(lineStart, commentOffset).toString();
+ Indent indent = myCodeStyleManager.getIndent(space, fileType);
minIndent = minIndent != null ? indent.min(minIndent) : indent;
}
}
if (minIndent == null) {
- minIndent = codeStyleManager.zeroIndent();
+ minIndent = myCodeStyleManager.zeroIndent();
}
return minIndent;
}
- private int getCommentStart(int line) {
- int offset = myDocument.getLineStartOffset(line);
- CharSequence chars = myDocument.getCharsSequence();
+ private static int getCommentStart(Editor editor, PsiFile psiFile, int line) {
+ int offset = editor.getDocument().getLineStartOffset(line);
+ CharSequence chars = editor.getDocument().getCharsSequence();
offset = CharArrayUtil.shiftForward(chars, offset, " \t");
- final Commenter commenter = findCommenter(line);
+ final Commenter commenter = findCommenter(editor, psiFile, line);
if (commenter == null) return -1;
String prefix = commenter.getLineCommentPrefix();
if (prefix == null) prefix = commenter.getBlockCommentPrefix();
@@ -428,53 +428,55 @@ public class CommentByLineCommentHandler implements CodeInsightActionHandler {
return CharArrayUtil.regionMatches(chars, offset, prefix) ? offset : -1;
}
- public void doDefaultCommenting(final Commenter commenter) {
+ public void doDefaultCommenting(final Block block) {
+ final Document document = block.editor.getDocument();
DocumentUtil.executeInBulk(
- myDocument, myEndLine - myStartLine >= Registry.intValue("comment.by.line.bulk.lines.trigger"), new Runnable() {
+ document, block.endLine - block.startLine >= Registry.intValue("comment.by.line.bulk.lines.trigger"), new Runnable() {
@Override
public void run() {
- for (int line = myEndLine; line >= myStartLine; line--) {
- int offset = myDocument.getLineStartOffset(line);
- commentLine(line, offset, commenter);
+ for (int line = block.endLine; line >= block.startLine; line--) {
+ int offset = document.getLineStartOffset(line);
+ commentLine(block, line, offset);
}
}
});
}
- private void doIndentCommenting(final Commenter commenter) {
- final CharSequence chars = myDocument.getCharsSequence();
- final FileType fileType = myFile.getFileType();
- final Indent minIndent = computeMinIndent(myStartLine, myEndLine, chars, myCodeStyleManager, fileType);
+ private void doIndentCommenting(final Block block) {
+ final Document document = block.editor.getDocument();
+ final CharSequence chars = document.getCharsSequence();
+ final FileType fileType = block.psiFile.getFileType();
+ final Indent minIndent = computeMinIndent(block.editor, block.psiFile, block.startLine, block.endLine, fileType);
DocumentUtil.executeInBulk(
- myDocument, myEndLine - myStartLine > Registry.intValue("comment.by.line.bulk.lines.trigger"), new Runnable() {
- @Override
- public void run() {
- for (int line = myEndLine; line >= myStartLine; line--) {
- int lineStart = myDocument.getLineStartOffset(line);
- int offset = lineStart;
- final StringBuilder buffer = new StringBuilder();
- while (true) {
- String space = buffer.toString();
- Indent indent = myCodeStyleManager.getIndent(space, fileType);
- if (indent.isGreaterThan(minIndent) || indent.equals(minIndent)) break;
- char c = chars.charAt(offset);
- if (c != ' ' && c != '\t') {
- String newSpace = myCodeStyleManager.fillIndent(minIndent, fileType);
- myDocument.replaceString(lineStart, offset, newSpace);
- offset = lineStart + newSpace.length();
- break;
+ document, block.endLine - block.startLine > Registry.intValue("comment.by.line.bulk.lines.trigger"), new Runnable() {
+ @Override
+ public void run() {
+ for (int line = block.endLine; line >= block.startLine; line--) {
+ int lineStart = document.getLineStartOffset(line);
+ int offset = lineStart;
+ final StringBuilder buffer = new StringBuilder();
+ while (true) {
+ String space = buffer.toString();
+ Indent indent = myCodeStyleManager.getIndent(space, fileType);
+ if (indent.isGreaterThan(minIndent) || indent.equals(minIndent)) break;
+ char c = chars.charAt(offset);
+ if (c != ' ' && c != '\t') {
+ String newSpace = myCodeStyleManager.fillIndent(minIndent, fileType);
+ document.replaceString(lineStart, offset, newSpace);
+ offset = lineStart + newSpace.length();
+ break;
+ }
+ buffer.append(c);
+ offset++;
}
- buffer.append(c);
- offset++;
+ commentLine(block, line, offset);
}
- commentLine(line, offset, commenter);
}
- }
- });
+ });
}
- private void uncommentRange(int startOffset, int endOffset, @NotNull Commenter commenter) {
+ private static void uncommentRange(Document document, int startOffset, int endOffset, @NotNull Commenter commenter) {
final String commentedSuffix = commenter.getCommentedBlockCommentSuffix();
final String commentedPrefix = commenter.getCommentedBlockCommentPrefix();
final String prefix = commenter.getBlockCommentPrefix();
@@ -482,52 +484,53 @@ public class CommentByLineCommentHandler implements CodeInsightActionHandler {
if (prefix == null || suffix == null) {
return;
}
- if (endOffset >= suffix.length() && CharArrayUtil.regionMatches(myDocument.getCharsSequence(), endOffset - suffix.length(), suffix)) {
- myDocument.deleteString(endOffset - suffix.length(), endOffset);
- endOffset = myDocument.getTextLength();
+ if (endOffset >= suffix.length() && CharArrayUtil.regionMatches(document.getCharsSequence(), endOffset - suffix.length(), suffix)) {
+ document.deleteString(endOffset - suffix.length(), endOffset);
+ endOffset = document.getTextLength();
}
if (commentedPrefix != null && commentedSuffix != null) {
- CommentByBlockCommentHandler.commentNestedComments(myDocument, new TextRange(startOffset, endOffset), commenter);
+ CommentByBlockCommentHandler.commentNestedComments(document, new TextRange(startOffset, endOffset), commenter);
}
- myDocument.deleteString(startOffset, startOffset + prefix.length());
+ document.deleteString(startOffset, startOffset + prefix.length());
}
- private void uncommentLine(int line) {
- Commenter commenter = myCommenters[line - myStartLine];
- if (commenter == null) commenter = findCommenter(line);
+ private static void uncommentLine(Block block, int line) {
+ Document document = block.editor.getDocument();
+ Commenter commenter = block.commenters[line - block.startLine];
+ if (commenter == null) commenter = findCommenter(block.editor, block.psiFile, line);
if (commenter == null) return;
- final int startOffset = myStartOffsets[line - myStartLine];
+ final int startOffset = block.startOffsets[line - block.startLine];
if (commenter instanceof SelfManagingCommenter) {
final SelfManagingCommenter selfManagingCommenter = (SelfManagingCommenter)commenter;
- selfManagingCommenter.uncommentLine(line, startOffset, myDocument, myCommenterStateMap.get(selfManagingCommenter));
+ selfManagingCommenter.uncommentLine(line, startOffset, document, block.commenterStateMap.get(selfManagingCommenter));
return;
}
- final int endOffset = myEndOffsets[line - myStartLine];
+ final int endOffset = block.endOffsets[line - block.startLine];
if (startOffset == endOffset) {
return;
}
String prefix = commenter.getLineCommentPrefix();
if (prefix != null) {
- CharSequence chars = myDocument.getCharsSequence();
+ CharSequence chars = document.getCharsSequence();
if (commenter instanceof CommenterWithLineSuffix) {
CommenterWithLineSuffix commenterWithLineSuffix = (CommenterWithLineSuffix)commenter;
String suffix = commenterWithLineSuffix.getLineCommentSuffix();
- int theEnd = endOffset > 0 ? endOffset : myDocument.getLineEndOffset(line);
+ int theEnd = endOffset > 0 ? endOffset : document.getLineEndOffset(line);
while (theEnd > startOffset && Character.isWhitespace(chars.charAt(theEnd - 1))) {
theEnd--;
}
- String lineText = myDocument.getText(new TextRange(startOffset, theEnd));
+ String lineText = document.getText(new TextRange(startOffset, theEnd));
if (lineText.indexOf(suffix) != -1) {
int start = startOffset + lineText.indexOf(suffix);
- myDocument.deleteString(start, start + suffix.length());
+ document.deleteString(start, start + suffix.length());
}
}
@@ -544,10 +547,10 @@ public class CommentByLineCommentHandler implements CodeInsightActionHandler {
charsToDelete++;
}
}
- myDocument.deleteString(startOffset, startOffset + charsToDelete);
+ document.deleteString(startOffset, startOffset + charsToDelete);
return;
}
- String text = myDocument.getCharsSequence().subSequence(startOffset, endOffset).toString();
+ String text = document.getCharsSequence().subSequence(startOffset, endOffset).toString();
prefix = commenter.getBlockCommentPrefix();
final String suffix = commenter.getBlockCommentSuffix();
@@ -575,45 +578,47 @@ public class CommentByLineCommentHandler implements CodeInsightActionHandler {
assert prefixes.size() == suffixes.size();
for (int i = prefixes.size() - 1; i >= 0; i--) {
- uncommentRange(startOffset + prefixes.get(i), Math.min(startOffset + suffixes.get(i) + suffix.length(), endOffset), commenter);
+ uncommentRange(document, startOffset + prefixes.get(i), Math.min(startOffset + suffixes.get(i) + suffix.length(), endOffset), commenter);
}
}
- private void commentLine(int line, int offset, @Nullable Commenter commenter) {
- if (commenter == null) commenter = findCommenter(line);
+ private static void commentLine(Block block, int line, int offset) {
+ Commenter commenter = block.blockSuitableCommenter;
+ Document document = block.editor.getDocument();
+ if (commenter == null) commenter = findCommenter(block.editor, block.psiFile, line);
if (commenter == null) return;
if (commenter instanceof SelfManagingCommenter) {
final SelfManagingCommenter selfManagingCommenter = (SelfManagingCommenter)commenter;
- selfManagingCommenter.commentLine(line, offset, myDocument, myCommenterStateMap.get(selfManagingCommenter));
+ selfManagingCommenter.commentLine(line, offset, document, block.commenterStateMap.get(selfManagingCommenter));
return;
}
String prefix = commenter.getLineCommentPrefix();
if (prefix != null) {
if (commenter instanceof CommenterWithLineSuffix) {
- int endOffset = myDocument.getLineEndOffset(line);
- endOffset = CharArrayUtil.shiftBackward(myDocument.getCharsSequence(), endOffset, " \t");
- int shiftedStartOffset = CharArrayUtil.shiftForward(myDocument.getCharsSequence(), offset, " \t");
+ int endOffset = document.getLineEndOffset(line);
+ endOffset = CharArrayUtil.shiftBackward(document.getCharsSequence(), endOffset, " \t");
+ int shiftedStartOffset = CharArrayUtil.shiftForward(document.getCharsSequence(), offset, " \t");
String lineSuffix = ((CommenterWithLineSuffix)commenter).getLineCommentSuffix();
- if (!CharArrayUtil.regionMatches(myDocument.getCharsSequence(), shiftedStartOffset, prefix)) {
- if (!CharArrayUtil.regionMatches(myDocument.getCharsSequence(), endOffset - lineSuffix.length(), lineSuffix)) {
- myDocument.insertString(endOffset, lineSuffix);
+ if (!CharArrayUtil.regionMatches(document.getCharsSequence(), shiftedStartOffset, prefix)) {
+ if (!CharArrayUtil.regionMatches(document.getCharsSequence(), endOffset - lineSuffix.length(), lineSuffix)) {
+ document.insertString(endOffset, lineSuffix);
}
- myDocument.insertString(offset, prefix);
+ document.insertString(offset, prefix);
}
}
else {
- myDocument.insertString(offset, prefix);
+ document.insertString(offset, prefix);
}
}
else {
prefix = commenter.getBlockCommentPrefix();
String suffix = commenter.getBlockCommentSuffix();
if (prefix == null || suffix == null) return;
- int endOffset = myDocument.getLineEndOffset(line);
- if (endOffset == offset && myStartLine != myEndLine) return;
- final int textLength = myDocument.getTextLength();
- final CharSequence chars = myDocument.getCharsSequence();
+ int endOffset = document.getLineEndOffset(line);
+ if (endOffset == offset && block.startLine != block.endLine) return;
+ final int textLength = document.getTextLength();
+ final CharSequence chars = document.getCharsSequence();
offset = CharArrayUtil.shiftForward(chars, offset, " \t");
if (endOffset == textLength) {
final int shifted = CharArrayUtil.shiftBackward(chars, textLength - 1, " \t");
@@ -623,7 +628,7 @@ public class CommentByLineCommentHandler implements CodeInsightActionHandler {
endOffset = CharArrayUtil.shiftBackward(chars, endOffset, " \t");
}
if (endOffset < offset ||
- offset == textLength - 1 && line != myDocument.getLineCount() - 1) {
+ offset == textLength - 1 && line != document.getLineCount() - 1) {
return;
}
final String text = chars.subSequence(offset, endOffset).toString();
@@ -653,7 +658,7 @@ public class CommentByLineCommentHandler implements CodeInsightActionHandler {
}
}
if (!(commentedSuffix == null && !suffixes.isEmpty() && offset + suffixes.get(suffixes.size() - 1) + suffix.length() >= endOffset)) {
- myDocument.insertString(endOffset, suffix);
+ document.insertString(endOffset, suffix);
}
int nearestPrefix = prefixes.size() - 1;
int nearestSuffix = suffixes.size() - 1;
@@ -662,26 +667,46 @@ public class CommentByLineCommentHandler implements CodeInsightActionHandler {
final int position = prefixes.get(nearestPrefix);
nearestPrefix--;
if (commentedPrefix != null) {
- myDocument.replaceString(offset + position, offset + position + prefix.length(), commentedPrefix);
+ document.replaceString(offset + position, offset + position + prefix.length(), commentedPrefix);
}
else if (position != 0) {
- myDocument.insertString(offset + position, suffix);
+ document.insertString(offset + position, suffix);
}
}
else {
final int position = suffixes.get(nearestSuffix);
nearestSuffix--;
if (commentedSuffix != null) {
- myDocument.replaceString(offset + position, offset + position + suffix.length(), commentedSuffix);
+ document.replaceString(offset + position, offset + position + suffix.length(), commentedSuffix);
}
else if (offset + position + suffix.length() < endOffset) {
- myDocument.insertString(offset + position + suffix.length(), prefix);
+ document.insertString(offset + position + suffix.length(), prefix);
}
}
}
if (!(commentedPrefix == null && !prefixes.isEmpty() && prefixes.get(0) == 0)) {
- myDocument.insertString(offset, prefix);
+ document.insertString(offset, prefix);
}
}
}
+
+ private static class Block {
+ private Editor editor;
+ private PsiFile psiFile;
+ private List<Caret> carets = new ArrayList<Caret>();
+ private int startLine;
+ private int endLine;
+ private int[] startOffsets;
+ private int[] endOffsets;
+ private Commenter blockSuitableCommenter;
+ private Commenter[] commenters;
+ private Map<SelfManagingCommenter, CommenterDataHolder> commenterStateMap;
+ private boolean commentWithIndent;
+ private CaretUpdate caretUpdate;
+ private boolean skip;
+ }
+
+ private enum CaretUpdate {
+ PUT_AT_COMMENT_START, SHIFT_DOWN, RESTORE_SELECTION
+ }
}