String (final PsiFile file, final int[] startOffsets, final int[] endOffsets, final String text) { if (!isSupportedFile(file)) { return null; } // The main idea is to un-escape string/char literals content if necessary. // Example: // Suppose we have a following text at the editor: String s = "first <selection>line \n second</selection> line" // When user copies the selection we want to transform text \n to the real line feed, i.e. clipboard should contains the following: // line // second // // However, we don't want to un-escape literal content if it's copied completely. // Example: // String s = <selection>"my string"</selection>; StringBuilder buffer = new StringBuilder(); int givenTextOffset = 0; boolean textWasChanged = false; int deducedBlockSelectionWidth = deduceBlockSelectionWidth(startOffsets, endOffsets, text); for (int i = 0; i < startOffsets.length && givenTextOffset < text.length(); i++, givenTextOffset++) { if (i > 0) { buffer.append('\n'); // LF is added for block selection } // Calculate offsets offsets of the selection interval being processed now. final int fileStartOffset = startOffsets[i]; final int fileEndOffset = endOffsets[i]; int givenTextStartOffset = givenTextOffset; final int givenTextEndOffset = givenTextOffset + (fileEndOffset - fileStartOffset); givenTextOffset = givenTextEndOffset; if (givenTextOffset > text.length()) { // This can happen e.g. line terminators were normalized in copied text, and it became shorter // than corresponding fragment in the original document. // We don't implement escaping/unescaping logic for documents with non-normalized line terminators currently. return null; } String fragment = text.substring(givenTextStartOffset, givenTextEndOffset); PsiElement element = file.findElementAt(fileStartOffset); TextRange escapedRange = element == null ? null : getEscapedRange(element); if (escapedRange == null || escapedRange.getStartOffset() > fileStartOffset || escapedRange.getEndOffset() < fileEndOffset) { buffer.append(fragment); } else { String unescaped = unescape(fragment, element); if (unescaped != null) { textWasChanged = true; buffer.append(unescaped); } else { buffer.append(fragment); } } int blockSelectionPadding = deducedBlockSelectionWidth - (fileEndOffset - fileStartOffset); for (int j = 0; j < blockSelectionPadding; j++) { buffer.append(' '); givenTextOffset++; } } return textWasChanged ? buffer.toString() : null; }