@Override public void handleInsert(InsertionContext context) { final Document document = context.getEditor().getDocument(); document.replaceString(context.getStartOffset(), context.getTailOffset(), ";"); final InsertionContext qualifierContext = CompletionUtil.emulateInsertion(context, context.getStartOffset(), myQualifier); OffsetKey oldStart = context.trackOffset(context.getStartOffset(), false); int start = CharArrayUtil.shiftForward( context.getDocument().getCharsSequence(), context.getStartOffset(), " \t\n"); if (shouldParenthesizeQualifier(context.getFile(), start, qualifierContext.getTailOffset())) { final String space = CodeStyleSettingsManager.getSettings(qualifierContext.getProject()) .SPACE_WITHIN_PARENTHESES ? " " : ""; document.insertString(start, "(" + space); document.insertString(qualifierContext.getTailOffset(), space + ")"); } final char atTail = document.getCharsSequence().charAt(context.getTailOffset() - 1); if (atTail != ';') { LOG.error( LogMessageEx.createEvent( "Unexpected character", "atTail=" + atTail + "\n" + "offset=" + context.getTailOffset() + "\n" + "item=" + this + "\n" + "item.class=" + this.getClass() + "\n" + DebugUtil.currentStackTrace(), AttachmentFactory.createAttachment(context.getDocument()))); } document.replaceString(context.getTailOffset() - 1, context.getTailOffset(), "."); CompletionUtil.emulateInsertion(getDelegate(), context.getTailOffset(), context); context.commitDocument(); int formatStart = context.getOffset(oldStart); int formatEnd = context.getTailOffset(); if (formatStart >= 0 && formatEnd >= 0) { CodeStyleManager.getInstance(context.getProject()) .reformatText(context.getFile(), formatStart, formatEnd); } }
@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; }
@Override public void doApply( @NotNull Editor editor, @NotNull PySmartEnterProcessor processor, @NotNull PyWithStatement withStatement) throws IncorrectOperationException { final PsiElement colonToken = PyUtil.getFirstChildOfType(withStatement, PyTokenTypes.COLON); final PsiElement withToken = PyUtil.getFirstChildOfType(withStatement, PyTokenTypes.WITH_KEYWORD); final Document document = editor.getDocument(); if (colonToken == null && withToken != null) { int insertAt = withToken.getTextRange().getEndOffset(); String textToInsert = ":"; final PyWithItem lastItem = ArrayUtil.getLastElement(withStatement.getWithItems()); if (lastItem == null || lastItem.getExpression() == null) { textToInsert = " :"; processor.registerUnresolvedError(insertAt + 1); } else { final PyExpression expression = lastItem.getExpression(); insertAt = expression.getTextRange().getEndOffset(); final PsiElement asToken = PyUtil.getFirstChildOfType(lastItem, PyTokenTypes.AS_KEYWORD); if (asToken != null) { insertAt = asToken.getTextRange().getEndOffset(); final PyExpression target = lastItem.getTarget(); if (target != null) { insertAt = target.getTextRange().getEndOffset(); } else { textToInsert = " :"; processor.registerUnresolvedError(insertAt + 1); } } } document.insertString(insertAt, textToInsert); } }
public void testSoftWrapsRecalculationInASpecificCase() throws Exception { configureFromFileText( getTestName(false) + ".java", "<selection>class Foo {\n" + "\t@Override\n" + "\tpublic boolean equals(Object other) {\n" + "\t\treturn this == other;\n" + "\t}\n" + "}</selection>"); CodeFoldingManager.getInstance(ourProject).buildInitialFoldings(myEditor); configureSoftWraps(32); // verify initial state assertEquals(4, EditorUtil.getTabSize(myEditor)); assertEquals( "[FoldRegion +(59:64), placeholder=' { ', FoldRegion +(85:88), placeholder=' }']", myEditor.getFoldingModel().toString()); verifySoftWrapPositions(52, 85); Document document = myEditor.getDocument(); for (int i = document.getLineCount() - 1; i >= 0; i--) { document.insertString(document.getLineStartOffset(i), "//"); } verifySoftWrapPositions(58, 93); }
@NotNull private static Document setupFileEditorAndDocument( @NotNull String fileName, @NotNull String fileText) throws IOException { EncodingProjectManager.getInstance(getProject()).setEncoding(null, CharsetToolkit.UTF8_CHARSET); EncodingProjectManager.getInstance(ProjectManager.getInstance().getDefaultProject()) .setEncoding(null, CharsetToolkit.UTF8_CHARSET); PostprocessReformattingAspect.getInstance(ourProject).doPostponedFormatting(); deleteVFile(); myVFile = getSourceRoot().createChildData(null, fileName); VfsUtil.saveText(myVFile, fileText); final FileDocumentManager manager = FileDocumentManager.getInstance(); final Document document = manager.getDocument(myVFile); assertNotNull("Can't create document for '" + fileName + "'", document); manager.reloadFromDisk(document); document.insertString(0, " "); document.deleteString(0, 1); myFile = getPsiManager().findFile(myVFile); assertNotNull( "Can't create PsiFile for '" + fileName + "'. Unknown file type most probably.", myFile); assertTrue(myFile.isPhysical()); myEditor = createEditor(myVFile); myVFile.setCharset(CharsetToolkit.UTF8_CHARSET); PsiDocumentManager.getInstance(getProject()).commitAllDocuments(); return document; }
@SuppressWarnings("ForLoopThatDoesntUseLoopVariable") private static void indentPlainTextBlock( final Document document, final int startOffset, final int endOffset, final int indentLevel) { CharSequence chars = document.getCharsSequence(); int spaceEnd = CharArrayUtil.shiftForward(chars, startOffset, " \t"); int line = document.getLineNumber(startOffset); if (spaceEnd > endOffset || indentLevel <= 0 || line >= document.getLineCount() - 1 || chars.charAt(spaceEnd) == '\n') { return; } int linesToAdjustIndent = 0; for (int i = line + 1; i < document.getLineCount(); i++) { if (document.getLineStartOffset(i) >= endOffset) { break; } linesToAdjustIndent++; } String indentString = StringUtil.repeatSymbol(' ', indentLevel); for (; linesToAdjustIndent > 0; linesToAdjustIndent--) { int lineStartOffset = document.getLineStartOffset(++line); document.insertString(lineStartOffset, indentString); } }
@Override public Result preprocessEnter( @NotNull final PsiFile file, @NotNull final Editor editor, @NotNull final Ref<Integer> caretOffsetRef, @NotNull final Ref<Integer> caretAdvance, @NotNull final DataContext dataContext, final EditorActionHandler originalHandler) { int caretOffset = caretOffsetRef.get().intValue(); PsiElement psiAtOffset = file.findElementAt(caretOffset); if (psiAtOffset != null && psiAtOffset.getTextOffset() < caretOffset) { ASTNode token = psiAtOffset.getNode(); Document document = editor.getDocument(); CharSequence text = document.getText(); final Language language = psiAtOffset.getLanguage(); final Commenter languageCommenter = LanguageCommenters.INSTANCE.forLanguage(language); final CodeDocumentationAwareCommenter commenter = languageCommenter instanceof CodeDocumentationAwareCommenter ? (CodeDocumentationAwareCommenter) languageCommenter : null; if (commenter != null && token.getElementType() == commenter.getLineCommentTokenType()) { final int offset = CharArrayUtil.shiftForward(text, caretOffset, " \t"); if (offset < document.getTextLength() && text.charAt(offset) != '\n') { String prefix = commenter.getLineCommentPrefix(); assert prefix != null : "Line Comment type is set but Line Comment Prefix is null!"; if (!StringUtil.startsWith(text, offset, prefix)) { if (text.charAt(caretOffset) != ' ' && !prefix.endsWith(" ")) { prefix += " "; } document.insertString(caretOffset, prefix); return Result.Default; } else { int afterPrefix = offset + prefix.length(); if (afterPrefix < document.getTextLength() && text.charAt(afterPrefix) != ' ') { document.insertString(afterPrefix, " "); // caretAdvance.set(0); } caretOffsetRef.set(offset); } return Result.Default; } } } return Result.Continue; }
public void testNestedAfter() { RangeMarker marker1 = createMarker("0[12345678]90123"); Document document = marker1.getDocument(); RangeMarker marker2 = document.createRangeMarker(2, 5); RangeMarker marker3 = document.createRangeMarker(3, 4); document.insertString(10, "x"); assertEquals(1, marker1.getStartOffset()); assertEquals(2, marker2.getStartOffset()); assertEquals(3, marker3.getStartOffset()); }
public static void emulateInsertion(LookupElement item, int offset, InsertionContext context) { setOffsets(context, offset, offset); final Editor editor = context.getEditor(); final Document document = editor.getDocument(); final String lookupString = item.getLookupString(); document.insertString(offset, lookupString); editor.getCaretModel().moveToOffset(context.getTailOffset()); PsiDocumentManager.getInstance(context.getProject()).commitDocument(document); item.handleInsert(context); }
@Override public void apply(Editor editor, JavaSmartEnterProcessor processor, PsiElement psiElement) throws IncorrectOperationException { if (psiElement instanceof PsiEnumConstant) { int insertionOffset = psiElement.getTextRange().getEndOffset(); Document doc = editor.getDocument(); final CharSequence text = doc.getCharsSequence(); final int probableCommaOffset = CharArrayUtil.shiftForward(text, insertionOffset, " \t"); if (probableCommaOffset >= text.length() || text.charAt(probableCommaOffset) != ',') { doc.insertString(insertionOffset, ","); } } }
private void insertString(VirtualFile file, int offset, @NotNull CharSequence s) throws InterruptedException { final Document document = FileDocumentManager.getInstance().getDocument(file); assertNotNull(document); AccessToken token = WriteAction.start(); try { document.insertString(offset, s); } finally { token.finish(); } semaphore.down(); PsiDocumentManager.getInstance(myProject).commitAllDocuments(); FileDocumentManager.getInstance().saveAllDocuments(); await(); }
public static RangeMarker insertTemporary( final int endOffset, final Document document, final String temporary) { final CharSequence chars = document.getCharsSequence(); final int length = chars.length(); final RangeMarker toDelete; if (endOffset < length && Character.isJavaIdentifierPart(chars.charAt(endOffset))) { document.insertString(endOffset, temporary); toDelete = document.createRangeMarker(endOffset, endOffset + 1); } else if (endOffset >= length) { toDelete = document.createRangeMarker(length, length); } else { toDelete = document.createRangeMarker(endOffset, endOffset); } toDelete.setGreedyToLeft(true); toDelete.setGreedyToRight(true); return toDelete; }
@Override public void actionPerformed(AnActionEvent e) { Project project = e.getProject(); if (project == null) { return; } for (StudyActionListener listener : Extensions.getExtensions(StudyActionListener.EP_NAME)) { listener.beforeCheck(e); } final AnswerPlaceholder answerPlaceholder = getAnswerPlaceholder(e); if (answerPlaceholder == null) { return; } StudyEditor studyEditor = StudyUtils.getSelectedStudyEditor(project); final StudyState studyState = new StudyState(studyEditor); if (answerPlaceholder.getTaskFile().getTask().hasSubtasks()) { StudySubtaskUtils.refreshPlaceholder(studyState.getEditor(), answerPlaceholder); return; } Document patternDocument = StudyUtils.getPatternDocument( answerPlaceholder.getTaskFile(), studyState.getVirtualFile().getName()); if (patternDocument == null) { return; } AnswerPlaceholder.MyInitialState initialState = answerPlaceholder.getInitialState(); int startOffset = initialState.getOffset(); final String text = patternDocument.getText(new TextRange(startOffset, startOffset + initialState.getLength())); CommandProcessor.getInstance() .executeCommand( project, () -> ApplicationManager.getApplication() .runWriteAction( () -> { Document document = studyState.getEditor().getDocument(); int offset = answerPlaceholder.getOffset(); document.deleteString(offset, offset + answerPlaceholder.getRealLength()); document.insertString(offset, text); }), NAME, null); }
private int doBoundCommentingAndGetShift( int offset, String commented, int skipLength, String toInsert, boolean skipBrace, TextRange selection) { if (commented == null && (offset == selection.getStartOffset() || offset + (skipBrace ? skipLength : 0) == selection.getEndOffset())) { return 0; } if (commented == null) { myDocument.insertString(offset + (skipBrace ? skipLength : 0), toInsert); return toInsert.length(); } else { myDocument.replaceString(offset, offset + skipLength, commented); return commented.length() - skipLength; } }
@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 doAddImport(GoFile file, Editor editor, String pathToImport) { GoImportDeclarations[] ids = file.getImportDeclarations(); Document document = editor.getDocument(); if (ids.length == 0) { addImportUnderPackage(file, document, pathToImport); return; } GoImportDeclarations importDeclarations = ids[ids.length - 1]; GoImportDeclaration[] imports = importDeclarations.getDeclarations(); if (imports.length == 0) { addImportUnderPackage(file, document, pathToImport); return; } GoImportDeclaration lastImport = imports[imports.length - 1]; PsiElement lastChild = getPrevSiblingIfItsWhiteSpaceOrComment(importDeclarations.getLastChild()); if (lastChild == null) { addImportUnderPackage(file, document, pathToImport); return; } if (")".equals(lastChild.getText())) { document.insertString(lastChild.getTextOffset(), "\"" + pathToImport + "\"\n"); int line = document.getLineNumber(lastChild.getTextOffset()); reformatLines(file, editor, line, line); } else { String oldImport = lastImport.getText(); int start = lastImport.getTextOffset(); int end = start + lastImport.getTextLength(); String declarations = String.format("(\n%s\n\"%s\"\n)", oldImport, pathToImport); document.replaceString(start, end, declarations); reformatPositions(file, start, start + declarations.length()); } }
public void testLazyRangeMarkers() { psiFile = createFile("x.txt", "xxx"); LazyRangeMarkerFactoryImpl factory = (LazyRangeMarkerFactoryImpl) LazyRangeMarkerFactory.getInstance(getProject()); VirtualFile virtualFile = psiFile.getVirtualFile(); LazyRangeMarkerFactoryImpl.LazyMarker marker = (LazyRangeMarkerFactoryImpl.LazyMarker) factory.createRangeMarker(virtualFile, 0); WeakList<LazyRangeMarkerFactoryImpl.LazyMarker> markers = LazyRangeMarkerFactoryImpl.getMarkers(virtualFile); assertSame(marker, assertOneElement(markers)); assertFalse(marker.isDelegated()); assertTrue(marker.isValid()); assertEquals(0, marker.getStartOffset()); assertFalse(marker.isDelegated()); marker.dispose(); assertFalse(marker.isValid()); assertEmpty(LazyRangeMarkerFactoryImpl.getMarkers(virtualFile)); marker = (LazyRangeMarkerFactoryImpl.LazyMarker) factory.createRangeMarker(virtualFile, 0); assertFalse(marker.isDelegated()); assertTrue(marker.isValid()); assertEquals(0, marker.getStartOffset()); assertFalse(marker.isDelegated()); Document document = marker.getDocument(); document.insertString(2, "yyy"); assertTrue(marker.isDelegated()); assertTrue(marker.isValid()); assertEquals(0, marker.getStartOffset()); assertEmpty(LazyRangeMarkerFactoryImpl.getMarkers(virtualFile)); marker.dispose(); assertEmpty(LazyRangeMarkerFactoryImpl.getMarkers(virtualFile)); }
private static void indentBlockWithFormatter( Project project, Document document, int startOffset, int endOffset, PsiFile file) { // Algorithm: the main idea is to process the first line of the pasted block, adjust its indent // if necessary, calculate indent // adjustment string and apply to each line of the pasted block starting from the second one. // // We differentiate the following possible states here: // --- pasted block doesn't start new line, i.e. there are non-white space symbols before it // at the first line. // Example: // old content [pasted line 1 // pasted line 2] // Indent adjustment string is just the first line indent then. // // --- pasted block starts with empty line(s) // Example: // old content [ // pasted line 1 // pasted line 2] // We parse existing indents of the pasted block then, adjust its first non-blank line via // formatter and adjust indent // of subsequent pasted lines in order to preserve old indentation. // // --- pasted block is located at the new line and starts with white space symbols. // Example: // [ pasted line 1 // pasted line 2] // We parse existing indents of the pasted block then, adjust its first line via formatter // and adjust indent of the pasted lines // starting from the second one in order to preserve old indentation. // // --- pasted block is located at the new line but doesn't start with white space symbols. // Example: // [pasted line 1 // pasted line 2] // We adjust the first line via formatter then and apply first line's indent to all // subsequent pasted lines. CharSequence chars = document.getCharsSequence(); final int firstLine = document.getLineNumber(startOffset); final int firstLineStart = document.getLineStartOffset(firstLine); // There is a possible case that we paste block that ends with new line that is empty or // contains only white space symbols. // We want to preserve indent for the original document line where paste was performed. // Example: // Original: // if (test) { // <caret> } // // Pasting: 'int i = 1;\n' // Expected: // if (test) { // int i = 1; // } boolean saveLastLineIndent = false; for (int i = endOffset - 1; i >= startOffset; i--) { final char c = chars.charAt(i); if (c == '\n') { saveLastLineIndent = true; break; } if (c != ' ' && c != '\t') { break; } } final int lastLine; if (saveLastLineIndent) { lastLine = document.getLineNumber(endOffset) - 1; // Remove white space symbols at the pasted text if any. int start = document.getLineStartOffset(lastLine + 1); if (start < endOffset) { int i = CharArrayUtil.shiftForward(chars, start, " \t"); if (i > start) { i = Math.min(i, endOffset); document.deleteString(start, i); } } // Insert white space from the start line of the pasted block. int indentToKeepEndOffset = Math.min(startOffset, CharArrayUtil.shiftForward(chars, firstLineStart, " \t")); if (indentToKeepEndOffset > firstLineStart) { document.insertString(start, chars.subSequence(firstLineStart, indentToKeepEndOffset)); } } else { lastLine = document.getLineNumber(endOffset); } final int i = CharArrayUtil.shiftBackward(chars, startOffset - 1, " \t"); // Handle a situation when pasted block doesn't start a new line. if (chars.charAt(startOffset) != '\n' && i > 0 && chars.charAt(i) != '\n') { int firstNonWsOffset = CharArrayUtil.shiftForward(chars, firstLineStart, " \t"); if (firstNonWsOffset > firstLineStart) { CharSequence toInsert = chars.subSequence(firstLineStart, firstNonWsOffset); for (int line = firstLine + 1; line <= lastLine; line++) { document.insertString(document.getLineStartOffset(line), toInsert); } } return; } // Sync document and PSI for correct formatting processing. PsiDocumentManager.getInstance(project).commitAllDocuments(); if (file == null) { return; } CodeStyleManager codeStyleManager = CodeStyleManager.getInstance(project); final int j = CharArrayUtil.shiftForward(chars, startOffset, " \t\n"); if (j >= endOffset) { // Pasted text contains white space/line feed symbols only, do nothing. return; } final int anchorLine = document.getLineNumber(j); final int anchorLineStart = document.getLineStartOffset(anchorLine); codeStyleManager.adjustLineIndent(file, j); // Handle situation when pasted block starts with non-white space symbols. if (anchorLine == firstLine && j == startOffset) { int indentOffset = CharArrayUtil.shiftForward(chars, firstLineStart, " \t"); if (indentOffset > firstLineStart) { CharSequence toInsert = chars.subSequence(firstLineStart, indentOffset); for (int line = firstLine + 1; line <= lastLine; line++) { document.insertString(document.getLineStartOffset(line), toInsert); } } return; } // Handle situation when pasted block starts from white space symbols. Assume that the pasted // text started at the line start, // i.e. correct indentation level is stored at the blocks structure. final int firstNonWsOffset = CharArrayUtil.shiftForward(chars, anchorLineStart, " \t"); final int diff = firstNonWsOffset - j; if (diff == 0) { return; } if (diff > 0) { CharSequence toInsert = chars.subSequence(anchorLineStart, anchorLineStart + diff); for (int line = anchorLine + 1; line <= lastLine; line++) { document.insertString(document.getLineStartOffset(line), toInsert); } return; } // We've pasted text to the non-first column and exact white space between the line start and // caret position on the moment of paste // has been removed by formatter during 'adjust line indent' // Example: // copied text: // ' line1 // line2' // after paste: // line start -> ' I line1 // line2' (I - caret position during 'paste') // formatter removed white space between the line start and caret position, so, current // document state is: // ' line1 // line2' if (anchorLine == firstLine && -diff == startOffset - firstLineStart) { return; } if (anchorLine != firstLine || -diff > startOffset - firstLineStart) { final int desiredSymbolsToRemove; if (anchorLine == firstLine) { desiredSymbolsToRemove = -diff - (startOffset - firstLineStart); } else { desiredSymbolsToRemove = -diff; } for (int line = anchorLine + 1; line <= lastLine; line++) { int currentLineStart = document.getLineStartOffset(line); int currentLineIndentOffset = CharArrayUtil.shiftForward(chars, currentLineStart, " \t"); int symbolsToRemove = Math.min(currentLineIndentOffset - currentLineStart, desiredSymbolsToRemove); if (symbolsToRemove > 0) { document.deleteString(currentLineStart, currentLineStart + symbolsToRemove); } } } else { CharSequence toInsert = chars.subSequence(anchorLineStart, diff + startOffset); for (int line = anchorLine + 1; line <= lastLine; line++) { document.insertString(document.getLineStartOffset(line), toInsert); } } }
public final void move(Editor editor, final PsiFile file) { myMover.beforeMove(editor, myInfo, myIsDown); final Document document = editor.getDocument(); final int start = StatementUpDownMover.getLineStartSafeOffset(document, myInfo.toMove.startLine); final int end = StatementUpDownMover.getLineStartSafeOffset(document, myInfo.toMove.endLine); myInfo.range1 = document.createRangeMarker(start, end); String textToInsert = document.getCharsSequence().subSequence(start, end).toString(); if (!StringUtil.endsWithChar(textToInsert, '\n')) textToInsert += '\n'; final int start2 = document.getLineStartOffset(myInfo.toMove2.startLine); final int end2 = StatementUpDownMover.getLineStartSafeOffset(document, myInfo.toMove2.endLine); String textToInsert2 = document.getCharsSequence().subSequence(start2, end2).toString(); if (!StringUtil.endsWithChar(textToInsert2, '\n')) textToInsert2 += '\n'; myInfo.range2 = document.createRangeMarker(start2, end2); if (myInfo.range1.getStartOffset() < myInfo.range2.getStartOffset()) { myInfo.range1.setGreedyToLeft(true); myInfo.range1.setGreedyToRight(false); myInfo.range2.setGreedyToLeft(true); myInfo.range2.setGreedyToRight(true); } else { myInfo.range1.setGreedyToLeft(true); myInfo.range1.setGreedyToRight(true); myInfo.range2.setGreedyToLeft(true); myInfo.range2.setGreedyToRight(false); } final CaretModel caretModel = editor.getCaretModel(); final int caretRelativePos = caretModel.getOffset() - start; final SelectionModel selectionModel = editor.getSelectionModel(); final int selectionStart = selectionModel.getSelectionStart(); final int selectionEnd = selectionModel.getSelectionEnd(); final boolean hasSelection = selectionModel.hasSelection(); // to prevent flicker caretModel.moveToOffset(0); // There is a possible case that the user performs, say, method move. It's also possible that // one (or both) of moved methods // are folded. We want to preserve their states then. The problem is that folding processing is // based on PSI element pointers // and the pointers behave as following during move up/down: // method1() {} // method2() {} // Pointer for the fold region from method1 points to 'method2()' now and vice versa (check // range markers processing on // document change for further information). I.e. information about fold regions statuses holds // the data swapped for // 'method1' and 'method2'. Hence, we want to apply correct 'collapsed' status. FoldRegion topRegion = null; FoldRegion bottomRegion = null; for (FoldRegion foldRegion : editor.getFoldingModel().getAllFoldRegions()) { if (!foldRegion.isValid() || (!contains(myInfo.range1, foldRegion) && !contains(myInfo.range2, foldRegion))) { continue; } if (contains(myInfo.range1, foldRegion) && !contains(topRegion, foldRegion)) { topRegion = foldRegion; } else if (contains(myInfo.range2, foldRegion) && !contains(bottomRegion, foldRegion)) { bottomRegion = foldRegion; } } document.insertString(myInfo.range1.getStartOffset(), textToInsert2); document.deleteString( myInfo.range1.getStartOffset() + textToInsert2.length(), myInfo.range1.getEndOffset()); document.insertString(myInfo.range2.getStartOffset(), textToInsert); int s = myInfo.range2.getStartOffset() + textToInsert.length(); int e = myInfo.range2.getEndOffset(); if (e > s) { document.deleteString(s, e); } final Project project = file.getProject(); PsiDocumentManager.getInstance(project).commitAllDocuments(); // Swap fold regions status if necessary. if (topRegion != null && bottomRegion != null) { final FoldRegion finalTopRegion = topRegion; final FoldRegion finalBottomRegion = bottomRegion; editor .getFoldingModel() .runBatchFoldingOperation( new Runnable() { @Override public void run() { boolean topExpanded = finalTopRegion.isExpanded(); finalTopRegion.setExpanded(finalBottomRegion.isExpanded()); finalBottomRegion.setExpanded(topExpanded); } }); } CodeFoldingManager.getInstance(project).allowFoldingOnCaretLine(editor); if (hasSelection) { restoreSelection(editor, selectionStart, selectionEnd, start, myInfo.range2.getStartOffset()); } caretModel.moveToOffset(myInfo.range2.getStartOffset() + caretRelativePos); if (myInfo.indentTarget) { indentLinesIn(editor, file, document, project, myInfo.range2); } if (myInfo.indentSource) { indentLinesIn(editor, file, document, project, myInfo.range1); } myMover.afterMove(editor, file, myInfo, myIsDown); editor.getScrollingModel().scrollToCaret(ScrollType.RELATIVE); }
private static void append(Document document, String s) { document.insertString(document.getTextLength(), s); }
@Override public void handleInsert(InsertionContext context) { PsiVariable variable = getObject(); Document document = context.getDocument(); document.replaceString(context.getStartOffset(), context.getTailOffset(), variable.getName()); context.commitDocument(); if (variable instanceof PsiField) { if (willBeImported()) { RangeMarker toDelete = JavaCompletionUtil.insertTemporary(context.getTailOffset(), document, " "); context.commitDocument(); final PsiReferenceExpression ref = PsiTreeUtil.findElementOfClassAtOffset( context.getFile(), context.getStartOffset(), PsiReferenceExpression.class, false); if (ref != null) { ref.bindToElementViaStaticImport(((PsiField) variable).getContainingClass()); PostprocessReformattingAspect.getInstance(ref.getProject()).doPostponedFormatting(); } if (toDelete.isValid()) { document.deleteString(toDelete.getStartOffset(), toDelete.getEndOffset()); } context.commitDocument(); } else if (shouldQualify((PsiField) variable, context)) { qualifyFieldReference(context, (PsiField) variable); } } PsiReferenceExpression ref = PsiTreeUtil.findElementOfClassAtOffset( context.getFile(), context.getTailOffset() - 1, PsiReferenceExpression.class, false); if (ref != null) { JavaCodeStyleManager.getInstance(context.getProject()).shortenClassReferences(ref); } ref = PsiTreeUtil.findElementOfClassAtOffset( context.getFile(), context.getTailOffset() - 1, PsiReferenceExpression.class, false); makeVariableFinalIfNeeded(context, ref); final char completionChar = context.getCompletionChar(); if (completionChar == '=') { context.setAddCompletionChar(false); TailType.EQ.processTail(context.getEditor(), context.getTailOffset()); } else if (completionChar == ',' && getAttribute(LookupItem.TAIL_TYPE_ATTR) != TailType.UNKNOWN) { context.setAddCompletionChar(false); TailType.COMMA.processTail(context.getEditor(), context.getTailOffset()); AutoPopupController.getInstance(context.getProject()) .autoPopupParameterInfo(context.getEditor(), null); } else if (completionChar == ':') { context.setAddCompletionChar(false); TailType.COND_EXPR_COLON.processTail(context.getEditor(), context.getTailOffset()); } else if (completionChar == '.') { AutoPopupController.getInstance(context.getProject()) .autoPopupMemberLookup(context.getEditor(), null); } else if (completionChar == '!' && PsiType.BOOLEAN.isAssignableFrom(variable.getType())) { context.setAddCompletionChar(false); if (ref != null) { FeatureUsageTracker.getInstance() .triggerFeatureUsed(CodeCompletionFeatures.EXCLAMATION_FINISH); document.insertString(ref.getTextRange().getStartOffset(), "!"); } } }
private void commentLine(int line, int offset, @Nullable Commenter commenter) { if (commenter == null) commenter = findCommenter(line); if (commenter == null) return; if (commenter instanceof SelfManagingCommenter) { final SelfManagingCommenter selfManagingCommenter = (SelfManagingCommenter) commenter; selfManagingCommenter.commentLine( line, offset, myDocument, myCommenterStateMap.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"); 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); } myDocument.insertString(offset, prefix); } } else { myDocument.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(); offset = CharArrayUtil.shiftForward(chars, offset, " \t"); if (endOffset == textLength) { final int shifted = CharArrayUtil.shiftBackward(chars, textLength - 1, " \t"); if (shifted < textLength - 1) endOffset = shifted; } else { endOffset = CharArrayUtil.shiftBackward(chars, endOffset, " \t"); } if (endOffset < offset || offset == textLength - 1 && line != myDocument.getLineCount() - 1) { return; } final String text = chars.subSequence(offset, endOffset).toString(); final IntArrayList prefixes = new IntArrayList(); final IntArrayList suffixes = new IntArrayList(); final String commentedSuffix = commenter.getCommentedBlockCommentSuffix(); final String commentedPrefix = commenter.getCommentedBlockCommentPrefix(); for (int position = 0; position < text.length(); ) { int nearestPrefix = text.indexOf(prefix, position); if (nearestPrefix == -1) { nearestPrefix = text.length(); } int nearestSuffix = text.indexOf(suffix, position); if (nearestSuffix == -1) { nearestSuffix = text.length(); } if (Math.min(nearestPrefix, nearestSuffix) == text.length()) { break; } if (nearestPrefix < nearestSuffix) { prefixes.add(nearestPrefix); position = nearestPrefix + prefix.length(); } else { suffixes.add(nearestSuffix); position = nearestSuffix + suffix.length(); } } if (!(commentedSuffix == null && !suffixes.isEmpty() && offset + suffixes.get(suffixes.size() - 1) + suffix.length() >= endOffset)) { myDocument.insertString(endOffset, suffix); } int nearestPrefix = prefixes.size() - 1; int nearestSuffix = suffixes.size() - 1; while (nearestPrefix >= 0 || nearestSuffix >= 0) { if (nearestSuffix == -1 || nearestPrefix != -1 && prefixes.get(nearestPrefix) > suffixes.get(nearestSuffix)) { final int position = prefixes.get(nearestPrefix); nearestPrefix--; if (commentedPrefix != null) { myDocument.replaceString( offset + position, offset + position + prefix.length(), commentedPrefix); } else if (position != 0) { myDocument.insertString(offset + position, suffix); } } else { final int position = suffixes.get(nearestSuffix); nearestSuffix--; if (commentedSuffix != null) { myDocument.replaceString( offset + position, offset + position + suffix.length(), commentedSuffix); } else if (offset + position + suffix.length() < endOffset) { myDocument.insertString(offset + position + suffix.length(), prefix); } } } if (!(commentedPrefix == null && !prefixes.isEmpty() && prefixes.get(0) == 0)) { myDocument.insertString(offset, prefix); } } }
@Override public void invoke( @NotNull Project project, @NotNull Editor editor, @NotNull Caret caret, @NotNull PsiFile file) { if (!CodeInsightUtilBase.prepareEditorForWrite(editor)) return; myProject = project; myEditor = editor; myCaret = caret; myFile = file; myDocument = editor.getDocument(); if (!FileDocumentManager.getInstance().requestWriting(myDocument, project)) { return; } FeatureUsageTracker.getInstance().triggerFeatureUsed("codeassists.comment.block"); final Commenter commenter = findCommenter(myFile, myEditor, caret); if (commenter == null) return; final String prefix; final String suffix; if (commenter instanceof SelfManagingCommenter) { final SelfManagingCommenter selfManagingCommenter = (SelfManagingCommenter) commenter; mySelfManagedCommenterData = selfManagingCommenter.createBlockCommentingState( caret.getSelectionStart(), caret.getSelectionEnd(), myDocument, myFile); if (mySelfManagedCommenterData == null) { mySelfManagedCommenterData = SelfManagingCommenter.EMPTY_STATE; } prefix = selfManagingCommenter.getBlockCommentPrefix( caret.getSelectionStart(), myDocument, mySelfManagedCommenterData); suffix = selfManagingCommenter.getBlockCommentSuffix( caret.getSelectionEnd(), myDocument, mySelfManagedCommenterData); } else { prefix = commenter.getBlockCommentPrefix(); suffix = commenter.getBlockCommentSuffix(); } if (prefix == null || suffix == null) return; TextRange commentedRange = findCommentedRange(commenter); if (commentedRange != null) { final int commentStart = commentedRange.getStartOffset(); final int commentEnd = commentedRange.getEndOffset(); int selectionStart = commentStart; int selectionEnd = commentEnd; if (myCaret.hasSelection()) { selectionStart = myCaret.getSelectionStart(); selectionEnd = myCaret.getSelectionEnd(); } if ((commentStart < selectionStart || commentStart >= selectionEnd) && (commentEnd <= selectionStart || commentEnd > selectionEnd)) { commentRange(selectionStart, selectionEnd, prefix, suffix, commenter); } else { uncommentRange(commentedRange, trim(prefix), trim(suffix), commenter); } } else { if (myCaret.hasSelection()) { int selectionStart = myCaret.getSelectionStart(); int selectionEnd = myCaret.getSelectionEnd(); if (commenter instanceof IndentedCommenter) { final Boolean value = ((IndentedCommenter) commenter).forceIndentedLineComment(); if (value != null && value == Boolean.TRUE) { selectionStart = myDocument.getLineStartOffset(myDocument.getLineNumber(selectionStart)); selectionEnd = myDocument.getLineEndOffset(myDocument.getLineNumber(selectionEnd)); } } commentRange(selectionStart, selectionEnd, prefix, suffix, commenter); } else { EditorUtil.fillVirtualSpaceUntilCaret(editor); int caretOffset = myCaret.getOffset(); if (commenter instanceof IndentedCommenter) { final Boolean value = ((IndentedCommenter) commenter).forceIndentedLineComment(); if (value != null && value == Boolean.TRUE) { final int lineNumber = myDocument.getLineNumber(caretOffset); final int start = myDocument.getLineStartOffset(lineNumber); final int end = myDocument.getLineEndOffset(lineNumber); commentRange(start, end, prefix, suffix, commenter); return; } } myDocument.insertString(caretOffset, prefix + suffix); myCaret.moveToOffset(caretOffset + prefix.length()); } } }
@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(); }
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(); } }
protected void appendToHistoryDocument(@NotNull Document history, @NotNull String text) { history.insertString(history.getTextLength(), text); }
/** * Generates a comment if possible. * * <p>It's assumed that this method {@link PsiDocumentManager#commitDocument(Document) syncs} all * PSI-document changes during the processing. * * @param anchor target element for which a comment should be generated * @param editor target editor * @param commenter commenter to use * @param project current project */ private static void generateComment( @NotNull PsiElement anchor, @NotNull Editor editor, @NotNull CodeDocumentationProvider documentationProvider, @NotNull CodeDocumentationAwareCommenter commenter, @NotNull Project project) { Document document = editor.getDocument(); int commentStartOffset = anchor.getTextRange().getStartOffset(); int lineStartOffset = document.getLineStartOffset(document.getLineNumber(commentStartOffset)); if (lineStartOffset > 0 && lineStartOffset < commentStartOffset) { // Example: // void test1() { // } // void test2() { // <offset> // } // We want to insert the comment at the start of the line where 'test2()' is declared. int nonWhiteSpaceOffset = CharArrayUtil.shiftBackward(document.getCharsSequence(), commentStartOffset - 1, " \t"); commentStartOffset = Math.max(nonWhiteSpaceOffset, lineStartOffset); } int commentBodyRelativeOffset = 0; int caretOffsetToSet = -1; StringBuilder buffer = new StringBuilder(); String commentPrefix = commenter.getDocumentationCommentPrefix(); if (commentPrefix != null) { buffer.append(commentPrefix).append("\n"); commentBodyRelativeOffset += commentPrefix.length() + 1; } String linePrefix = commenter.getDocumentationCommentLinePrefix(); if (linePrefix != null) { buffer.append(linePrefix); commentBodyRelativeOffset += linePrefix.length(); caretOffsetToSet = commentStartOffset + commentBodyRelativeOffset; } buffer.append("\n"); commentBodyRelativeOffset++; String commentSuffix = commenter.getDocumentationCommentSuffix(); if (commentSuffix != null) { buffer.append(commentSuffix).append("\n"); } if (buffer.length() <= 0) { return; } document.insertString(commentStartOffset, buffer); PsiDocumentManager docManager = PsiDocumentManager.getInstance(project); docManager.commitDocument(document); Pair<PsiElement, PsiComment> pair = documentationProvider.parseContext(anchor); if (pair == null || pair.second == null) { return; } String stub = documentationProvider.generateDocumentationContentStub(pair.second); CaretModel caretModel = editor.getCaretModel(); if (stub != null) { int insertionOffset = commentStartOffset + commentBodyRelativeOffset; // if (CodeStyleSettingsManager.getSettings(project).JD_ADD_BLANK_AFTER_DESCRIPTION) { // buffer.setLength(0); // if (linePrefix != null) { // buffer.append(linePrefix); // } // buffer.append("\n"); // buffer.append(stub); // stub = buffer.toString(); // } document.insertString(insertionOffset, stub); docManager.commitDocument(document); pair = documentationProvider.parseContext(anchor); } if (caretOffsetToSet >= 0) { caretModel.moveToOffset(caretOffsetToSet); } if (pair == null || pair.second == null) { return; } int start = Math.min(calcStartReformatOffset(pair.first), calcStartReformatOffset(pair.second)); int end = pair.second.getTextRange().getEndOffset(); CodeStyleManager codeStyleManager = CodeStyleManager.getInstance(project); codeStyleManager.reformatText(anchor.getContainingFile(), start, end); int caretOffset = caretModel.getOffset(); if (caretOffset > 0 && caretOffset <= document.getTextLength()) { char c = document.getCharsSequence().charAt(caretOffset - 1); if (!StringUtil.isWhiteSpace(c)) { document.insertString(caretOffset, " "); caretModel.moveToOffset(caretOffset + 1); } } }
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; }
private static void addImportUnderPackage(GoFile file, Document document, String pathToImport) { int insertPoint = file.getPackage().getTextRange().getEndOffset(); document.insertString(insertPoint, String.format("\n\nimport \"%s\"", pathToImport)); }