blob: 9fc8492d176c0c71c827eca2f7a6e6a848ddbf44 [file] [log] [blame]
/*
* Copyright 2000-2013 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.editorActions;
import com.intellij.codeInsight.CodeInsightBundle;
import com.intellij.codeInsight.CodeInsightSettings;
import com.intellij.codeInsight.editorActions.enter.EnterHandlerDelegate;
import com.intellij.ide.DataManager;
import com.intellij.lang.*;
import com.intellij.lang.documentation.CodeDocumentationProvider;
import com.intellij.lang.documentation.CompositeDocumentationProvider;
import com.intellij.lang.documentation.DocumentationProvider;
import com.intellij.lexer.Lexer;
import com.intellij.openapi.actionSystem.CommonDataKeys;
import com.intellij.openapi.actionSystem.DataContext;
import com.intellij.openapi.actionSystem.PlatformDataKeys;
import com.intellij.openapi.command.CommandProcessor;
import com.intellij.openapi.diagnostic.Logger;
import com.intellij.openapi.editor.*;
import com.intellij.openapi.editor.actionSystem.EditorActionHandler;
import com.intellij.openapi.extensions.Extensions;
import com.intellij.openapi.project.Project;
import com.intellij.openapi.util.Ref;
import com.intellij.openapi.util.TextRange;
import com.intellij.openapi.util.text.StringUtil;
import com.intellij.psi.*;
import com.intellij.psi.codeStyle.CodeStyleManager;
import com.intellij.psi.codeStyle.CodeStyleSettings;
import com.intellij.psi.codeStyle.CodeStyleSettingsManager;
import com.intellij.psi.formatter.FormatterUtil;
import com.intellij.psi.impl.source.PostprocessReformattingAspect;
import com.intellij.psi.tree.IElementType;
import com.intellij.psi.util.PsiTreeUtil;
import com.intellij.psi.util.PsiUtilBase;
import com.intellij.util.IncorrectOperationException;
import com.intellij.util.text.CharArrayUtil;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
public class EnterHandler extends BaseEnterHandler {
private static final Logger LOG = Logger.getInstance("#com.intellij.codeInsight.editorActions.EnterHandler");
private final EditorActionHandler myOriginalHandler;
public EnterHandler(EditorActionHandler originalHandler) {
myOriginalHandler = originalHandler;
}
@Override
public boolean isEnabled(Editor editor, DataContext dataContext) {
return myOriginalHandler.isEnabled(editor, dataContext);
}
@Override
public void executeWriteAction(final Editor editor, final DataContext dataContext) {
final Project project = CommonDataKeys.PROJECT.getData(dataContext);
if (project != null && !project.isDefault()) {
PostprocessReformattingAspect.getInstance(project).disablePostprocessFormattingInside(new Runnable() {
@Override
public void run() {
executeWriteActionInner(editor, dataContext, project);
}
});
}
else {
executeWriteActionInner(editor, dataContext, project);
}
}
private void executeWriteActionInner(Editor editor, DataContext dataContext, Project project) {
CodeInsightSettings settings = CodeInsightSettings.getInstance();
if (project == null) {
myOriginalHandler.execute(editor, dataContext);
return;
}
final Document document = editor.getDocument();
final PsiFile file = PsiUtilBase.getPsiFileInEditor(editor, project);
if (file == null) {
myOriginalHandler.execute(editor, dataContext);
return;
}
CommandProcessor.getInstance().setCurrentCommandName(CodeInsightBundle.message("command.name.typing"));
EditorModificationUtil.deleteSelectedText(editor);
int caretOffset = editor.getCaretModel().getOffset();
CharSequence text = document.getCharsSequence();
int length = document.getTextLength();
if (caretOffset < length && text.charAt(caretOffset) != '\n') {
int offset1 = CharArrayUtil.shiftBackward(text, caretOffset, " \t");
if (offset1 < 0 || text.charAt(offset1) == '\n') {
int offset2 = CharArrayUtil.shiftForward(text, offset1 + 1, " \t");
boolean isEmptyLine = offset2 >= length || text.charAt(offset2) == '\n';
if (!isEmptyLine) { // we are in leading spaces of a non-empty line
myOriginalHandler.execute(editor, dataContext);
return;
}
}
}
final PsiDocumentManager documentManager = PsiDocumentManager.getInstance(project);
documentManager.commitDocument(document);
boolean forceIndent = false;
boolean forceSkipIndent = false;
Ref<Integer> caretOffsetRef = new Ref<Integer>(caretOffset);
Ref<Integer> caretAdvanceRef = new Ref<Integer>(0);
final EnterHandlerDelegate[] delegates = Extensions.getExtensions(EnterHandlerDelegate.EP_NAME);
for(EnterHandlerDelegate delegate: delegates) {
EnterHandlerDelegate.Result result = delegate.preprocessEnter(file, editor, caretOffsetRef, caretAdvanceRef, dataContext, myOriginalHandler);
if (caretOffsetRef.get() > document.getTextLength()) {
throw new AssertionError("Wrong caret offset change by " + delegate);
}
if (result == EnterHandlerDelegate.Result.Stop) {
return;
}
if (result != EnterHandlerDelegate.Result.Continue) {
if (result == EnterHandlerDelegate.Result.DefaultForceIndent) {
forceIndent = true;
}
else if (result == EnterHandlerDelegate.Result.DefaultSkipIndent) {
forceSkipIndent = true;
}
break;
}
}
text = document.getCharsSequence(); // update after changes done in preprocessEnter()
caretOffset = caretOffsetRef.get().intValue();
boolean isFirstColumn = caretOffset == 0 || text.charAt(caretOffset - 1) == '\n';
final boolean insertSpace =
!isFirstColumn && !(caretOffset >= text.length() || text.charAt(caretOffset) == ' ' || text.charAt(caretOffset) == '\t');
editor.getCaretModel().moveToOffset(caretOffset);
myOriginalHandler.execute(editor, dataContext);
if (!editor.isInsertMode() || forceSkipIndent) {
return;
}
if (settings.SMART_INDENT_ON_ENTER || forceIndent) {
caretOffset += 1;
caretOffset = CharArrayUtil.shiftForward(editor.getDocument().getCharsSequence(), caretOffset, " \t");
}
else {
caretOffset = editor.getCaretModel().getOffset();
}
documentManager.commitAllDocuments();
final DoEnterAction action = new DoEnterAction(
file, editor, document, dataContext, caretOffset, !insertSpace, caretAdvanceRef.get(), project
);
action.setForceIndent(forceIndent);
action.run();
documentManager.commitDocument(document);
for (EnterHandlerDelegate delegate : delegates) {
if (delegate.postProcessEnter(file, editor, dataContext) == EnterHandlerDelegate.Result.Stop) {
break;
}
}
documentManager.commitDocument(document);
}
private static boolean isCommentComplete(PsiComment comment, CodeDocumentationAwareCommenter commenter, Editor editor) {
for (CommentCompleteHandler handler : Extensions.getExtensions(CommentCompleteHandler.EP_NAME)) {
if (handler.isApplicable(comment, commenter)) {
return handler.isCommentComplete(comment, commenter, editor);
}
}
String commentText = comment.getText();
final boolean docComment = isDocComment(comment, commenter);
final String expectedCommentEnd = docComment ? commenter.getDocumentationCommentSuffix():commenter.getBlockCommentSuffix();
if (!commentText.endsWith(expectedCommentEnd)) return false;
final PsiFile containingFile = comment.getContainingFile();
final Language language = containingFile.getLanguage();
ParserDefinition parserDefinition = LanguageParserDefinitions.INSTANCE.forLanguage(language);
if (parserDefinition == null) {
return true;
}
Lexer lexer = parserDefinition.createLexer(containingFile.getProject());
final String commentPrefix = docComment? commenter.getDocumentationCommentPrefix() : commenter.getBlockCommentPrefix();
lexer.start(commentText, commentPrefix == null? 0 : commentPrefix.length(), commentText.length());
QuoteHandler fileTypeHandler = TypedHandler.getQuoteHandler(containingFile, editor);
JavaLikeQuoteHandler javaLikeQuoteHandler = fileTypeHandler instanceof JavaLikeQuoteHandler ?
(JavaLikeQuoteHandler)fileTypeHandler:null;
while (true) {
IElementType tokenType = lexer.getTokenType();
if (tokenType == null) {
return false;
}
if (javaLikeQuoteHandler != null &&
javaLikeQuoteHandler.getStringTokenTypes() != null &&
javaLikeQuoteHandler.getStringTokenTypes().contains(tokenType)) {
String text = commentText.substring(lexer.getTokenStart(), lexer.getTokenEnd());
int endOffset = comment.getTextRange().getEndOffset();
if (text.endsWith(expectedCommentEnd) &&
endOffset < containingFile.getTextLength() &&
containingFile.getText().charAt(endOffset) == '\n') {
return true;
}
}
if (tokenType == commenter.getDocumentationCommentTokenType() || tokenType == commenter.getBlockCommentTokenType()) {
return false;
}
if (tokenType == commenter.getLineCommentTokenType() && lexer.getTokenText().contains(commentPrefix)) {
return false;
}
if (lexer.getTokenEnd() == commentText.length()) {
if (tokenType == commenter.getLineCommentTokenType()) {
String prefix = commenter.getLineCommentPrefix();
lexer.start(commentText, lexer.getTokenStart() + (prefix == null ? 0 : prefix.length()), commentText.length());
lexer.advance();
continue;
}
else if (isInvalidPsi(comment)) {
return false;
}
return true;
}
lexer.advance();
}
}
/**
* There is a following possible use-case:
* <pre>
* <ul>
* <li>
* <b>Particular document has valid text:</b>
* <pre>
* [caret]
* class A {
* int foo() {
* return 1 *&#47;*comment*&#47; 1;
* }
* }
* </pre>
* </li>
* <li>
* <b>The user starts comment (inserts comment start symbols):</b>
* <pre>
* &#47;**[caret]
* class A {
* int foo() {
* return 1 *&#47;*comment*&#47; 1;
* }
* }
* </pre>
* </li>
* <li>The user presses <code>'enter'</code>;</li>
* </ul>
* </pre>
* We want to understand that doc comment is incomplete now, i.e. don't want to consider '*&#47;' before
* '*comment*&#47; 1;' as comment end. Current approach is to check if next PSI sibling to the current PSI comment is invalid.
* This method allows to perform such an examination.
*/
private static boolean isInvalidPsi(@NotNull PsiElement base) {
for (PsiElement current = base.getNextSibling(); current != null; current = current.getNextSibling()) {
if (current.getTextLength() != 0) {
return current instanceof PsiErrorElement;
}
}
return false;
}
private static boolean isDocComment(final PsiElement element, final CodeDocumentationAwareCommenter commenter) {
if (!(element instanceof PsiComment)) return false;
PsiComment comment = (PsiComment) element;
return commenter.isDocumentationComment(comment);
}
private static class DoEnterAction implements Runnable {
private final DataContext myDataContext;
private final PsiFile myFile;
private int myOffset;
private final Document myDocument;
private final boolean myInsertSpace;
private final Editor myEditor;
private final Project myProject;
private int myCaretAdvance;
private boolean myForceIndent = false;
private static final String LINE_SEPARATOR = "\n";
public DoEnterAction(PsiFile file, Editor view, Document document, DataContext dataContext, int offset, boolean insertSpace,
int caretAdvance, Project project)
{
myEditor = view;
myFile = file;
myDataContext = dataContext;
myOffset = offset;
myDocument = document;
myInsertSpace = insertSpace;
myCaretAdvance = caretAdvance;
myProject = project;
}
public void setForceIndent(boolean forceIndent) {
myForceIndent = forceIndent;
}
@Override
public void run() {
CaretModel caretModel = myEditor.getCaretModel();
try {
final CharSequence chars = myDocument.getCharsSequence();
int i = CharArrayUtil.shiftBackwardUntil(chars, myOffset - 1, LINE_SEPARATOR) - 1;
i = CharArrayUtil.shiftBackwardUntil(chars, i, LINE_SEPARATOR) + 1;
if (i < 0) i = 0;
int lineStart = CharArrayUtil.shiftForward(chars, i, " \t");
CodeDocumentationUtil.CommentContext commentContext
= CodeDocumentationUtil.tryParseCommentContext(myFile, chars, myOffset, lineStart);
PsiDocumentManager psiDocumentManager = PsiDocumentManager.getInstance(getProject());
if (commentContext.docStart) {
PsiElement element = myFile.findElementAt(commentContext.lineStart);
final String text = element.getText();
final PsiElement parent = element.getParent();
if (text.equals(commentContext.commenter.getDocumentationCommentPrefix()) && isDocComment(parent, commentContext.commenter) ||
text.startsWith(commentContext.commenter.getDocumentationCommentPrefix()) && element instanceof PsiComment)
{
PsiComment comment = isDocComment(parent, commentContext.commenter) ? (PsiComment)parent:(PsiComment)element;
int commentEnd = comment.getTextRange().getEndOffset();
if (myOffset >= commentEnd) {
commentContext.docStart = false;
}
else {
if (isCommentComplete(comment, commentContext.commenter, myEditor)) {
if (myOffset >= commentEnd) {
commentContext.docAsterisk = false;
commentContext.docStart = false;
}
else {
commentContext.docAsterisk = true;
commentContext.docStart = false;
}
}
else {
generateJavadoc(commentContext.commenter);
}
}
}
else {
commentContext.docStart = false;
}
}
else if (commentContext.cStyleStart) {
PsiElement element = myFile.findElementAt(commentContext.lineStart);
if (element instanceof PsiComment && commentContext.commenter.getBlockCommentTokenType() == ((PsiComment)element).getTokenType()) {
final PsiComment comment = (PsiComment)element;
int commentEnd = comment.getTextRange().getEndOffset();
if (myOffset >= commentEnd && myOffset < myFile.getTextRange().getEndOffset()) {
commentContext.docStart = false;
}
else {
if (isCommentComplete(comment, commentContext.commenter, myEditor)) {
if (myOffset >= commentEnd) {
commentContext.docAsterisk = false;
commentContext.docStart = false;
}
else {
commentContext.docAsterisk = true;
commentContext.docStart = false;
}
}
else {
final int currentEndOfLine = CharArrayUtil.shiftForwardUntil(chars, myOffset, "\n");
myDocument.insertString(currentEndOfLine, " " + commentContext.commenter.getBlockCommentSuffix());
int lstart = CharArrayUtil.shiftBackwardUntil(chars, myOffset, "\n");
myDocument.insertString(currentEndOfLine, chars.subSequence(lstart, myOffset));
psiDocumentManager.commitDocument(myDocument);
}
}
}
else {
commentContext.docStart = false;
}
}
String indentInsideJavadoc = null;
if (myOffset < myDocument.getTextLength()) {
final int line = myDocument.getLineNumber(myOffset);
if (line > 0 && (commentContext.docAsterisk || commentContext.docStart)) {
indentInsideJavadoc = CodeDocumentationUtil.getIndentInsideJavadoc(myDocument, myDocument.getLineStartOffset(line - 1));
}
}
if (commentContext.docAsterisk) {
commentContext.docAsterisk = insertDocAsterisk(commentContext.lineStart, commentContext.docAsterisk,
!StringUtil.isEmpty(indentInsideJavadoc), commentContext.commenter);
}
boolean docIndentApplied = false;
CodeInsightSettings codeInsightSettings = CodeInsightSettings.getInstance();
if (codeInsightSettings.SMART_INDENT_ON_ENTER || myForceIndent || commentContext.docStart || commentContext.docAsterisk
|| commentContext.slashSlash)
{
final CodeStyleManager codeStyleManager = CodeStyleManager.getInstance(getProject());
myOffset = codeStyleManager.adjustLineIndent(myFile, myOffset);
psiDocumentManager.commitAllDocuments();
if (!StringUtil.isEmpty(indentInsideJavadoc) && myOffset < myDocument.getTextLength()) {
myDocument.insertString(myOffset + 1, indentInsideJavadoc);
myOffset += indentInsideJavadoc.length();
docIndentApplied = true;
}
if (myForceIndent && indentInsideJavadoc != null) {
int indentSize = CodeStyleSettingsManager.getSettings(myProject).getIndentSize(myFile.getFileType());
myDocument.insertString(myOffset + 1, StringUtil.repeatSymbol(' ', indentSize));
myCaretAdvance += indentSize;
}
}
if ((commentContext.docAsterisk || commentContext.docStart || commentContext.slashSlash) && !docIndentApplied) {
if (myInsertSpace) {
if (myOffset == myDocument.getTextLength()) {
myDocument.insertString(myOffset, " ");
}
myDocument.insertString(myOffset + 1, " ");
}
final char c = myDocument.getCharsSequence().charAt(myOffset);
if (c != '\n') {
myOffset += 1;
}
}
if ((commentContext.docAsterisk || commentContext.slashSlash) && !commentContext.docStart) {
myCaretAdvance += commentContext.slashSlash ? commentContext.commenter.getLineCommentPrefix().length() : 1;
}
}
catch (IncorrectOperationException e) {
LOG.error(e);
}
myOffset = Math.min(myOffset, myDocument.getTextLength());
caretModel.moveToOffset(myOffset);
myEditor.getScrollingModel().scrollToCaret(ScrollType.RELATIVE);
myEditor.getSelectionModel().removeSelection();
if (myCaretAdvance != 0) {
LogicalPosition caretPosition = caretModel.getLogicalPosition();
LogicalPosition pos = new LogicalPosition(caretPosition.line, caretPosition.column + myCaretAdvance);
caretModel.moveToLogicalPosition(pos);
}
}
private void generateJavadoc(CodeDocumentationAwareCommenter commenter) throws IncorrectOperationException {
CodeInsightSettings settings = CodeInsightSettings.getInstance();
StringBuilder buffer = new StringBuilder();
final String docCommentLinePrefix = commenter.getDocumentationCommentLinePrefix();
if(docCommentLinePrefix==null){
return;
}
// There are at least two approaches for completing javadoc in case there is a text between current caret position and line end:
// 1. Move that tail text below the javadoc. Use-case:
// Before:
// /**<caret>public void foo() {}
// After:
// /**
// */
// public void foo() {}
// 2. Move the tail text inside the javadoc. Use-case:
// Before:
// /**This is <caret>javadoc description
// After:
// /** This is
// * javadoc description
// */
// The later is most relevant when we have 'auto wrap when typing reaches right margin' option set, i.e. user starts javadoc
// and types until right margin is reached. We want the wrapped text tail to be located inside javadoc and continue typing
// inside it. So, we have a control flow branch below that does the trick.
buffer.append(docCommentLinePrefix);
if (DataManager.getInstance().loadFromDataContext(myDataContext, AutoHardWrapHandler.AUTO_WRAP_LINE_IN_PROGRESS_KEY) == Boolean.TRUE) {
myDocument.insertString(myOffset, buffer);
// We create new buffer here because the one referenced by current 'buffer' variable value may be already referenced at another
// place (e.g. 'undo' processing stuff).
buffer = new StringBuilder(LINE_SEPARATOR).append(commenter.getDocumentationCommentSuffix());
int line = myDocument.getLineNumber(myOffset);
myOffset = myDocument.getLineEndOffset(line);
}
else {
buffer.append(LINE_SEPARATOR);
buffer.append(commenter.getDocumentationCommentSuffix());
}
PsiComment comment = createComment(buffer, settings);
if(comment == null){
return;
}
myOffset = comment.getTextRange().getStartOffset();
CharSequence text = myDocument.getCharsSequence();
myOffset = CharArrayUtil.shiftForwardUntil(text, myOffset, LINE_SEPARATOR);
myOffset = CharArrayUtil.shiftForward(text, myOffset, LINE_SEPARATOR);
myOffset = CharArrayUtil.shiftForwardUntil(text, myOffset, docCommentLinePrefix) + 1;
removeTrailingSpaces(myDocument, myOffset);
if (!CodeStyleSettingsManager.getSettings(getProject()).JD_LEADING_ASTERISKS_ARE_ENABLED) {
LOG.assertTrue(CharArrayUtil.regionMatches(myDocument.getCharsSequence(),myOffset - docCommentLinePrefix.length(), docCommentLinePrefix));
myDocument.deleteString(myOffset - docCommentLinePrefix.length(), myOffset);
myOffset--;
} else {
myDocument.insertString(myOffset, " ");
myOffset++;
}
PsiDocumentManager.getInstance(getProject()).commitAllDocuments();
}
@Nullable
private PsiComment createComment(final CharSequence buffer, final CodeInsightSettings settings)
throws IncorrectOperationException {
myDocument.insertString(myOffset, buffer);
PsiDocumentManager.getInstance(getProject()).commitAllDocuments();
CodeStyleManager.getInstance(getProject()).adjustLineIndent(myFile, myOffset + buffer.length() - 2);
PsiComment comment = PsiTreeUtil.getNonStrictParentOfType(myFile.findElementAt(myOffset), PsiComment.class);
comment = createJavaDocStub(settings, comment, getProject());
if (comment == null) {
return null;
}
CodeStyleManager codeStyleManager = CodeStyleManager.getInstance(getProject());
CodeStyleSettings codeStyleSettings = CodeStyleSettingsManager.getSettings(getProject());
boolean old = codeStyleSettings.ENABLE_JAVADOC_FORMATTING;
codeStyleSettings.ENABLE_JAVADOC_FORMATTING = false;
try {
comment = (PsiComment)codeStyleManager.reformat(comment);
}
finally {
codeStyleSettings.ENABLE_JAVADOC_FORMATTING = old;
}
PsiElement next = comment.getNextSibling();
if (next == null && comment.getParent().getClass() == comment.getClass()) {
next = comment.getParent().getNextSibling(); // expanding chameleon comment produces comment under comment
}
if (next != null) {
next = myFile.findElementAt(next.getTextRange().getStartOffset()); // maybe switch to another tree
}
if (next != null && (!FormatterUtil.containsWhiteSpacesOnly(next.getNode()) || !next.getText().contains(LINE_SEPARATOR))) {
int lineBreakOffset = comment.getTextRange().getEndOffset();
myDocument.insertString(lineBreakOffset, LINE_SEPARATOR);
PsiDocumentManager.getInstance(getProject()).commitAllDocuments();
codeStyleManager.adjustLineIndent(myFile, lineBreakOffset + 1);
comment = PsiTreeUtil.getNonStrictParentOfType(myFile.findElementAt(myOffset), PsiComment.class);
}
return comment;
}
@Nullable
private PsiComment createJavaDocStub(final CodeInsightSettings settings,
final PsiComment comment,
final Project project) {
if (settings.JAVADOC_STUB_ON_ENTER) {
final DocumentationProvider langDocumentationProvider =
LanguageDocumentation.INSTANCE.forLanguage(comment.getParent().getLanguage());
@Nullable final CodeDocumentationProvider docProvider;
if (langDocumentationProvider instanceof CompositeDocumentationProvider) {
docProvider = ((CompositeDocumentationProvider)langDocumentationProvider).getFirstCodeDocumentationProvider();
} else {
docProvider = langDocumentationProvider instanceof CodeDocumentationProvider ?
(CodeDocumentationProvider)langDocumentationProvider : null;
}
if (docProvider != null) {
if (docProvider.findExistingDocComment(comment) != comment) return comment;
String docStub = docProvider.generateDocumentationContentStub(comment);
if (docStub != null && docStub.length() != 0) {
myOffset = CharArrayUtil.shiftForwardUntil(myDocument.getCharsSequence(), myOffset, LINE_SEPARATOR);
myOffset = CharArrayUtil.shiftForward(myDocument.getCharsSequence(), myOffset, LINE_SEPARATOR);
myDocument.insertString(myOffset, docStub);
}
}
PsiDocumentManager.getInstance(project).commitAllDocuments();
return PsiTreeUtil.getNonStrictParentOfType(myFile.findElementAt(myOffset), PsiComment.class);
}
return comment;
}
private Project getProject() {
return myFile.getProject();
}
private static void removeTrailingSpaces(final Document document, final int startOffset) {
int endOffset = startOffset;
final CharSequence charsSequence = document.getCharsSequence();
for (int i = startOffset; i < charsSequence.length(); i++) {
final char c = charsSequence.charAt(i);
endOffset = i;
if (c == '\n') {
break;
}
if (c != ' ' && c != '\t') {
return;
}
}
document.deleteString(startOffset, endOffset);
}
private boolean insertDocAsterisk(int lineStart, boolean docAsterisk, boolean previousLineIndentUsed,
CodeDocumentationAwareCommenter commenter)
{
PsiElement atLineStart = myFile.findElementAt(lineStart);
if (atLineStart == null) return false;
final String linePrefix = commenter.getDocumentationCommentLinePrefix();
final String docPrefix = commenter.getDocumentationCommentPrefix();
final String text = atLineStart.getText();
final TextRange textRange = atLineStart.getTextRange();
if (text.equals(linePrefix) ||
text.equals(docPrefix) ||
docPrefix != null && text.regionMatches(lineStart - textRange.getStartOffset(), docPrefix, 0, docPrefix.length()) ||
linePrefix != null && text.regionMatches(lineStart - textRange.getStartOffset(), linePrefix, 0 , linePrefix.length()) ) {
PsiElement element = myFile.findElementAt(myOffset);
if (element == null) return false;
PsiComment comment = element instanceof PsiComment ? (PsiComment)element : PsiTreeUtil.getParentOfType(element, PsiComment.class, false);
if (comment != null) {
int commentEnd = comment.getTextRange().getEndOffset();
if (myOffset >= commentEnd) {
docAsterisk = false;
}
else {
removeTrailingSpaces(myDocument, myOffset);
String toInsert = previousLineIndentUsed ? "*" : CodeDocumentationUtil.createDocCommentLine("", getProject(), commenter);
myDocument.insertString(myOffset, toInsert);
PsiDocumentManager.getInstance(getProject()).commitAllDocuments();
}
}
else {
docAsterisk = false;
}
}
else if (linePrefix != null && atLineStart instanceof PsiComment && ((PsiComment)atLineStart).getTokenType() == commenter.getBlockCommentTokenType()) {
// Check if C-Style comment already uses asterisks.
boolean usesAstersk = false;
int commentLine = myDocument.getLineNumber(textRange.getStartOffset());
if (commentLine < myDocument.getLineCount() - 1 && textRange.getEndOffset() >= myOffset) {
int nextLineOffset = myDocument.getLineStartOffset(commentLine + 1);
if (nextLineOffset < textRange.getEndOffset()) {
final CharSequence chars = myDocument.getCharsSequence();
nextLineOffset = CharArrayUtil.shiftForward(chars, nextLineOffset, " \t");
usesAstersk = CharArrayUtil.regionMatches(chars, nextLineOffset, linePrefix);
}
}
if (usesAstersk) {
removeTrailingSpaces(myDocument, myOffset);
myDocument.insertString(myOffset, linePrefix + " ");
PsiDocumentManager.getInstance(getProject()).commitAllDocuments();
}
docAsterisk = usesAstersk;
}
else {
docAsterisk = false;
}
return docAsterisk;
}
}
}