@Nullable private static String getPropertyName(@NotNull Document document, int line) { int startOffset = document.getLineStartOffset(line); int endOffset = StringUtil.indexOf( document.getCharsSequence(), '=', startOffset, document.getLineEndOffset(line)); if (endOffset <= startOffset) { return null; } String propertyName = document.getCharsSequence().subSequence(startOffset, endOffset).toString().trim(); return propertyName.isEmpty() ? null : propertyName; }
private void altCommitToOriginal(@NotNull DocumentEvent e) { final PsiFile origPsiFile = PsiDocumentManager.getInstance(myProject).getPsiFile(myOrigDocument); String newText = myNewDocument.getText(); // prepare guarded blocks LinkedHashMap<String, String> replacementMap = new LinkedHashMap<String, String>(); int count = 0; for (RangeMarker o : ContainerUtil.reverse(((DocumentEx) myNewDocument).getGuardedBlocks())) { String replacement = o.getUserData(REPLACEMENT_KEY); String tempText = "REPLACE" + (count++) + Long.toHexString(StringHash.calc(replacement)); newText = newText.substring(0, o.getStartOffset()) + tempText + newText.substring(o.getEndOffset()); replacementMap.put(tempText, replacement); } // run preformat processors final int hostStartOffset = myAltFullRange.getStartOffset(); myEditor.getCaretModel().moveToOffset(hostStartOffset); for (CopyPastePreProcessor preProcessor : Extensions.getExtensions(CopyPastePreProcessor.EP_NAME)) { newText = preProcessor.preprocessOnPaste(myProject, origPsiFile, myEditor, newText, null); } myOrigDocument.replaceString(hostStartOffset, myAltFullRange.getEndOffset(), newText); // replace temp strings for guarded blocks for (String tempText : replacementMap.keySet()) { int idx = CharArrayUtil.indexOf( myOrigDocument.getCharsSequence(), tempText, hostStartOffset, myAltFullRange.getEndOffset()); myOrigDocument.replaceString(idx, idx + tempText.length(), replacementMap.get(tempText)); } // JAVA: fix occasional char literal concatenation fixDocumentQuotes(myOrigDocument, hostStartOffset - 1); fixDocumentQuotes(myOrigDocument, myAltFullRange.getEndOffset()); // reformat PsiDocumentManager.getInstance(myProject).commitDocument(myOrigDocument); Runnable task = () -> { try { CodeStyleManager.getInstance(myProject) .reformatRange(origPsiFile, hostStartOffset, myAltFullRange.getEndOffset(), true); } catch (IncorrectOperationException e1) { // LOG.error(e); } }; DocumentUtil.executeInBulk(myOrigDocument, true, task); PsiElement newInjected = InjectedLanguageManager.getInstance(myProject) .findInjectedElementAt(origPsiFile, hostStartOffset); DocumentWindow documentWindow = newInjected == null ? null : InjectedLanguageUtil.getDocumentWindow(newInjected); if (documentWindow != null) { myEditor.getCaretModel().moveToOffset(documentWindow.injectedToHost(e.getOffset())); myEditor.getScrollingModel().scrollToCaret(ScrollType.MAKE_VISIBLE); } }
public void commentRange( int startOffset, int endOffset, String commentPrefix, String commentSuffix, Commenter commenter) { final CharSequence chars = myDocument.getCharsSequence(); LogicalPosition caretPosition = myCaret.getLogicalPosition(); if (startOffset == 0 || chars.charAt(startOffset - 1) == '\n') { if (endOffset == myDocument.getTextLength() || endOffset > 0 && chars.charAt(endOffset - 1) == '\n') { CodeStyleManager codeStyleManager = CodeStyleManager.getInstance(myProject); CommonCodeStyleSettings settings = CodeStyleSettingsManager.getSettings(myProject).getCommonSettings(myFile.getLanguage()); String space; if (!settings.BLOCK_COMMENT_AT_FIRST_COLUMN) { final FileType fileType = myFile.getFileType(); int line1 = myEditor.offsetToLogicalPosition(startOffset).line; int line2 = myEditor.offsetToLogicalPosition(endOffset - 1).line; Indent minIndent = CommentUtil.getMinLineIndent(myProject, myDocument, line1, line2, fileType); if (minIndent == null) { minIndent = codeStyleManager.zeroIndent(); } space = codeStyleManager.fillIndent(minIndent, fileType); } else { space = ""; } final StringBuilder nestingPrefix = new StringBuilder(space).append(commentPrefix); if (!commentPrefix.endsWith("\n")) { nestingPrefix.append("\n"); } final StringBuilder nestingSuffix = new StringBuilder(space); nestingSuffix.append( commentSuffix.startsWith("\n") ? commentSuffix.substring(1) : commentSuffix); nestingSuffix.append("\n"); TextRange range = insertNestedComments( startOffset, endOffset, nestingPrefix.toString(), nestingSuffix.toString(), commenter); myCaret.setSelection(range.getStartOffset(), range.getEndOffset()); LogicalPosition pos = new LogicalPosition(caretPosition.line + 1, caretPosition.column); myCaret.moveToLogicalPosition(pos); return; } } TextRange range = insertNestedComments(startOffset, endOffset, commentPrefix, commentSuffix, commenter); myCaret.setSelection(range.getStartOffset(), range.getEndOffset()); LogicalPosition pos = new LogicalPosition(caretPosition.line, caretPosition.column + commentPrefix.length()); myCaret.moveToLogicalPosition(pos); }
@Nullable private 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 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 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 static String getNewText(PsiElement elt) { Project project = elt.getProject(); PsiFile psiFile = getContainingFile(elt); final Document doc = PsiDocumentManager.getInstance(project).getDocument(psiFile); if (doc == null) return null; final ImplementationTextSelectioner implementationTextSelectioner = LanguageImplementationTextSelectioner.INSTANCE.forLanguage(elt.getLanguage()); int start = implementationTextSelectioner.getTextStartOffset(elt); final int end = implementationTextSelectioner.getTextEndOffset(elt); final int lineStart = doc.getLineStartOffset(doc.getLineNumber(start)); final int lineEnd = end < doc.getTextLength() ? doc.getLineEndOffset(doc.getLineNumber(end)) : doc.getTextLength(); return doc.getCharsSequence().subSequence(lineStart, lineEnd).toString(); }
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 static void fixDocumentQuotes(Document doc, int offset) { if (doc.getCharsSequence().charAt(offset) == '\'') { doc.replaceString(offset, offset + 1, "\""); } }
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 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; }
@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); } }
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(); } }
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(); 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 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(); } }
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 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; }