mirror of
https://gitflic.ru/project/openide/openide.git
synced 2026-09-27 10:03:11 +07:00
IDEA-67767 Copy syntax-coloured code block to clipboard as rtf
This commit is contained in:
+34
-3
@@ -446,11 +446,42 @@
|
||||
</component>
|
||||
</children>
|
||||
</grid>
|
||||
<vspacer id="9f8">
|
||||
<grid id="62a96" layout-manager="GridLayoutManager" row-count="2" column-count="2" same-size-horizontally="false" same-size-vertically="false" hgap="-1" vgap="-1">
|
||||
<margin top="0" left="0" bottom="0" right="0"/>
|
||||
<constraints>
|
||||
<grid row="2" column="0" row-span="1" col-span="1" vsize-policy="6" hsize-policy="1" anchor="0" fill="2" indent="0" use-parent-layout="false"/>
|
||||
<grid row="2" column="0" row-span="1" col-span="1" vsize-policy="3" hsize-policy="3" anchor="0" fill="3" indent="0" use-parent-layout="false"/>
|
||||
</constraints>
|
||||
</vspacer>
|
||||
<properties/>
|
||||
<clientProperties>
|
||||
<BorderFactoryClass class="java.lang.String" value="com.intellij.ui.IdeBorderFactory$PlainSmallWithIndent"/>
|
||||
</clientProperties>
|
||||
<border type="none" title-resource-bundle="messages/ApplicationBundle" title-key="group.richcopy"/>
|
||||
<children>
|
||||
<component id="8b6fe" class="javax.swing.JCheckBox" binding="myRichCopyStripWhitespaceCheckBox">
|
||||
<constraints>
|
||||
<grid row="1" column="0" row-span="1" col-span="2" vsize-policy="0" hsize-policy="3" anchor="8" fill="0" indent="0" use-parent-layout="false"/>
|
||||
</constraints>
|
||||
<properties>
|
||||
<selected value="true"/>
|
||||
<text resource-bundle="messages/ApplicationBundle" key="checkbox.richcopy.strip.whitespace.at.line.start"/>
|
||||
</properties>
|
||||
</component>
|
||||
<component id="828ad" class="javax.swing.JLabel">
|
||||
<constraints>
|
||||
<grid row="0" column="0" row-span="1" col-span="1" vsize-policy="0" hsize-policy="0" anchor="8" fill="0" indent="0" use-parent-layout="false"/>
|
||||
</constraints>
|
||||
<properties>
|
||||
<text resource-bundle="messages/ApplicationBundle" key="combobox.richcopy.color.scheme"/>
|
||||
</properties>
|
||||
</component>
|
||||
<component id="3b679" class="javax.swing.JComboBox" binding="myRichCopyColorSchemeComboBox">
|
||||
<constraints>
|
||||
<grid row="0" column="1" row-span="1" col-span="1" vsize-policy="0" hsize-policy="2" anchor="8" fill="1" indent="0" use-parent-layout="false"/>
|
||||
</constraints>
|
||||
<properties/>
|
||||
</component>
|
||||
</children>
|
||||
</grid>
|
||||
</children>
|
||||
</grid>
|
||||
</children>
|
||||
|
||||
+51
-1
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2000-2012 JetBrains s.r.o.
|
||||
* Copyright 2000-2014 JetBrains s.r.o.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
@@ -28,9 +28,12 @@ import com.intellij.openapi.components.ServiceManager;
|
||||
import com.intellij.openapi.editor.Document;
|
||||
import com.intellij.openapi.editor.Editor;
|
||||
import com.intellij.openapi.editor.EditorFactory;
|
||||
import com.intellij.openapi.editor.colors.EditorColorsManager;
|
||||
import com.intellij.openapi.editor.colors.EditorColorsScheme;
|
||||
import com.intellij.openapi.editor.ex.EditorEx;
|
||||
import com.intellij.openapi.editor.ex.EditorSettingsExternalizable;
|
||||
import com.intellij.openapi.editor.impl.softwrap.SoftWrapAppliancePlaces;
|
||||
import com.intellij.openapi.editor.richcopy.settings.RichCopySettings;
|
||||
import com.intellij.openapi.fileEditor.FileEditor;
|
||||
import com.intellij.openapi.fileEditor.FileEditorManager;
|
||||
import com.intellij.openapi.fileEditor.TextEditor;
|
||||
@@ -38,8 +41,10 @@ import com.intellij.openapi.options.ConfigurationException;
|
||||
import com.intellij.openapi.options.SearchableConfigurable;
|
||||
import com.intellij.openapi.project.Project;
|
||||
import com.intellij.openapi.project.ProjectManager;
|
||||
import com.intellij.openapi.util.Comparing;
|
||||
import com.intellij.openapi.util.SystemInfo;
|
||||
import com.intellij.openapi.util.text.StringUtil;
|
||||
import com.intellij.ui.ListCellRendererWrapper;
|
||||
import com.intellij.ui.components.JBCheckBox;
|
||||
import com.intellij.ui.components.JBLabel;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
@@ -91,6 +96,10 @@ public class EditorOptionsPanel {
|
||||
private JBCheckBox myCbShowQuickDocOnMouseMove;
|
||||
private JBLabel myQuickDocDelayLabel;
|
||||
private JTextField myQuickDocDelayTextField;
|
||||
private JComboBox myRichCopyColorSchemeComboBox;
|
||||
private JCheckBox myRichCopyStripWhitespaceCheckBox;
|
||||
|
||||
private static final String ACTIVE_COLOR_SCHEME = ApplicationBundle.message("combobox.richcopy.color.scheme.active");
|
||||
|
||||
private final ErrorHighlightingPanel myErrorHighlightingPanel = new ErrorHighlightingPanel();
|
||||
private final MyConfigurable myConfigurable;
|
||||
@@ -113,6 +122,20 @@ public class EditorOptionsPanel {
|
||||
|
||||
myCbRenameLocalVariablesInplace.setVisible(OptionsApplicabilityFilter.isApplicable(OptionId.RENAME_IN_PLACE));
|
||||
|
||||
myRichCopyColorSchemeComboBox.setRenderer(new ListCellRendererWrapper<String>() {
|
||||
@Override
|
||||
public void customize(JList list, String value, int index, boolean selected, boolean hasFocus) {
|
||||
final String textToUse;
|
||||
if (RichCopySettings.ACTIVE_GLOBAL_SCHEME_MARKER.equals(value)) {
|
||||
textToUse = ACTIVE_COLOR_SCHEME;
|
||||
}
|
||||
else {
|
||||
textToUse = value;
|
||||
}
|
||||
setText(textToUse);
|
||||
}
|
||||
});
|
||||
|
||||
myConfigurable = new MyConfigurable();
|
||||
initQuickDocProcessing();
|
||||
initSoftWrapsSettingsProcessing();
|
||||
@@ -189,6 +212,20 @@ public class EditorOptionsPanel {
|
||||
myShowOptimizeImportsDialogCheckBox.setSelected(editorSettings.getOptions().SHOW_OPIMIZE_IMPORTS_DIALOG);
|
||||
|
||||
myErrorHighlightingPanel.reset();
|
||||
|
||||
RichCopySettings settings = RichCopySettings.getInstance();
|
||||
myRichCopyColorSchemeComboBox.removeAllItems();
|
||||
EditorColorsScheme[] schemes = EditorColorsManager.getInstance().getAllSchemes();
|
||||
myRichCopyColorSchemeComboBox.addItem(RichCopySettings.ACTIVE_GLOBAL_SCHEME_MARKER);
|
||||
for (EditorColorsScheme scheme : schemes) {
|
||||
myRichCopyColorSchemeComboBox.addItem(scheme.getName());
|
||||
}
|
||||
String toSelect = settings.getSchemeName();
|
||||
if (!StringUtil.isEmpty(toSelect)) {
|
||||
myRichCopyColorSchemeComboBox.setSelectedItem(toSelect);
|
||||
}
|
||||
|
||||
myRichCopyStripWhitespaceCheckBox.setSelected(settings.isStripIndents());
|
||||
}
|
||||
|
||||
public void apply() throws ConfigurationException {
|
||||
@@ -289,6 +326,14 @@ public class EditorOptionsPanel {
|
||||
uiSettings.CONSOLE_COMMAND_HISTORY_LIMIT = StringUtil.parseInt(myCommandsHistoryLimitField.getText(), uiSettings.CONSOLE_COMMAND_HISTORY_LIMIT);
|
||||
|
||||
myErrorHighlightingPanel.apply();
|
||||
|
||||
RichCopySettings settings = RichCopySettings.getInstance();
|
||||
Object item = myRichCopyColorSchemeComboBox.getSelectedItem();
|
||||
if (item instanceof String) {
|
||||
settings.setSchemeName(item.toString());
|
||||
}
|
||||
settings.setStripIndents(myRichCopyStripWhitespaceCheckBox.isSelected());
|
||||
|
||||
restartDaemons();
|
||||
}
|
||||
|
||||
@@ -406,6 +451,11 @@ public class EditorOptionsPanel {
|
||||
isModified |= isModified(myShowOptimizeImportsDialogCheckBox, editorSettings.getOptions().SHOW_OPIMIZE_IMPORTS_DIALOG);
|
||||
|
||||
isModified |= myErrorHighlightingPanel.isModified();
|
||||
|
||||
RichCopySettings settings = RichCopySettings.getInstance();
|
||||
isModified |= !Comparing.equal(settings.getSchemeName(), myRichCopyColorSchemeComboBox.getSelectedItem());
|
||||
isModified |= isModified(myRichCopyStripWhitespaceCheckBox, settings.isStripIndents());
|
||||
|
||||
return isModified;
|
||||
}
|
||||
|
||||
|
||||
+17
-18
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2000-2009 JetBrains s.r.o.
|
||||
* Copyright 2000-2014 JetBrains s.r.o.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
@@ -27,38 +27,37 @@ import java.io.IOException;
|
||||
import java.io.StringReader;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collection;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
|
||||
class TextBlockTransferable implements Transferable {
|
||||
private final Collection<TextBlockTransferableData> myExtraData;
|
||||
private final RawText myRawText;
|
||||
private final String myText;
|
||||
private final DataFlavor[] myTransferDataFlavors;
|
||||
|
||||
public TextBlockTransferable(String text, Collection<TextBlockTransferableData> extraData, RawText rawText) {
|
||||
myText = text;
|
||||
myExtraData = extraData;
|
||||
myRawText = rawText;
|
||||
|
||||
List<DataFlavor> dataFlavors = new ArrayList<DataFlavor>();
|
||||
Collections.addAll(dataFlavors, DataFlavor.stringFlavor, DataFlavor.plainTextFlavor);
|
||||
final DataFlavor flavor = RawText.getDataFlavor();
|
||||
if (myRawText != null && flavor != null) {
|
||||
dataFlavors.add(flavor);
|
||||
}
|
||||
for(TextBlockTransferableData data: extraData) {
|
||||
final DataFlavor blockFlavor = data.getFlavor();
|
||||
if (blockFlavor != null) {
|
||||
dataFlavors.add(blockFlavor);
|
||||
}
|
||||
}
|
||||
myTransferDataFlavors = dataFlavors.toArray(new DataFlavor[dataFlavors.size()]);
|
||||
}
|
||||
|
||||
@Override
|
||||
public DataFlavor[] getTransferDataFlavors() {
|
||||
return myTransferDataFlavors;
|
||||
// We don't cache the result to allow certain dataflavors to 'expire'.
|
||||
// This is used e.g. for RTF and HTML flavors to avoid memory leaking.
|
||||
List<DataFlavor> flavors = new ArrayList<DataFlavor>(myExtraData.size() + 3);
|
||||
flavors.add(DataFlavor.stringFlavor);
|
||||
flavors.add(DataFlavor.plainTextFlavor);
|
||||
final DataFlavor flavor = RawText.getDataFlavor();
|
||||
if (myRawText != null && flavor != null) {
|
||||
flavors.add(flavor);
|
||||
}
|
||||
for(TextBlockTransferableData data : myExtraData) {
|
||||
final DataFlavor blockFlavor = data.getFlavor();
|
||||
if (blockFlavor != null) {
|
||||
flavors.add(blockFlavor);
|
||||
}
|
||||
}
|
||||
return flavors.toArray(new DataFlavor[flavors.size()]);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
||||
+95
@@ -0,0 +1,95 @@
|
||||
/*
|
||||
* Copyright 2000-2014 JetBrains s.r.o.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* 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.openapi.editor.richcopy;
|
||||
|
||||
import com.intellij.codeInsight.editorActions.CopyPastePostProcessor;
|
||||
import com.intellij.codeInsight.editorActions.TextBlockTransferableData;
|
||||
import com.intellij.openapi.diagnostic.Logger;
|
||||
import com.intellij.openapi.editor.Editor;
|
||||
import com.intellij.openapi.editor.RangeMarker;
|
||||
import com.intellij.openapi.editor.richcopy.model.SyntaxInfo;
|
||||
import com.intellij.openapi.project.Project;
|
||||
import com.intellij.openapi.util.Ref;
|
||||
import com.intellij.openapi.util.registry.Registry;
|
||||
import com.intellij.psi.PsiFile;
|
||||
import com.intellij.util.StringBuilderSpinAllocator;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
import java.awt.datatransfer.Transferable;
|
||||
|
||||
public abstract class BaseTextWithMarkupCopyPasteProcessor<T extends TextBlockTransferableData> implements CopyPastePostProcessor<T>, TextWithMarkupBuilder {
|
||||
private static final Logger LOG = Logger.getInstance("#" + BaseTextWithMarkupCopyPasteProcessor.class.getName());
|
||||
|
||||
private T myData;
|
||||
|
||||
protected BaseTextWithMarkupCopyPasteProcessor(TextWithMarkupProcessor processor) {
|
||||
processor.addBuilder(this);
|
||||
}
|
||||
|
||||
@Nullable
|
||||
@Override
|
||||
public T collectTransferableData(PsiFile file, Editor editor, int[] startOffsets, int[] endOffsets) {
|
||||
if (!Registry.is("editor.richcopy.enable")) {
|
||||
return null;
|
||||
}
|
||||
return myData;
|
||||
}
|
||||
|
||||
@Nullable
|
||||
@Override
|
||||
public T extractTransferableData(Transferable content) {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void processTransferableData(Project project,
|
||||
Editor editor,
|
||||
RangeMarker bounds,
|
||||
int caretOffset,
|
||||
Ref<Boolean> indented,
|
||||
T value) {
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public void build(CharSequence charSequence, SyntaxInfo syntaxInfo) {
|
||||
String stringRepresentation = null;
|
||||
final StringBuilder buffer = StringBuilderSpinAllocator.alloc();
|
||||
try {
|
||||
buildStringRepresentation(buffer, charSequence, syntaxInfo, Registry.intValue("editor.richcopy.max.size.megabytes") * 1048576);
|
||||
stringRepresentation = buffer.toString();
|
||||
if (Registry.is("editor.richcopy.debug")) {
|
||||
LOG.info("Resulting text: \n'" + stringRepresentation + "'");
|
||||
}
|
||||
}
|
||||
catch (Exception e){
|
||||
LOG.error(e);
|
||||
}
|
||||
finally {
|
||||
StringBuilderSpinAllocator.dispose(buffer);
|
||||
}
|
||||
myData = stringRepresentation == null ? null : createTransferable(stringRepresentation);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void reset() {
|
||||
myData = null;
|
||||
}
|
||||
|
||||
protected abstract void buildStringRepresentation(@NotNull StringBuilder buffer, @NotNull CharSequence rawText, @NotNull SyntaxInfo syntaxInfo, int maxLength);
|
||||
protected abstract T createTransferable(@NotNull String data);
|
||||
}
|
||||
@@ -0,0 +1,73 @@
|
||||
/*
|
||||
* Copyright 2000-2014 JetBrains s.r.o.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* 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.openapi.editor.richcopy;
|
||||
|
||||
import com.intellij.application.options.OptionsConstants;
|
||||
import com.intellij.openapi.diagnostic.Logger;
|
||||
import sun.font.CompositeFont;
|
||||
import sun.font.Font2D;
|
||||
import sun.font.FontManager;
|
||||
import sun.font.FontManagerFactory;
|
||||
|
||||
import java.awt.*;
|
||||
import java.lang.reflect.Field;
|
||||
import java.util.HashMap;
|
||||
import java.util.Locale;
|
||||
import java.util.Map;
|
||||
|
||||
/**
|
||||
* Java logical font names (like 'Monospaced') don't necessarily make sense for other applications, so we try to map those fonts to
|
||||
* the corresponding physical font names.
|
||||
*/
|
||||
public class FontMapper {
|
||||
private static final Logger LOG = Logger.getInstance("#" + FontMapper.class.getName());
|
||||
|
||||
private static final String MAC_OS_FONT_CLASS = "sun.font.CFont";
|
||||
private static final String[] logicalFontsToMap = {Font.DIALOG, Font.DIALOG_INPUT, Font.MONOSPACED, Font.SERIF, Font.SANS_SERIF};
|
||||
private static final Map<String, String> logicalToPhysicalMapping = new HashMap<String, String>();
|
||||
|
||||
static {
|
||||
try {
|
||||
FontManager fontManager = FontManagerFactory.getInstance();
|
||||
for (String logicalFont : logicalFontsToMap) {
|
||||
String physicalFont = null;
|
||||
Font2D font2D = fontManager.findFont2D(logicalFont, OptionsConstants.DEFAULT_EDITOR_FONT_SIZE, 0);
|
||||
if (font2D == null) {
|
||||
continue;
|
||||
}
|
||||
else if (font2D instanceof CompositeFont && ((CompositeFont)font2D).getNumSlots() > 0) {
|
||||
physicalFont = ((CompositeFont)font2D).getSlotFont(0).getFamilyName(Locale.getDefault());
|
||||
}
|
||||
else if (font2D.getClass().getName().equals(MAC_OS_FONT_CLASS)) {
|
||||
Field field = Class.forName(MAC_OS_FONT_CLASS).getDeclaredField("nativeFontName");
|
||||
field.setAccessible(true);
|
||||
physicalFont = (String)field.get(font2D);
|
||||
}
|
||||
if (physicalFont != null) {
|
||||
logicalToPhysicalMapping.put(logicalFont, physicalFont);
|
||||
}
|
||||
}
|
||||
}
|
||||
catch (Throwable e) {
|
||||
LOG.warn("Failed to determine logical to physical font mappings");
|
||||
}
|
||||
}
|
||||
|
||||
public static String getPhysicalFontName(String logicalFontName) {
|
||||
String mapped = logicalToPhysicalMapping.get(logicalFontName);
|
||||
return mapped == null ? logicalFontName : mapped;
|
||||
}
|
||||
}
|
||||
+249
@@ -0,0 +1,249 @@
|
||||
/*
|
||||
* Copyright 2000-2014 JetBrains s.r.o.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* 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.openapi.editor.richcopy;
|
||||
|
||||
import com.intellij.openapi.editor.richcopy.model.*;
|
||||
import com.intellij.openapi.editor.richcopy.view.ReaderTransferableData;
|
||||
import com.intellij.util.StringBuilderSpinAllocator;
|
||||
import com.intellij.util.ui.UIUtil;
|
||||
import gnu.trove.TIntObjectHashMap;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
|
||||
import java.awt.*;
|
||||
import java.awt.datatransfer.DataFlavor;
|
||||
|
||||
/**
|
||||
* @author Denis Zhdanov
|
||||
* @since 3/28/13 1:05 PM
|
||||
*/
|
||||
public class HtmlCopyPasteProcessor extends BaseTextWithMarkupCopyPasteProcessor<ReaderTransferableData> implements OutputInfoVisitor {
|
||||
@NotNull public static final DataFlavor FLAVOR = new DataFlavor("text/html;class=java.io.Reader", "HTML text");
|
||||
|
||||
private StringBuilder myResultBuffer;
|
||||
private CharSequence myRawText;
|
||||
private ColorRegistry myColorRegistry;
|
||||
private FontNameRegistry myFontNameRegistry;
|
||||
private TIntObjectHashMap<String> myColors = new TIntObjectHashMap<String>();
|
||||
|
||||
private int myForeground;
|
||||
private int myBackground;
|
||||
private int myFontFamily;
|
||||
private int myFontSize;
|
||||
private boolean myBold;
|
||||
private boolean myItalic;
|
||||
private boolean myIgnoreFontSize;
|
||||
|
||||
public HtmlCopyPasteProcessor(TextWithMarkupProcessor processor) {
|
||||
super(processor);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void buildStringRepresentation(@NotNull StringBuilder buffer,
|
||||
@NotNull CharSequence rawText,
|
||||
@NotNull SyntaxInfo syntaxInfo,
|
||||
int maxLength) {
|
||||
myResultBuffer = buffer;
|
||||
myRawText = rawText;
|
||||
myColorRegistry = syntaxInfo.getColorRegistry();
|
||||
myFontNameRegistry = syntaxInfo.getFontNameRegistry();
|
||||
buildColorMap();
|
||||
try {
|
||||
myResultBuffer.append("<div style=\"border:1px inset;padding:2%;\">")
|
||||
.append("<pre style=\"margin:0;padding:6px;background-color:");
|
||||
// .append("<pre style='height:30%;overflow:auto;margin:0;padding:6px;background-color:")
|
||||
appendColor(myResultBuffer, syntaxInfo.getDefaultBackground());
|
||||
myResultBuffer.append(';');
|
||||
if (myFontNameRegistry.size() == 1) {
|
||||
appendFontFamilyRule(myResultBuffer, myFontNameRegistry.getAllIds()[0]);
|
||||
myFontNameRegistry = null;
|
||||
}
|
||||
int fontSize = syntaxInfo.getSingleFontSize();
|
||||
if (fontSize > 0) {
|
||||
appendFontSizeRule(myResultBuffer, fontSize);
|
||||
myIgnoreFontSize = true;
|
||||
}
|
||||
myResultBuffer.append("\" bgcolor=\"");
|
||||
appendColor(myResultBuffer, syntaxInfo.getDefaultBackground());
|
||||
myResultBuffer.append("\">");
|
||||
|
||||
for (OutputInfo info : syntaxInfo.getOutputInfos()) {
|
||||
info.invite(this);
|
||||
if (myResultBuffer.length() > maxLength) {
|
||||
myResultBuffer.append("... truncated ...");
|
||||
break;
|
||||
}
|
||||
}
|
||||
myResultBuffer.append("</pre></div>");
|
||||
}
|
||||
finally {
|
||||
myResultBuffer = null;
|
||||
myRawText = null;
|
||||
myColorRegistry = null;
|
||||
myFontNameRegistry = null;
|
||||
myColors.clear();
|
||||
myForeground = 0;
|
||||
myBackground = 0;
|
||||
myFontFamily = 0;
|
||||
myFontSize = 0;
|
||||
myBold = false;
|
||||
myItalic = false;
|
||||
myIgnoreFontSize = false;
|
||||
}
|
||||
}
|
||||
|
||||
private void defineForeground(int id, @NotNull StringBuilder styleBuffer, @NotNull StringBuilder closeTagBuffer) {
|
||||
myResultBuffer.append("<font color=\"");
|
||||
appendColor(myResultBuffer, id);
|
||||
myResultBuffer.append("\">");
|
||||
styleBuffer.append("color:");
|
||||
appendColor(styleBuffer, id);
|
||||
styleBuffer.append(";");
|
||||
closeTagBuffer.insert(0, "</font>");
|
||||
}
|
||||
|
||||
private void defineBackground(int id, @NotNull StringBuilder styleBuffer, @NotNull StringBuilder closeTagBuffer) {
|
||||
myResultBuffer.append("<font bgcolor=\"");
|
||||
appendColor(myResultBuffer, id);
|
||||
myResultBuffer.append("\">");
|
||||
styleBuffer.append("background-color:");
|
||||
appendColor(styleBuffer, id);
|
||||
styleBuffer.append(";");
|
||||
closeTagBuffer.insert(0, "</font>");
|
||||
}
|
||||
|
||||
private void defineBold(@NotNull StringBuilder styleBuffer, @NotNull StringBuilder closeTagBuffer) {
|
||||
myResultBuffer.append("<b>");
|
||||
styleBuffer.append("font-weight:bold;");
|
||||
closeTagBuffer.insert(0, "</b>");
|
||||
}
|
||||
|
||||
private void defineItalic(@NotNull StringBuilder styleBuffer, @NotNull StringBuilder closeTagBuffer) {
|
||||
myResultBuffer.append("<i>");
|
||||
styleBuffer.append("font-style:italic;");
|
||||
closeTagBuffer.insert(0, "</i>");
|
||||
}
|
||||
|
||||
private void buildColorMap() {
|
||||
for (int id : myColorRegistry.getAllIds()) {
|
||||
StringBuilder b = new StringBuilder("#");
|
||||
UIUtil.appendColor(myColorRegistry.dataById(id), b);
|
||||
myColors.put(id, b.toString());
|
||||
}
|
||||
}
|
||||
|
||||
private void appendColor(StringBuilder builder, int id) {
|
||||
builder.append(myColors.get(id));
|
||||
}
|
||||
|
||||
private void appendFontFamilyRule(@NotNull StringBuilder styleBuffer, int fontFamilyId) {
|
||||
styleBuffer.append("font-family:'").append(myFontNameRegistry.dataById(fontFamilyId)).append("';");
|
||||
}
|
||||
|
||||
private static void appendFontSizeRule(@NotNull StringBuilder styleBuffer, int fontSize) {
|
||||
styleBuffer.append("font-size:").append(fontSize).append(';');
|
||||
}
|
||||
|
||||
@Override
|
||||
public void visit(@NotNull Text text) {
|
||||
boolean formattedText = myForeground > 0 || myBackground > 0 || myFontFamily > 0 || myFontSize > 0 || myBold || myItalic;
|
||||
if (!formattedText) {
|
||||
escapeAndAdd(text);
|
||||
return;
|
||||
}
|
||||
|
||||
StringBuilder styleBuffer = StringBuilderSpinAllocator.alloc();
|
||||
StringBuilder closeTagBuffer = StringBuilderSpinAllocator.alloc();
|
||||
try {
|
||||
if (myForeground > 0) {
|
||||
defineForeground(myForeground, styleBuffer, closeTagBuffer);
|
||||
}
|
||||
if (myBackground > 0) {
|
||||
defineBackground(myBackground, styleBuffer, closeTagBuffer);
|
||||
}
|
||||
if (myBold) {
|
||||
defineBold(styleBuffer, closeTagBuffer);
|
||||
}
|
||||
if (myItalic) {
|
||||
defineItalic(styleBuffer, closeTagBuffer);
|
||||
}
|
||||
if (myFontFamily > 0) {
|
||||
appendFontFamilyRule(styleBuffer, myFontFamily);
|
||||
}
|
||||
if (myFontSize > 0) {
|
||||
appendFontSizeRule(styleBuffer, myFontSize);
|
||||
}
|
||||
myResultBuffer.append("<span style=\"");
|
||||
myResultBuffer.append(styleBuffer);
|
||||
myResultBuffer.append("\">");
|
||||
escapeAndAdd(text);
|
||||
myResultBuffer.append("</span>");
|
||||
myResultBuffer.append(closeTagBuffer);
|
||||
}
|
||||
finally {
|
||||
StringBuilderSpinAllocator.dispose(styleBuffer);
|
||||
StringBuilderSpinAllocator.dispose(closeTagBuffer);
|
||||
}
|
||||
}
|
||||
|
||||
private void escapeAndAdd(Text text) {
|
||||
int start = text.getStartOffset();
|
||||
int end = text.getEndOffset();
|
||||
for (int i = start; i < end; i++) {
|
||||
char c = text.getCharAt(myRawText, i);
|
||||
switch (c) {
|
||||
case '<': myResultBuffer.append("<"); break;
|
||||
case '>': myResultBuffer.append(">"); break;
|
||||
case '&': myResultBuffer.append("&"); break;
|
||||
default: myResultBuffer.append(c);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void visit(@NotNull Foreground color) {
|
||||
myForeground = color.getId();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void visit(@NotNull Background color) {
|
||||
myBackground = color.getId();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void visit(@NotNull FontFamilyName name) {
|
||||
if (myFontNameRegistry != null) {
|
||||
myFontFamily = name.getId();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void visit(@NotNull FontStyle style) {
|
||||
myBold = (Font.BOLD & style.getStyle()) != 0;
|
||||
myItalic = (Font.ITALIC & style.getStyle()) != 0;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void visit(@NotNull FontSize size) {
|
||||
if (!myIgnoreFontSize) {
|
||||
myFontSize = size.getSize();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
protected ReaderTransferableData createTransferable(@NotNull String data) {
|
||||
return new ReaderTransferableData(data, FLAVOR);
|
||||
}
|
||||
}
|
||||
+230
@@ -0,0 +1,230 @@
|
||||
/*
|
||||
* Copyright 2000-2014 JetBrains s.r.o.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* 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.openapi.editor.richcopy;
|
||||
|
||||
import com.intellij.openapi.editor.richcopy.model.*;
|
||||
import com.intellij.openapi.editor.richcopy.view.InputStreamTransferableData;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
|
||||
import java.awt.*;
|
||||
import java.awt.datatransfer.DataFlavor;
|
||||
import java.io.UnsupportedEncodingException;
|
||||
|
||||
/**
|
||||
* @author Denis Zhdanov
|
||||
* @since 3/25/13 2:18 PM
|
||||
*/
|
||||
public class RtfCopyPasteProcessor extends BaseTextWithMarkupCopyPasteProcessor<InputStreamTransferableData> {
|
||||
@NotNull public static final DataFlavor FLAVOR = new DataFlavor("text/rtf;class=java.io.InputStream", "RTF text");
|
||||
|
||||
private static final String CHARSET = "US-ASCII";
|
||||
|
||||
@NotNull private static final String HEADER_PREFIX = "{\\rtf1\\ansi\\deff0";
|
||||
@NotNull private static final String HEADER_SUFFIX = "}";
|
||||
@NotNull private static final String TAB = "\\tab\n";
|
||||
@NotNull private static final String NEW_LINE = "\\line\n";
|
||||
@NotNull private static final String BOLD = "\\b";
|
||||
@NotNull private static final String ITALIC = "\\i";
|
||||
@NotNull private static final String PLAIN = "\\plain\n";
|
||||
|
||||
public RtfCopyPasteProcessor(TextWithMarkupProcessor processor) {
|
||||
super(processor);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void buildStringRepresentation(@NotNull final StringBuilder buffer,
|
||||
@NotNull final CharSequence rawText,
|
||||
@NotNull final SyntaxInfo syntaxInfo,
|
||||
final int maxLength) {
|
||||
header(syntaxInfo, buffer, new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
rectangularBackground(syntaxInfo, buffer, new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
content(syntaxInfo, buffer, rawText, maxLength);
|
||||
}
|
||||
});
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
@Override
|
||||
protected InputStreamTransferableData createTransferable(@NotNull String data) {
|
||||
try {
|
||||
return new InputStreamTransferableData(data.getBytes(CHARSET), FLAVOR);
|
||||
}
|
||||
catch (UnsupportedEncodingException e) {
|
||||
throw new RuntimeException(e); // shouldn't happen
|
||||
}
|
||||
}
|
||||
|
||||
private static void header(@NotNull SyntaxInfo syntaxInfo, @NotNull StringBuilder buffer, @NotNull Runnable next) {
|
||||
buffer.append(HEADER_PREFIX);
|
||||
|
||||
// Color table.
|
||||
buffer.append("{\\colortbl;");
|
||||
ColorRegistry colorRegistry = syntaxInfo.getColorRegistry();
|
||||
for (int id : colorRegistry.getAllIds()) {
|
||||
Color color = colorRegistry.dataById(id);
|
||||
buffer.append(String.format("\\red%d\\green%d\\blue%d;", color.getRed(), color.getGreen(), color.getBlue()));
|
||||
}
|
||||
buffer.append("}\n");
|
||||
|
||||
// Font table.
|
||||
buffer.append("{\\fonttbl");
|
||||
FontNameRegistry fontNameRegistry = syntaxInfo.getFontNameRegistry();
|
||||
for (int id : fontNameRegistry.getAllIds()) {
|
||||
String fontName = fontNameRegistry.dataById(id);
|
||||
buffer.append(String.format("{\\f%d %s;}", id, fontName));
|
||||
}
|
||||
buffer.append("}\n");
|
||||
|
||||
next.run();
|
||||
buffer.append(HEADER_SUFFIX);
|
||||
}
|
||||
|
||||
private static void rectangularBackground(@NotNull SyntaxInfo syntaxInfo, @NotNull StringBuilder buffer, @NotNull Runnable next) {
|
||||
buffer.append("\n\\s0\\box\\brdrhair\\brdrcf").append(syntaxInfo.getDefaultForeground()).append("\\brsp317").append("\\cbpat")
|
||||
.append(syntaxInfo.getDefaultBackground());
|
||||
saveBackground(buffer, syntaxInfo.getDefaultBackground());
|
||||
next.run();
|
||||
buffer.append("\\par");
|
||||
}
|
||||
|
||||
private static void content(@NotNull SyntaxInfo syntaxInfo, @NotNull StringBuilder buffer, @NotNull CharSequence rawText, int maxLength) {
|
||||
MyVisitor visitor = new MyVisitor(buffer, rawText);
|
||||
for (OutputInfo info : syntaxInfo.getOutputInfos()) {
|
||||
info.invite(visitor);
|
||||
if (buffer.length() > maxLength) {
|
||||
buffer.append("... truncated ...");
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private static void saveBackground(@NotNull StringBuilder buffer, int id) {
|
||||
buffer.append("\\cb").append(id);
|
||||
}
|
||||
|
||||
private static void saveForeground(@NotNull StringBuilder buffer, int id) {
|
||||
buffer.append("\\cf").append(id);
|
||||
}
|
||||
|
||||
private static void saveFontName(@NotNull StringBuilder buffer, int id) {
|
||||
buffer.append("\\f").append(id);
|
||||
}
|
||||
|
||||
private static void saveFontSize(@NotNull StringBuilder buffer, int size) {
|
||||
buffer.append("\\fs").append(size * 2);
|
||||
}
|
||||
|
||||
private static class MyVisitor implements OutputInfoVisitor {
|
||||
|
||||
@NotNull private final StringBuilder myBuffer;
|
||||
@NotNull private final CharSequence myRawText;
|
||||
|
||||
private int myBackgroundId = -1;
|
||||
private int myForegroundId = -1;
|
||||
private int myFontNameId = -1;
|
||||
private int myFontStyle = -1;
|
||||
private int myFontSize = -1;
|
||||
|
||||
MyVisitor(@NotNull StringBuilder buffer, @NotNull CharSequence rawText) {
|
||||
myBuffer = buffer;
|
||||
myRawText = rawText;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void visit(@NotNull Text text) {
|
||||
myBuffer.append("\n");
|
||||
for (int i = text.getStartOffset(), limit = text.getEndOffset(); i < limit; i++) {
|
||||
char c = text.getCharAt(myRawText, i);
|
||||
if (c > 127) {
|
||||
// Escape non-ascii symbols.
|
||||
myBuffer.append(String.format("\\u%04d?", (int)c));
|
||||
continue;
|
||||
}
|
||||
|
||||
switch (c) {
|
||||
case '\t':
|
||||
myBuffer.append(TAB);
|
||||
continue;
|
||||
case '\n':
|
||||
myBuffer.append(NEW_LINE);
|
||||
continue;
|
||||
case '\\':
|
||||
case '{':
|
||||
case '}':
|
||||
myBuffer.append('\\');
|
||||
}
|
||||
myBuffer.append(c);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void visit(@NotNull Foreground color) {
|
||||
saveForeground(myBuffer, color.getId());
|
||||
myForegroundId = color.getId();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void visit(@NotNull Background color) {
|
||||
saveBackground(myBuffer, color.getId());
|
||||
myBackgroundId = color.getId();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void visit(@NotNull FontFamilyName name) {
|
||||
saveFontName(myBuffer, name.getId());
|
||||
myFontNameId = name.getId();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void visit(@NotNull FontSize size) {
|
||||
saveFontSize(myBuffer, size.getSize());
|
||||
myFontSize = size.getSize();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void visit(@NotNull FontStyle style) {
|
||||
// Reset formatting settings
|
||||
myBuffer.append(PLAIN);
|
||||
|
||||
// Restore target formatting settings.
|
||||
if (myForegroundId >= 0) {
|
||||
saveForeground(myBuffer, myForegroundId);
|
||||
}
|
||||
if (myBackgroundId >= 0) {
|
||||
saveBackground(myBuffer, myBackgroundId);
|
||||
}
|
||||
if (myFontNameId >= 0) {
|
||||
saveFontName(myBuffer, myFontNameId);
|
||||
}
|
||||
if (myFontSize > 0) {
|
||||
saveFontSize(myBuffer, myFontSize);
|
||||
}
|
||||
|
||||
myFontStyle = style.getStyle();
|
||||
if ((myFontStyle & Font.ITALIC) > 0) {
|
||||
myBuffer.append(ITALIC);
|
||||
}
|
||||
if ((myFontStyle & Font.BOLD) > 0) {
|
||||
myBuffer.append(BOLD);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
+23
@@ -0,0 +1,23 @@
|
||||
/*
|
||||
* Copyright 2000-2014 JetBrains s.r.o.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* 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.openapi.editor.richcopy;
|
||||
|
||||
import com.intellij.openapi.editor.richcopy.model.SyntaxInfo;
|
||||
|
||||
public interface TextWithMarkupBuilder {
|
||||
void reset();
|
||||
void build(CharSequence charSequence, SyntaxInfo syntaxInfo);
|
||||
}
|
||||
+803
@@ -0,0 +1,803 @@
|
||||
/*
|
||||
* Copyright 2000-2014 JetBrains s.r.o.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* 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.openapi.editor.richcopy;
|
||||
|
||||
import com.intellij.codeInsight.daemon.impl.HighlightInfo;
|
||||
import com.intellij.codeInsight.daemon.impl.HighlightInfoType;
|
||||
import com.intellij.codeInsight.editorActions.CopyPastePostProcessor;
|
||||
import com.intellij.codeInsight.editorActions.TextBlockTransferableData;
|
||||
import com.intellij.ide.highlighter.HighlighterFactory;
|
||||
import com.intellij.openapi.diagnostic.Logger;
|
||||
import com.intellij.openapi.editor.Document;
|
||||
import com.intellij.openapi.editor.Editor;
|
||||
import com.intellij.openapi.editor.RangeMarker;
|
||||
import com.intellij.openapi.editor.colors.EditorColorsScheme;
|
||||
import com.intellij.openapi.editor.colors.TextAttributesKey;
|
||||
import com.intellij.openapi.editor.ex.DisposableIterator;
|
||||
import com.intellij.openapi.editor.ex.MarkupModelEx;
|
||||
import com.intellij.openapi.editor.ex.RangeHighlighterEx;
|
||||
import com.intellij.openapi.editor.highlighter.EditorHighlighter;
|
||||
import com.intellij.openapi.editor.highlighter.HighlighterIterator;
|
||||
import com.intellij.openapi.editor.impl.ComplementaryFontsRegistry;
|
||||
import com.intellij.openapi.editor.impl.DocumentMarkupModel;
|
||||
import com.intellij.openapi.editor.markup.HighlighterLayer;
|
||||
import com.intellij.openapi.editor.markup.MarkupModel;
|
||||
import com.intellij.openapi.editor.markup.TextAttributes;
|
||||
import com.intellij.openapi.editor.richcopy.model.*;
|
||||
import com.intellij.openapi.editor.richcopy.settings.RichCopySettings;
|
||||
import com.intellij.openapi.project.Project;
|
||||
import com.intellij.openapi.util.Pair;
|
||||
import com.intellij.openapi.util.Ref;
|
||||
import com.intellij.openapi.util.registry.Registry;
|
||||
import com.intellij.psi.PsiFile;
|
||||
import com.intellij.psi.TokenType;
|
||||
import com.intellij.util.containers.ContainerUtilRt;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
import java.awt.*;
|
||||
import java.awt.datatransfer.Transferable;
|
||||
import java.util.*;
|
||||
import java.util.List;
|
||||
import java.util.Queue;
|
||||
|
||||
public class TextWithMarkupProcessor implements CopyPastePostProcessor<TextBlockTransferableData> {
|
||||
private static final Logger LOG = Logger.getInstance("#" + TextWithMarkupProcessor.class.getName());
|
||||
|
||||
private final List<TextWithMarkupBuilder> myBuilders = new ArrayList<TextWithMarkupBuilder>();
|
||||
|
||||
public void addBuilder(TextWithMarkupBuilder builder) {
|
||||
myBuilders.add(builder);
|
||||
}
|
||||
|
||||
@Nullable
|
||||
@Override
|
||||
public TextBlockTransferableData collectTransferableData(PsiFile file, Editor editor, int[] startOffsets, int[] endOffsets) {
|
||||
if (!Registry.is("editor.richcopy.enable")) {
|
||||
return null;
|
||||
}
|
||||
|
||||
try {
|
||||
for (TextWithMarkupBuilder builder : myBuilders) {
|
||||
builder.reset();
|
||||
}
|
||||
|
||||
RichCopySettings settings = RichCopySettings.getInstance();
|
||||
Document document = editor.getDocument();
|
||||
final int indentSymbolsToStrip;
|
||||
final int firstLineStartOffset;
|
||||
if (settings.isStripIndents() && startOffsets.length == 1) {
|
||||
Pair<Integer, Integer> p = calcIndentSymbolsToStrip(document, startOffsets[0], endOffsets[0]);
|
||||
firstLineStartOffset = p.first;
|
||||
indentSymbolsToStrip = p.second;
|
||||
}
|
||||
else {
|
||||
firstLineStartOffset = startOffsets[0];
|
||||
indentSymbolsToStrip = 0;
|
||||
}
|
||||
logInitial(document, startOffsets, endOffsets, indentSymbolsToStrip, firstLineStartOffset);
|
||||
CharSequence text = document.getCharsSequence();
|
||||
EditorColorsScheme schemeToUse = settings.getColorsScheme(editor.getColorsScheme());
|
||||
EditorHighlighter highlighter = HighlighterFactory.createHighlighter(file.getVirtualFile(), schemeToUse, file.getProject());
|
||||
highlighter.setText(text);
|
||||
MarkupModel markupModel = DocumentMarkupModel.forDocument(document, file.getProject(), false);
|
||||
Context context = new Context(text, schemeToUse, indentSymbolsToStrip);
|
||||
|
||||
for (int i = 0; i < startOffsets.length; i++) {
|
||||
int startOffsetToUse;
|
||||
if (i == 0) {
|
||||
startOffsetToUse = firstLineStartOffset;
|
||||
}
|
||||
else {
|
||||
startOffsetToUse = startOffsets[i];
|
||||
context.outputInfos.add(new Text('\n'));
|
||||
}
|
||||
int endOffsetToUse = endOffsets[i];
|
||||
context.reset();
|
||||
if (endOffsetToUse <= startOffsetToUse) {
|
||||
continue;
|
||||
}
|
||||
DisposableIterator<SegmentInfo> it = aggregateSyntaxInfo(schemeToUse,
|
||||
wrap(highlighter, text, schemeToUse, startOffsetToUse, endOffsetToUse),
|
||||
wrap(markupModel, text, schemeToUse, startOffsetToUse, endOffsetToUse));
|
||||
try {
|
||||
while (it.hasNext()) {
|
||||
SegmentInfo info = it.next();
|
||||
if (info.startOffset >= endOffsetToUse) {
|
||||
break;
|
||||
}
|
||||
context.onNewData(info);
|
||||
}
|
||||
}
|
||||
finally {
|
||||
it.dispose();
|
||||
}
|
||||
context.onIterationEnd(endOffsetToUse);
|
||||
}
|
||||
SyntaxInfo syntaxInfo = context.finish();
|
||||
logSyntaxInfo(syntaxInfo);
|
||||
|
||||
for (TextWithMarkupBuilder builder : myBuilders) {
|
||||
builder.build(text, syntaxInfo);
|
||||
}
|
||||
}
|
||||
catch (Exception e) {
|
||||
// catching the exception so that the rest of copy/paste functionality can still work fine
|
||||
LOG.error(e);
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
@Nullable
|
||||
@Override
|
||||
public TextBlockTransferableData extractTransferableData(Transferable content) {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void processTransferableData(Project project,
|
||||
Editor editor,
|
||||
RangeMarker bounds,
|
||||
int caretOffset,
|
||||
Ref<Boolean> indented,
|
||||
TextBlockTransferableData value) {
|
||||
|
||||
}
|
||||
|
||||
private static void logInitial(@NotNull Document document,
|
||||
@NotNull int[] startOffsets,
|
||||
@NotNull int[] endOffsets,
|
||||
int indentSymbolsToStrip,
|
||||
int firstLineStartOffset)
|
||||
{
|
||||
if (!Registry.is("editor.richcopy.debug")) {
|
||||
return;
|
||||
}
|
||||
|
||||
StringBuilder buffer = new StringBuilder();
|
||||
CharSequence text = document.getCharsSequence();
|
||||
for (int i = 0; i < startOffsets.length; i++) {
|
||||
int start = startOffsets[i];
|
||||
int lineStart = document.getLineStartOffset(document.getLineNumber(start));
|
||||
int end = endOffsets[i];
|
||||
int lineEnd = document.getLineEndOffset(document.getLineNumber(end));
|
||||
buffer.append(" region #").append(i).append(": ").append(start).append('-').append(end).append(", text at range ")
|
||||
.append(lineStart).append('-').append(lineEnd).append(": \n'").append(text.subSequence(lineStart, lineEnd)).append("'\n");
|
||||
}
|
||||
if (buffer.length() > 0) {
|
||||
buffer.setLength(buffer.length() - 1);
|
||||
}
|
||||
LOG.info(String.format(
|
||||
"Preparing syntax-aware text. Given: %s selection, indent symbols to strip=%d, first line start offset=%d, selected text:%n%s",
|
||||
startOffsets.length > 1 ? "block" : "regular", indentSymbolsToStrip, firstLineStartOffset, buffer
|
||||
));
|
||||
}
|
||||
|
||||
private static void logSyntaxInfo(@NotNull SyntaxInfo info) {
|
||||
if (!Registry.is("editor.richcopy.debug")) {
|
||||
return;
|
||||
}
|
||||
LOG.info("Constructed syntax info: " + info);
|
||||
}
|
||||
|
||||
private static Pair<Integer/* start offset to use */, Integer /* indent symbols to strip */> calcIndentSymbolsToStrip(
|
||||
@NotNull Document document, int startOffset, int endOffset)
|
||||
{
|
||||
int startLine = document.getLineNumber(startOffset);
|
||||
int endLine = document.getLineNumber(endOffset);
|
||||
CharSequence text = document.getCharsSequence();
|
||||
int maximumCommonIndent = Integer.MAX_VALUE;
|
||||
int firstLineStart = startOffset;
|
||||
int firstLineEnd = startOffset;
|
||||
for (int line = startLine; line <= endLine; line++) {
|
||||
int lineStartOffset = document.getLineStartOffset(line);
|
||||
int lineEndOffset = document.getLineEndOffset(line);
|
||||
if (line == startLine) {
|
||||
firstLineStart = lineStartOffset;
|
||||
firstLineEnd = lineEndOffset;
|
||||
}
|
||||
int nonWsOffset = lineEndOffset;
|
||||
for (int i = lineStartOffset; i < lineEndOffset && (i - lineStartOffset) < maximumCommonIndent && i < endOffset; i++) {
|
||||
char c = text.charAt(i);
|
||||
if (c != ' ' && c != '\t') {
|
||||
nonWsOffset = i;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (nonWsOffset >= lineEndOffset) {
|
||||
continue; // Blank line
|
||||
}
|
||||
int indent = nonWsOffset - lineStartOffset;
|
||||
maximumCommonIndent = Math.min(maximumCommonIndent, indent);
|
||||
if (maximumCommonIndent == 0) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
int startOffsetToUse = Math.min(firstLineEnd, Math.max(startOffset, firstLineStart + maximumCommonIndent));
|
||||
return Pair.create(startOffsetToUse, maximumCommonIndent);
|
||||
}
|
||||
|
||||
private static DisposableIterator<SegmentInfo> aggregateSyntaxInfo(@NotNull EditorColorsScheme colorsScheme,
|
||||
@NotNull final DisposableIterator<List<SegmentInfo>>... iterators)
|
||||
{
|
||||
final Color defaultForeground = colorsScheme.getDefaultForeground();
|
||||
final Color defaultBackground = colorsScheme.getDefaultBackground();
|
||||
return new DisposableIterator<SegmentInfo>() {
|
||||
|
||||
@NotNull private final Queue<SegmentInfo> myInfos = new PriorityQueue<SegmentInfo>();
|
||||
@NotNull private final Map<SegmentInfo, DisposableIterator<List<SegmentInfo>>> myEndMarkers
|
||||
= new IdentityHashMap<SegmentInfo, DisposableIterator<List<SegmentInfo>>>();
|
||||
|
||||
{
|
||||
for (DisposableIterator<List<SegmentInfo>> iterator : iterators) {
|
||||
extract(iterator);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean hasNext() {
|
||||
return !myInfos.isEmpty();
|
||||
}
|
||||
|
||||
@Override
|
||||
public SegmentInfo next() {
|
||||
SegmentInfo result = myInfos.remove();
|
||||
DisposableIterator<List<SegmentInfo>> iterator = myEndMarkers.remove(result);
|
||||
if (iterator != null) {
|
||||
extract(iterator);
|
||||
}
|
||||
while (!myInfos.isEmpty()) {
|
||||
SegmentInfo toMerge = myInfos.peek();
|
||||
if (toMerge.endOffset > result.endOffset) {
|
||||
break;
|
||||
}
|
||||
myInfos.remove();
|
||||
result = merge(result, toMerge);
|
||||
DisposableIterator<List<SegmentInfo>> it = myEndMarkers.remove(toMerge);
|
||||
if (it != null) {
|
||||
extract(it);
|
||||
}
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
@NotNull
|
||||
private SegmentInfo merge(@NotNull SegmentInfo info1, @NotNull SegmentInfo info2) {
|
||||
Color background = info1.background;
|
||||
if (background == null || defaultBackground.equals(background)) {
|
||||
background = info2.background;
|
||||
}
|
||||
|
||||
Color foreground = info1.foreground;
|
||||
if (foreground == null || defaultForeground.equals(foreground)) {
|
||||
foreground = info2.foreground;
|
||||
}
|
||||
|
||||
int fontStyle = info1.fontStyle;
|
||||
if (fontStyle == Font.PLAIN) {
|
||||
fontStyle = info2.fontStyle;
|
||||
}
|
||||
return new SegmentInfo(foreground, background, info1.fontFamilyName, fontStyle, info1.fontSize, info1.startOffset, info1.endOffset);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void remove() {
|
||||
throw new UnsupportedOperationException();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void dispose() {
|
||||
for (DisposableIterator<List<SegmentInfo>> iterator : iterators) {
|
||||
iterator.dispose();
|
||||
}
|
||||
}
|
||||
|
||||
private void extract(@NotNull DisposableIterator<List<SegmentInfo>> iterator) {
|
||||
while (iterator.hasNext()) {
|
||||
List<SegmentInfo> infos = iterator.next();
|
||||
if (infos.isEmpty()) {
|
||||
continue;
|
||||
}
|
||||
myInfos.addAll(infos);
|
||||
myEndMarkers.put(infos.get(infos.size() - 1), iterator);
|
||||
break;
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
@NotNull
|
||||
private static DisposableIterator<List<SegmentInfo>> wrap(@NotNull final EditorHighlighter highlighter,
|
||||
@NotNull final CharSequence text,
|
||||
@NotNull final EditorColorsScheme colorsScheme,
|
||||
final int startOffset,
|
||||
final int endOffset)
|
||||
{
|
||||
final HighlighterIterator highlighterIterator = highlighter.createIterator(startOffset);
|
||||
return new DisposableIterator<List<SegmentInfo>>() {
|
||||
|
||||
@Nullable private List<SegmentInfo> myCached;
|
||||
|
||||
@Override
|
||||
public boolean hasNext() {
|
||||
return myCached != null || updateCached();
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<SegmentInfo> next() {
|
||||
if (myCached != null) {
|
||||
List<SegmentInfo> result = myCached;
|
||||
myCached = null;
|
||||
return result;
|
||||
}
|
||||
|
||||
if (!updateCached()) {
|
||||
throw new UnsupportedOperationException();
|
||||
}
|
||||
return myCached;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void remove() {
|
||||
throw new UnsupportedOperationException();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void dispose() {
|
||||
}
|
||||
|
||||
private boolean updateCached() {
|
||||
if (highlighterIterator.atEnd()) {
|
||||
return false;
|
||||
}
|
||||
int tokenStart = Math.max(highlighterIterator.getStart(), startOffset);
|
||||
if (tokenStart >= endOffset) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (highlighterIterator.getTokenType() == TokenType.BAD_CHARACTER) {
|
||||
// Skip syntax errors.
|
||||
highlighterIterator.advance();
|
||||
return updateCached();
|
||||
}
|
||||
TextAttributes attributes = highlighterIterator.getTextAttributes();
|
||||
int tokenEnd = Math.min(highlighterIterator.getEnd(), endOffset);
|
||||
myCached = SegmentInfo.produce(attributes, text, colorsScheme, tokenStart, tokenEnd);
|
||||
highlighterIterator.advance();
|
||||
return true;
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
@NotNull
|
||||
private static DisposableIterator<List<SegmentInfo>> wrap(@NotNull MarkupModel model,
|
||||
@NotNull final CharSequence charSequence,
|
||||
@NotNull final EditorColorsScheme colorsScheme,
|
||||
final int startOffset,
|
||||
final int endOffset)
|
||||
{
|
||||
if (!(model instanceof MarkupModelEx)) {
|
||||
return DisposableIterator.EMPTY;
|
||||
}
|
||||
final DisposableIterator<RangeHighlighterEx> iterator = ((MarkupModelEx)model).overlappingIterator(startOffset, endOffset);
|
||||
final Color defaultForeground = colorsScheme.getDefaultForeground();
|
||||
final Color defaultBackground = colorsScheme.getDefaultBackground();
|
||||
return new DisposableIterator<List<SegmentInfo>>() {
|
||||
|
||||
@Nullable private List<SegmentInfo> myCached;
|
||||
|
||||
@Override
|
||||
public boolean hasNext() {
|
||||
return myCached != null || updateCached();
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<SegmentInfo> next() {
|
||||
if (myCached != null) {
|
||||
List<SegmentInfo> result = myCached;
|
||||
myCached = null;
|
||||
return result;
|
||||
}
|
||||
|
||||
if (!updateCached()) {
|
||||
throw new UnsupportedOperationException();
|
||||
}
|
||||
return myCached;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void remove() {
|
||||
throw new UnsupportedOperationException();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void dispose() {
|
||||
iterator.dispose();
|
||||
}
|
||||
|
||||
private boolean updateCached() {
|
||||
if (!iterator.hasNext()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
RangeHighlighterEx highlighter = iterator.next();
|
||||
while (highlighter == null
|
||||
|| !highlighter.isValid()
|
||||
|| !isInterestedHighlightLayer(highlighter.getLayer()))
|
||||
{
|
||||
if (!iterator.hasNext()) {
|
||||
return false;
|
||||
}
|
||||
highlighter = iterator.next();
|
||||
}
|
||||
|
||||
int tokenStart = Math.max(highlighter.getStartOffset(), startOffset);
|
||||
if (tokenStart >= endOffset) {
|
||||
return false;
|
||||
}
|
||||
|
||||
TextAttributes attributes = null;
|
||||
Object tooltip = highlighter.getErrorStripeTooltip();
|
||||
if (tooltip instanceof HighlightInfo) {
|
||||
HighlightInfo info = (HighlightInfo)tooltip;
|
||||
TextAttributesKey key = info.forcedTextAttributesKey;
|
||||
if (key == null) {
|
||||
HighlightInfoType type = info.type;
|
||||
key = type.getAttributesKey();
|
||||
}
|
||||
if (key != null) {
|
||||
attributes = colorsScheme.getAttributes(key);
|
||||
}
|
||||
}
|
||||
|
||||
if (attributes == null) {
|
||||
return updateCached();
|
||||
}
|
||||
Color foreground = attributes.getForegroundColor();
|
||||
Color background = attributes.getBackgroundColor();
|
||||
if ((foreground == null || defaultForeground.equals(foreground))
|
||||
&& (background == null || defaultBackground.equals(background))
|
||||
&& attributes.getFontType() == Font.PLAIN)
|
||||
{
|
||||
return updateCached();
|
||||
}
|
||||
|
||||
int tokenEnd = Math.min(highlighter.getEndOffset(), endOffset);
|
||||
//noinspection ConstantConditions
|
||||
myCached = SegmentInfo.produce(attributes, charSequence, colorsScheme, tokenStart, tokenEnd);
|
||||
return true;
|
||||
}
|
||||
|
||||
private boolean isInterestedHighlightLayer(int layer) {
|
||||
return layer != HighlighterLayer.CARET_ROW && layer != HighlighterLayer.SELECTION && layer != HighlighterLayer.ERROR
|
||||
&& layer != HighlighterLayer.WARNING;
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
private static class Context {
|
||||
|
||||
@NotNull public final List<OutputInfo> outputInfos = ContainerUtilRt.newArrayList();
|
||||
|
||||
@NotNull private final ColorRegistry myColorRegistry = new ColorRegistry();
|
||||
@NotNull private final FontNameRegistry myFontNameRegistry = new FontNameRegistry();
|
||||
|
||||
@NotNull private final CharSequence myText;
|
||||
@NotNull private final Color myDefaultForeground;
|
||||
@NotNull private final Color myDefaultBackground;
|
||||
|
||||
@Nullable private Color myBackground;
|
||||
@Nullable private Color myForeground;
|
||||
@Nullable private String myFontFamilyName;
|
||||
|
||||
private final int myIndentSymbolsToStrip;
|
||||
|
||||
private int myFontStyle = -1;
|
||||
private int myFontSize = -1;
|
||||
private int myStartOffset = -1;
|
||||
|
||||
private int mySingleFontSize;
|
||||
|
||||
private int myIndentSymbolsToStripAtCurrentLine;
|
||||
|
||||
Context(@NotNull CharSequence charSequence, @NotNull EditorColorsScheme scheme, int indentSymbolsToStrip) {
|
||||
myText = charSequence;
|
||||
myDefaultForeground = scheme.getDefaultForeground();
|
||||
myDefaultBackground = scheme.getDefaultBackground();
|
||||
myIndentSymbolsToStrip = indentSymbolsToStrip;
|
||||
}
|
||||
|
||||
public void reset() {
|
||||
myStartOffset = -1;
|
||||
myIndentSymbolsToStripAtCurrentLine = 0;
|
||||
}
|
||||
|
||||
public void onNewData(@NotNull SegmentInfo info) {
|
||||
if (myStartOffset < 0) {
|
||||
myStartOffset = info.startOffset;
|
||||
}
|
||||
|
||||
boolean whiteSpacesOnly = containsWhiteSpacesOnly(info);
|
||||
|
||||
processBackground(info);
|
||||
if (!whiteSpacesOnly) {
|
||||
processForeground(info);
|
||||
processFontFamilyName(info);
|
||||
processFontStyle(info);
|
||||
processFontSize(info);
|
||||
}
|
||||
}
|
||||
|
||||
private boolean containsWhiteSpacesOnly(@NotNull SegmentInfo info) {
|
||||
for (int i = info.startOffset, limit = info.endOffset; i < limit; i++) {
|
||||
char c = myText.charAt(i);
|
||||
if (c != ' ' && c != '\t' && c != '\n') {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
private void processFontStyle(@NotNull SegmentInfo info) {
|
||||
if (info.fontStyle != myFontStyle) {
|
||||
addTextIfPossible(info.startOffset);
|
||||
outputInfos.add(new FontStyle(info.fontStyle));
|
||||
myFontStyle = info.fontStyle;
|
||||
}
|
||||
}
|
||||
|
||||
private void processFontSize(@NotNull SegmentInfo info) {
|
||||
if (mySingleFontSize == 0) {
|
||||
mySingleFontSize = info.fontSize;
|
||||
}
|
||||
else if (mySingleFontSize > 0 && mySingleFontSize != info.fontSize) {
|
||||
mySingleFontSize = -1;
|
||||
}
|
||||
if (info.fontSize != myFontSize) {
|
||||
addTextIfPossible(info.startOffset);
|
||||
outputInfos.add(new FontSize(info.fontSize));
|
||||
myFontSize = info.fontSize;
|
||||
}
|
||||
}
|
||||
|
||||
private void processFontFamilyName(@NotNull SegmentInfo info) {
|
||||
String fontFamilyName = FontMapper.getPhysicalFontName(info.fontFamilyName);
|
||||
if (!fontFamilyName.equals(myFontFamilyName)) {
|
||||
addTextIfPossible(info.startOffset);
|
||||
outputInfos.add(new FontFamilyName(myFontNameRegistry.getId(fontFamilyName)));
|
||||
myFontFamilyName = fontFamilyName;
|
||||
}
|
||||
}
|
||||
|
||||
private void processForeground(@NotNull SegmentInfo info) {
|
||||
if (myForeground == null && info.foreground != null) {
|
||||
addTextIfPossible(info.startOffset);
|
||||
myForeground = info.foreground;
|
||||
outputInfos.add(new Foreground(myColorRegistry.getId(info.foreground)));
|
||||
}
|
||||
else if (myForeground != null) {
|
||||
Color c = info.foreground == null ? myDefaultForeground : info.foreground;
|
||||
if (!myForeground.equals(c)) {
|
||||
addTextIfPossible(info.startOffset);
|
||||
outputInfos.add(new Foreground(myColorRegistry.getId(c)));
|
||||
myForeground = c;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void processBackground(@NotNull SegmentInfo info) {
|
||||
if (myBackground == null && info.background != null && !myDefaultBackground.equals(info.background)) {
|
||||
addTextIfPossible(info.startOffset);
|
||||
myBackground = info.background;
|
||||
outputInfos.add(new Background(myColorRegistry.getId(info.background)));
|
||||
}
|
||||
else if (myBackground != null) {
|
||||
Color c = info.background == null ? myDefaultBackground : info.background;
|
||||
if (!myBackground.equals(c)) {
|
||||
addTextIfPossible(info.startOffset);
|
||||
outputInfos.add(new Background(myColorRegistry.getId(c)));
|
||||
myBackground = c;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void addTextIfPossible(int endOffset) {
|
||||
if (endOffset <= myStartOffset) {
|
||||
return;
|
||||
}
|
||||
|
||||
for (int i = myStartOffset; i < endOffset; i++) {
|
||||
char c = myText.charAt(i);
|
||||
switch (c) {
|
||||
case '\n':
|
||||
myIndentSymbolsToStripAtCurrentLine = myIndentSymbolsToStrip;
|
||||
outputInfos.add(new Text(myStartOffset, i + 1));
|
||||
myStartOffset = i + 1;
|
||||
break;
|
||||
// Intended fall-through.
|
||||
case ' ':
|
||||
case '\t':
|
||||
if (myIndentSymbolsToStripAtCurrentLine > 0) {
|
||||
myIndentSymbolsToStripAtCurrentLine--;
|
||||
myStartOffset++;
|
||||
continue;
|
||||
}
|
||||
default: myIndentSymbolsToStripAtCurrentLine = 0;
|
||||
}
|
||||
}
|
||||
|
||||
if (myStartOffset < endOffset) {
|
||||
outputInfos.add(new Text(myStartOffset, endOffset));
|
||||
myStartOffset = endOffset;
|
||||
}
|
||||
}
|
||||
|
||||
public void onIterationEnd(int endOffset) {
|
||||
addTextIfPossible(endOffset);
|
||||
}
|
||||
|
||||
@NotNull
|
||||
public SyntaxInfo finish() {
|
||||
int foreground = myColorRegistry.getId(myDefaultForeground);
|
||||
int background = myColorRegistry.getId(myDefaultBackground);
|
||||
myColorRegistry.seal();
|
||||
myFontNameRegistry.seal();
|
||||
return new SyntaxInfo(outputInfos, foreground, background, mySingleFontSize, myFontNameRegistry, myColorRegistry);
|
||||
}
|
||||
}
|
||||
|
||||
private static class SegmentInfo implements Comparable<SegmentInfo> {
|
||||
|
||||
@Nullable public final Color foreground;
|
||||
@Nullable public final Color background;
|
||||
@NotNull public final String fontFamilyName;
|
||||
|
||||
public final int fontStyle;
|
||||
public final int fontSize;
|
||||
public final int startOffset;
|
||||
public final int endOffset;
|
||||
|
||||
SegmentInfo(@Nullable Color foreground,
|
||||
@Nullable Color background,
|
||||
@NotNull String fontFamilyName,
|
||||
int fontStyle,
|
||||
int fontSize,
|
||||
int startOffset,
|
||||
int endOffset)
|
||||
{
|
||||
this.foreground = foreground;
|
||||
this.background = background;
|
||||
this.fontFamilyName = fontFamilyName;
|
||||
this.fontStyle = fontStyle;
|
||||
this.fontSize = fontSize;
|
||||
this.startOffset = startOffset;
|
||||
this.endOffset = endOffset;
|
||||
}
|
||||
|
||||
@NotNull
|
||||
public static List<SegmentInfo> produce(@NotNull TextAttributes attribute,
|
||||
@NotNull CharSequence text,
|
||||
@NotNull EditorColorsScheme colorsScheme,
|
||||
int start,
|
||||
int end)
|
||||
{
|
||||
if (end <= start) {
|
||||
return Collections.emptyList();
|
||||
}
|
||||
List<SegmentInfo> result = ContainerUtilRt.newArrayList();
|
||||
int currentStart = start;
|
||||
int fontSize = colorsScheme.getEditorFontSize();
|
||||
int fontStyle = attribute.getFontType();
|
||||
String defaultFontFamily = colorsScheme.getEditorFontName();
|
||||
Font font = ComplementaryFontsRegistry.getFontAbleToDisplay(text.charAt(start), fontSize, fontStyle, defaultFontFamily).getFont();
|
||||
String currentFontFamilyName = font.getFamily();
|
||||
int currentFontSize = font.getSize();
|
||||
String candidateFontFamilyName;
|
||||
int candidateFontSize;
|
||||
for (int i = start + 1; i < end; i++) {
|
||||
font = ComplementaryFontsRegistry.getFontAbleToDisplay(text.charAt(i), fontSize, fontStyle, defaultFontFamily).getFont();
|
||||
candidateFontFamilyName = font.getFamily();
|
||||
candidateFontSize = font.getSize();
|
||||
if (!candidateFontFamilyName.equals(currentFontFamilyName) || currentFontSize != candidateFontSize) {
|
||||
result.add(new SegmentInfo(attribute.getForegroundColor(),
|
||||
attribute.getBackgroundColor(),
|
||||
currentFontFamilyName,
|
||||
fontStyle,
|
||||
currentFontSize,
|
||||
currentStart,
|
||||
i
|
||||
));
|
||||
currentStart = i;
|
||||
currentFontFamilyName = candidateFontFamilyName;
|
||||
currentFontSize = candidateFontSize;
|
||||
}
|
||||
}
|
||||
|
||||
if (currentStart < end) {
|
||||
result.add(new SegmentInfo(attribute.getForegroundColor(),
|
||||
attribute.getBackgroundColor(),
|
||||
currentFontFamilyName,
|
||||
fontStyle,
|
||||
currentFontSize,
|
||||
currentStart,
|
||||
end
|
||||
));
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int compareTo(@NotNull SegmentInfo o) {
|
||||
return startOffset - o.startOffset;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
int result = foreground != null ? foreground.hashCode() : 0;
|
||||
result = 31 * result + (background != null ? background.hashCode() : 0);
|
||||
result = 31 * result + fontFamilyName.hashCode();
|
||||
result = 31 * result + fontStyle;
|
||||
result = 31 * result + fontSize;
|
||||
result = 31 * result + startOffset;
|
||||
result = 31 * result + endOffset;
|
||||
return result;
|
||||
}
|
||||
|
||||
@SuppressWarnings("RedundantIfStatement")
|
||||
@Override
|
||||
public boolean equals(Object o) {
|
||||
if (this == o) return true;
|
||||
if (o == null || getClass() != o.getClass()) return false;
|
||||
|
||||
SegmentInfo info = (SegmentInfo)o;
|
||||
|
||||
if (endOffset != info.endOffset) return false;
|
||||
if (fontStyle != info.fontStyle) return false;
|
||||
if (fontSize != info.fontSize) return false;
|
||||
if (startOffset != info.startOffset) return false;
|
||||
if (background != null ? !background.equals(info.background) : info.background != null) return false;
|
||||
if (!fontFamilyName.equals(info.fontFamilyName)) return false;
|
||||
if (foreground != null ? !foreground.equals(info.foreground) : info.foreground != null) return false;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
StringBuilder fontStyleAsString = new StringBuilder();
|
||||
if (fontStyle == Font.PLAIN) {
|
||||
fontStyleAsString.append("plain");
|
||||
}
|
||||
else {
|
||||
if ((fontStyle & Font.BOLD) != 0) {
|
||||
fontStyleAsString.append("bold ");
|
||||
}
|
||||
if ((fontStyle & Font.ITALIC) != 0) {
|
||||
fontStyleAsString.append("italic ");
|
||||
}
|
||||
if (fontStyleAsString.length() > 0) {
|
||||
fontStyleAsString.setLength(fontStyleAsString.length() - 1);
|
||||
}
|
||||
else {
|
||||
fontStyleAsString.append("unknown font style");
|
||||
}
|
||||
}
|
||||
return String.format("%d-%d: %s, %s", startOffset, endOffset, fontFamilyName, fontStyleAsString);
|
||||
}
|
||||
}
|
||||
}
|
||||
+47
@@ -0,0 +1,47 @@
|
||||
/*
|
||||
* Copyright 2000-2014 JetBrains s.r.o.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* 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.openapi.editor.richcopy.model;
|
||||
|
||||
/**
|
||||
* @author Denis Zhdanov
|
||||
* @since 3/23/13 3:11 PM
|
||||
*/
|
||||
public abstract class AbstractFlyweightInfo implements OutputInfo {
|
||||
|
||||
private final int myId;
|
||||
|
||||
public AbstractFlyweightInfo(int id) {
|
||||
myId = id;
|
||||
}
|
||||
|
||||
public int getId() {
|
||||
return myId;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
return myId;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(Object o) {
|
||||
if (this == o) return true;
|
||||
if (o == null || getClass() != o.getClass()) return false;
|
||||
|
||||
AbstractFlyweightInfo info = (AbstractFlyweightInfo)o;
|
||||
return myId == info.myId;
|
||||
}
|
||||
}
|
||||
+73
@@ -0,0 +1,73 @@
|
||||
/*
|
||||
* Copyright 2000-2014 JetBrains s.r.o.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* 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.openapi.editor.richcopy.model;
|
||||
|
||||
import gnu.trove.TIntObjectHashMap;
|
||||
import gnu.trove.TObjectIntHashMap;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
|
||||
import java.io.Serializable;
|
||||
import java.util.Arrays;
|
||||
|
||||
/**
|
||||
* @author Denis Zhdanov
|
||||
* @since 3/23/13 3:17 PM
|
||||
*/
|
||||
public abstract class AbstractRegistry<T> implements Serializable {
|
||||
|
||||
@NotNull private final TIntObjectHashMap<T> myDataById = new TIntObjectHashMap<T>();
|
||||
|
||||
private transient TObjectIntHashMap<T> myIdsByData = new TObjectIntHashMap<T>();
|
||||
|
||||
@NotNull
|
||||
public T dataById(int id) throws IllegalArgumentException {
|
||||
T result = myDataById.get(id);
|
||||
if (result == null) {
|
||||
throw new IllegalArgumentException("No data is registered for id " + id);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
public int getId(@NotNull T data) throws IllegalStateException {
|
||||
if (myIdsByData == null) {
|
||||
throw new IllegalStateException(String.format(
|
||||
"Can't register data '%s'. Reason: the %s registry is already sealed", data, getClass().getName()
|
||||
));
|
||||
}
|
||||
int id = myIdsByData.get(data);
|
||||
if (id <= 0) {
|
||||
id = myIdsByData.size() + 1;
|
||||
myDataById.put(id, data);
|
||||
myIdsByData.put(data, id);
|
||||
}
|
||||
return id;
|
||||
}
|
||||
|
||||
public int[] getAllIds() {
|
||||
int[] result = myDataById.keys();
|
||||
Arrays.sort(result);
|
||||
return result;
|
||||
}
|
||||
|
||||
public int size() {
|
||||
return myDataById.size();
|
||||
}
|
||||
|
||||
public void seal() {
|
||||
myIdsByData = null;
|
||||
myDataById.compact();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,39 @@
|
||||
/*
|
||||
* Copyright 2000-2014 JetBrains s.r.o.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* 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.openapi.editor.richcopy.model;
|
||||
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
|
||||
/**
|
||||
* @author Denis Zhdanov
|
||||
* @since 3/23/13 3:23 PM
|
||||
*/
|
||||
public class Background extends AbstractFlyweightInfo {
|
||||
|
||||
public Background(int id) {
|
||||
super(id);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void invite(@NotNull OutputInfoVisitor visitor) {
|
||||
visitor.visit(this);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return "background=" + getId();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,25 @@
|
||||
/*
|
||||
* Copyright 2000-2014 JetBrains s.r.o.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* 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.openapi.editor.richcopy.model;
|
||||
|
||||
import java.awt.*;
|
||||
|
||||
/**
|
||||
* @author Denis Zhdanov
|
||||
* @since 3/23/13 3:13 PM
|
||||
*/
|
||||
public class ColorRegistry extends AbstractRegistry<Color> {
|
||||
}
|
||||
@@ -0,0 +1,39 @@
|
||||
/*
|
||||
* Copyright 2000-2014 JetBrains s.r.o.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* 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.openapi.editor.richcopy.model;
|
||||
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
|
||||
/**
|
||||
* @author Denis Zhdanov
|
||||
* @since 3/25/13 6:48 PM
|
||||
*/
|
||||
public class FontFamilyName extends AbstractFlyweightInfo {
|
||||
|
||||
public FontFamilyName(int id) {
|
||||
super(id);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void invite(@NotNull OutputInfoVisitor visitor) {
|
||||
visitor.visit(this);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return "font family=" + getId();
|
||||
}
|
||||
}
|
||||
+23
@@ -0,0 +1,23 @@
|
||||
/*
|
||||
* Copyright 2000-2014 JetBrains s.r.o.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* 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.openapi.editor.richcopy.model;
|
||||
|
||||
/**
|
||||
* @author Denis Zhdanov
|
||||
* @since 3/23/13 3:21 PM
|
||||
*/
|
||||
public class FontNameRegistry extends AbstractRegistry<String> {
|
||||
}
|
||||
@@ -0,0 +1,60 @@
|
||||
/*
|
||||
* Copyright 2000-2014 JetBrains s.r.o.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* 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.openapi.editor.richcopy.model;
|
||||
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
|
||||
/**
|
||||
* @author Denis Zhdanov
|
||||
* @since 3/25/13 11:23 PM
|
||||
*/
|
||||
public class FontSize implements OutputInfo {
|
||||
|
||||
private final int mySize;
|
||||
|
||||
public FontSize(int size) {
|
||||
mySize = size;
|
||||
}
|
||||
|
||||
public int getSize() {
|
||||
return mySize;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void invite(@NotNull OutputInfoVisitor visitor) {
|
||||
visitor.visit(this);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
return mySize;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(Object o) {
|
||||
if (this == o) return true;
|
||||
if (o == null || getClass() != o.getClass()) return false;
|
||||
|
||||
FontSize size = (FontSize)o;
|
||||
|
||||
return mySize == size.mySize;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return "font size=" + mySize;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,60 @@
|
||||
/*
|
||||
* Copyright 2000-2014 JetBrains s.r.o.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* 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.openapi.editor.richcopy.model;
|
||||
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
|
||||
/**
|
||||
* @author Denis Zhdanov
|
||||
* @since 3/25/13 7:10 PM
|
||||
*/
|
||||
public class FontStyle implements OutputInfo {
|
||||
|
||||
private final int myStyle;
|
||||
|
||||
public FontStyle(int style) {
|
||||
myStyle = style;
|
||||
}
|
||||
|
||||
public int getStyle() {
|
||||
return myStyle;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void invite(@NotNull OutputInfoVisitor visitor) {
|
||||
visitor.visit(this);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
return myStyle;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(Object o) {
|
||||
if (this == o) return true;
|
||||
if (o == null || getClass() != o.getClass()) return false;
|
||||
|
||||
FontStyle style = (FontStyle)o;
|
||||
|
||||
return myStyle == style.myStyle;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return "font style=" + myStyle;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,39 @@
|
||||
/*
|
||||
* Copyright 2000-2014 JetBrains s.r.o.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* 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.openapi.editor.richcopy.model;
|
||||
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
|
||||
/**
|
||||
* @author Denis Zhdanov
|
||||
* @since 3/23/13 3:23 PM
|
||||
*/
|
||||
public class Foreground extends AbstractFlyweightInfo {
|
||||
|
||||
public Foreground(int id) {
|
||||
super(id);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void invite(@NotNull OutputInfoVisitor visitor) {
|
||||
visitor.visit(this);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return "foreground=" + getId();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,29 @@
|
||||
/*
|
||||
* Copyright 2000-2014 JetBrains s.r.o.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* 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.openapi.editor.richcopy.model;
|
||||
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
|
||||
import java.io.Serializable;
|
||||
|
||||
/**
|
||||
* @author Denis Zhdanov
|
||||
* @since 3/23/13 3:06 PM
|
||||
*/
|
||||
public interface OutputInfo extends Serializable {
|
||||
|
||||
void invite(@NotNull OutputInfoVisitor visitor);
|
||||
}
|
||||
+32
@@ -0,0 +1,32 @@
|
||||
/*
|
||||
* Copyright 2000-2014 JetBrains s.r.o.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* 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.openapi.editor.richcopy.model;
|
||||
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
|
||||
/**
|
||||
* @author Denis Zhdanov
|
||||
* @since 3/23/13 3:07 PM
|
||||
*/
|
||||
public interface OutputInfoVisitor {
|
||||
|
||||
void visit(@NotNull Text text);
|
||||
void visit(@NotNull Foreground color);
|
||||
void visit(@NotNull Background color);
|
||||
void visit(@NotNull FontFamilyName name);
|
||||
void visit(@NotNull FontStyle style);
|
||||
void visit(@NotNull FontSize size);
|
||||
}
|
||||
@@ -0,0 +1,90 @@
|
||||
/*
|
||||
* Copyright 2000-2014 JetBrains s.r.o.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* 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.openapi.editor.richcopy.model;
|
||||
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
|
||||
import java.io.Serializable;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* @author Denis Zhdanov
|
||||
* @since 3/25/13 1:19 PM
|
||||
*/
|
||||
public class SyntaxInfo implements Serializable {
|
||||
|
||||
private static final long serialVersionUID = 1L;
|
||||
|
||||
@NotNull private final List<OutputInfo> myOutputInfos;
|
||||
@NotNull private final ColorRegistry myColorRegistry;
|
||||
@NotNull private final FontNameRegistry myFontNameRegistry;
|
||||
|
||||
private final int myDefaultForeground;
|
||||
private final int myDefaultBackground;
|
||||
private final int mySingleFontSize;
|
||||
|
||||
public SyntaxInfo(@NotNull List<OutputInfo> infos,
|
||||
int defaultForeground,
|
||||
int defaultBackground,
|
||||
int singleFontSize,
|
||||
@NotNull FontNameRegistry fontNameRegistry,
|
||||
@NotNull ColorRegistry colorRegistry)
|
||||
{
|
||||
myOutputInfos = infos;
|
||||
myDefaultForeground = defaultForeground;
|
||||
myDefaultBackground = defaultBackground;
|
||||
mySingleFontSize = singleFontSize;
|
||||
myFontNameRegistry = fontNameRegistry;
|
||||
myColorRegistry = colorRegistry;
|
||||
}
|
||||
|
||||
@NotNull
|
||||
public List<OutputInfo> getOutputInfos() {
|
||||
return myOutputInfos;
|
||||
}
|
||||
|
||||
@NotNull
|
||||
public ColorRegistry getColorRegistry() {
|
||||
return myColorRegistry;
|
||||
}
|
||||
|
||||
@NotNull
|
||||
public FontNameRegistry getFontNameRegistry() {
|
||||
return myFontNameRegistry;
|
||||
}
|
||||
|
||||
public int getDefaultForeground() {
|
||||
return myDefaultForeground;
|
||||
}
|
||||
|
||||
public int getDefaultBackground() {
|
||||
return myDefaultBackground;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return positive value if all tokens have the same font size (returned value);
|
||||
* non-positive value otherwise
|
||||
*/
|
||||
public int getSingleFontSize() {
|
||||
return mySingleFontSize;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return String.format("default colors: foreground=%d, background=%d; output infos: %s",
|
||||
myDefaultForeground, myDefaultBackground, myOutputInfos);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,76 @@
|
||||
/*
|
||||
* Copyright 2000-2014 JetBrains s.r.o.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* 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.openapi.editor.richcopy.model;
|
||||
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
|
||||
/**
|
||||
* @author Denis Zhdanov
|
||||
* @since 3/23/13 3:08 PM
|
||||
*/
|
||||
public class Text implements OutputInfo {
|
||||
|
||||
private final int myStartOffset;
|
||||
private final int myEndOffset;
|
||||
|
||||
public Text(int startOffset, int endOffset) {
|
||||
myStartOffset = startOffset;
|
||||
myEndOffset = endOffset;
|
||||
}
|
||||
|
||||
public Text(char c) {
|
||||
myStartOffset = -1;
|
||||
myEndOffset = c;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void invite(@NotNull OutputInfoVisitor visitor) {
|
||||
visitor.visit(this);
|
||||
}
|
||||
|
||||
public int getStartOffset() {
|
||||
return myStartOffset;
|
||||
}
|
||||
|
||||
public int getEndOffset() {
|
||||
return myStartOffset == -1 ? 0 : myEndOffset;
|
||||
}
|
||||
|
||||
public char getCharAt(CharSequence charSequence, int offset) {
|
||||
return myStartOffset == -1 ? (char)myEndOffset : charSequence.charAt(offset);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
int result = myStartOffset;
|
||||
result = 31 * result + myEndOffset;
|
||||
return result;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(Object o) {
|
||||
if (this == o) return true;
|
||||
if (o == null || getClass() != o.getClass()) return false;
|
||||
|
||||
Text text = (Text)o;
|
||||
return myEndOffset == text.myEndOffset && myStartOffset == text.myStartOffset;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return myStartOffset == -1 ? String.format("text=%c", myEndOffset) : String.format("text=%d-%d", myStartOffset, myEndOffset);
|
||||
}
|
||||
}
|
||||
+96
@@ -0,0 +1,96 @@
|
||||
/*
|
||||
* Copyright 2000-2014 JetBrains s.r.o.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* 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.openapi.editor.richcopy.settings;
|
||||
|
||||
import com.intellij.openapi.application.ApplicationManager;
|
||||
import com.intellij.openapi.components.*;
|
||||
import com.intellij.openapi.editor.colors.EditorColorsManager;
|
||||
import com.intellij.openapi.editor.colors.EditorColorsScheme;
|
||||
import com.intellij.util.xmlb.XmlSerializerUtil;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
/**
|
||||
* @author Denis Zhdanov
|
||||
* @since 3/26/13 6:40 PM
|
||||
*/
|
||||
@State(
|
||||
name = "EditorRichCopySettings",
|
||||
storages = {@Storage(file = StoragePathMacros.APP_CONFIG + "/editor.rich.copy.xml")}
|
||||
)
|
||||
public class RichCopySettings implements PersistentStateComponent<RichCopySettings>, ApplicationComponent {
|
||||
|
||||
@NotNull public static final String ACTIVE_GLOBAL_SCHEME_MARKER = "__ACTIVE_GLOBAL_SCHEME__";
|
||||
|
||||
private String mySchemeName = ACTIVE_GLOBAL_SCHEME_MARKER;
|
||||
private boolean myStripIndents = true;
|
||||
|
||||
@NotNull
|
||||
public static RichCopySettings getInstance() {
|
||||
return ApplicationManager.getApplication().getComponent(RichCopySettings.class);
|
||||
}
|
||||
|
||||
@NotNull
|
||||
public EditorColorsScheme getColorsScheme(@NotNull EditorColorsScheme editorColorsScheme) {
|
||||
EditorColorsScheme result = null;
|
||||
if (!ACTIVE_GLOBAL_SCHEME_MARKER.equals(mySchemeName)) {
|
||||
result = EditorColorsManager.getInstance().getScheme(mySchemeName);
|
||||
}
|
||||
return result == null ? editorColorsScheme : result;
|
||||
}
|
||||
|
||||
@Nullable
|
||||
@Override
|
||||
public RichCopySettings getState() {
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void loadState(RichCopySettings state) {
|
||||
XmlSerializerUtil.copyBean(state, this);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void initComponent() {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void disposeComponent() {
|
||||
}
|
||||
|
||||
@NotNull
|
||||
@Override
|
||||
public String getComponentName() {
|
||||
return getClass().getName();
|
||||
}
|
||||
|
||||
@NotNull
|
||||
public String getSchemeName() {
|
||||
return mySchemeName == null ? ACTIVE_GLOBAL_SCHEME_MARKER : mySchemeName;
|
||||
}
|
||||
|
||||
public void setSchemeName(@Nullable String schemeName) {
|
||||
mySchemeName = schemeName;
|
||||
}
|
||||
|
||||
public boolean isStripIndents() {
|
||||
return myStripIndents;
|
||||
}
|
||||
|
||||
public void setStripIndents(boolean stripIndents) {
|
||||
myStripIndents = stripIndents;
|
||||
}
|
||||
}
|
||||
+83
@@ -0,0 +1,83 @@
|
||||
/*
|
||||
* Copyright 2000-2014 JetBrains s.r.o.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* 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.openapi.editor.richcopy.view;
|
||||
|
||||
import com.intellij.codeInsight.editorActions.TextBlockTransferableData;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
import java.awt.datatransfer.DataFlavor;
|
||||
import java.io.ByteArrayInputStream;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
|
||||
/**
|
||||
* @author Denis Zhdanov
|
||||
* @since 3/28/13 1:20 PM
|
||||
*/
|
||||
public class InputStreamTransferableData extends InputStream implements TextBlockTransferableData
|
||||
{
|
||||
@NotNull
|
||||
private final DataFlavor myDataFlavor;
|
||||
@Nullable
|
||||
private InputStream myDelegate;
|
||||
|
||||
public InputStreamTransferableData(@NotNull byte[] data, @NotNull DataFlavor flavor) {
|
||||
myDataFlavor = flavor;
|
||||
myDelegate = new ByteArrayInputStream(data);
|
||||
}
|
||||
|
||||
@Override
|
||||
public DataFlavor getFlavor() {
|
||||
return myDelegate == null ? null : myDataFlavor;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getOffsetCount() {
|
||||
return 0;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getOffsets(int[] offsets, int index) {
|
||||
return index;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int setOffsets(int[] offsets, int index) {
|
||||
return index;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int read() throws IOException {
|
||||
if (myDelegate == null) {
|
||||
return -1;
|
||||
}
|
||||
return myDelegate.read();
|
||||
}
|
||||
|
||||
@Override
|
||||
public int read(@NotNull byte[] b, int off, int len) throws IOException {
|
||||
if (myDelegate == null) {
|
||||
return -1;
|
||||
}
|
||||
return myDelegate.read(b, off, len);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void close() throws IOException {
|
||||
myDelegate = null;
|
||||
}
|
||||
}
|
||||
+84
@@ -0,0 +1,84 @@
|
||||
/*
|
||||
* Copyright 2000-2014 JetBrains s.r.o.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* 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.openapi.editor.richcopy.view;
|
||||
|
||||
import com.intellij.codeInsight.editorActions.TextBlockTransferableData;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
import java.awt.datatransfer.DataFlavor;
|
||||
import java.io.IOException;
|
||||
import java.io.Reader;
|
||||
import java.io.StringReader;
|
||||
|
||||
/**
|
||||
* @author Denis Zhdanov
|
||||
* @since 3/28/13 7:09 PM
|
||||
*/
|
||||
public class ReaderTransferableData extends Reader implements TextBlockTransferableData
|
||||
{
|
||||
@NotNull
|
||||
private final DataFlavor myDataFlavor;
|
||||
@Nullable
|
||||
private Reader myDelegate;
|
||||
|
||||
@SuppressWarnings("AbstractMethodCallInConstructor")
|
||||
public ReaderTransferableData(@NotNull String data, @NotNull DataFlavor dataFlavor) {
|
||||
myDataFlavor = dataFlavor;
|
||||
myDelegate = new StringReader(data);
|
||||
}
|
||||
|
||||
@Override
|
||||
public DataFlavor getFlavor() {
|
||||
return myDelegate == null ? null : myDataFlavor;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getOffsetCount() {
|
||||
return 0;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getOffsets(int[] offsets, int index) {
|
||||
return index;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int setOffsets(int[] offsets, int index) {
|
||||
return index;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int read() throws IOException {
|
||||
if (myDelegate == null) {
|
||||
return -1;
|
||||
}
|
||||
return myDelegate.read();
|
||||
}
|
||||
|
||||
@Override
|
||||
public int read(@NotNull char[] cbuf, int off, int len) throws IOException {
|
||||
if (myDelegate == null) {
|
||||
return -1;
|
||||
}
|
||||
return myDelegate.read(cbuf, off, len);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void close() throws IOException {
|
||||
myDelegate = null;
|
||||
}
|
||||
}
|
||||
@@ -631,4 +631,9 @@ settings.code.style.general.formatter.marker.title=Markers
|
||||
settings.code.style.general.formatter.marker.options.title=Options
|
||||
import.scheme.chooser.source=From\:
|
||||
import.scheme.chooser.destination=To\:
|
||||
checkbox.reformat.on.typing.rbrace=Reformat block on typing '}'
|
||||
checkbox.reformat.on.typing.rbrace=Reformat block on typing '}'
|
||||
|
||||
group.richcopy=Rich-text copy
|
||||
checkbox.richcopy.strip.whitespace.at.line.start=Strip common indent
|
||||
combobox.richcopy.color.scheme=Color scheme
|
||||
combobox.richcopy.color.scheme.active=Active scheme
|
||||
|
||||
@@ -378,3 +378,7 @@ editor.injected.highlighting.enabled.description=Disables injected fragments hig
|
||||
run.processes.with.pty=false
|
||||
|
||||
ide.certificate.manager=true
|
||||
|
||||
editor.richcopy.enable=true
|
||||
editor.richcopy.debug=false
|
||||
editor.richcopy.max.size.megabytes=10
|
||||
|
||||
@@ -880,6 +880,10 @@
|
||||
<lang.foldingBuilder language="TEXT" implementationClass="com.intellij.ide.highlighter.custom.impl.CustomFileTypeFoldingBuilder"/>
|
||||
|
||||
<virtualFileSystem key="scratchpad" implementationClass="com.intellij.ide.scratch.ScratchpadFileSystem"/>
|
||||
|
||||
<copyPastePostProcessor implementation="com.intellij.openapi.editor.richcopy.TextWithMarkupProcessor"/>
|
||||
<copyPastePostProcessor implementation="com.intellij.openapi.editor.richcopy.RtfCopyPasteProcessor"/>
|
||||
<copyPastePostProcessor implementation="com.intellij.openapi.editor.richcopy.HtmlCopyPasteProcessor"/>
|
||||
</extensions>
|
||||
</idea-plugin>
|
||||
|
||||
|
||||
@@ -37,6 +37,10 @@
|
||||
<component>
|
||||
<implementation-class>com.intellij.codeInsight.preview.ImageOrColorPreviewManager</implementation-class>
|
||||
</component>
|
||||
|
||||
<component>
|
||||
<implementation-class>com.intellij.openapi.editor.richcopy.settings.RichCopySettings</implementation-class>
|
||||
</component>
|
||||
</application-components>
|
||||
|
||||
<project-components>
|
||||
|
||||
@@ -0,0 +1,3 @@
|
||||
<div style="border:1px inset;padding:2%;"><pre style="margin:0;padding:6px;background-color:#ffffff;font-family:'Monospaced';font-size:12;" bgcolor="#ffffff"> <font color="#000080"><b><span style="color:#000080;font-weight:bold;">int </span></b></font><font color="#000000"><span style="color:#000000;">field;</span></font><font color="#000000"><span style="color:#000000;">
|
||||
</span></font><font color="#000000"><span style="color:#000000;">
|
||||
</span></font><font color="#000000"><span style="color:#000000;"> </span></font><font color="#000080"><b><span style="color:#000080;font-weight:bold;">public static void </span></b></font><font color="#000000"><span style="color:#000000;">main(String[] args) {</span></font></pre></div>
|
||||
@@ -0,0 +1,7 @@
|
||||
public class Basic {
|
||||
<selection> int field;
|
||||
|
||||
public static void main(String[] args) {</selection>
|
||||
System.out.println("Hello\tworld!");
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,18 @@
|
||||
{\rtf1\ansi\deff0{\colortbl;\red0\green0\blue128;\red0\green0\blue0;\red255\green255\blue255;}
|
||||
{\fonttbl{\f1 Monospaced;}}
|
||||
|
||||
\s0\box\brdrhair\brdrcf2\brsp317\cbpat3\cb3
|
||||
\cf1\f1\plain
|
||||
\cf1\f1\b\fs24
|
||||
int \cf2\plain
|
||||
\cf2\f1\fs24
|
||||
field;
|
||||
\line
|
||||
|
||||
\line
|
||||
|
||||
\cf1\plain
|
||||
\cf1\f1\fs24\b
|
||||
public static void \cf2\plain
|
||||
\cf2\f1\fs24
|
||||
main(String[] args) \{\par}
|
||||
@@ -0,0 +1,8 @@
|
||||
<div style="border:1px inset;padding:2%;"><pre style="margin:0;padding:6px;background-color:#ffffff;font-family:'Monospaced';font-size:12;" bgcolor="#ffffff"><font color="#000080"><b><span style="color:#000080;font-weight:bold;">public class </span></b></font><font color="#000000"><span style="color:#000000;">Basic {
|
||||
</span></font><font color="#000000"><span style="color:#000000;"> </span></font><font color="#000080"><b><span style="color:#000080;font-weight:bold;">int </span></b></font><font color="#000000"><span style="color:#000000;">field;
|
||||
</span></font><font color="#000000"><span style="color:#000000;">
|
||||
</span></font><font color="#000000"><span style="color:#000000;"> </span></font><font color="#000080"><b><span style="color:#000080;font-weight:bold;">public static void </span></b></font><font color="#000000"><span style="color:#000000;">main(String[] args) {
|
||||
</span></font><font color="#000000"><span style="color:#000000;"> System.out.println(</span></font><font color="#008000"><b><span style="color:#008000;font-weight:bold;">"Hello</span></b></font><font color="#000080"><b><span style="color:#000080;font-weight:bold;">\t</span></b></font><font color="#008000"><b><span style="color:#008000;font-weight:bold;">world!"</span></b></font><font color="#000000"><span style="color:#000000;">);
|
||||
</span></font><font color="#000000"><span style="color:#000000;"> }
|
||||
</span></font><font color="#000000"><span style="color:#000000;">}
|
||||
</span></font></pre></div>
|
||||
@@ -0,0 +1,8 @@
|
||||
<selection>public class Basic {
|
||||
int field;
|
||||
|
||||
public static void main(String[] args) {
|
||||
System.out.println("Hello\tworld!");
|
||||
}
|
||||
}
|
||||
</selection>
|
||||
@@ -0,0 +1,35 @@
|
||||
{\rtf1\ansi\deff0{\colortbl;\red0\green0\blue128;\red0\green0\blue0;\red0\green128\blue0;\red255\green255\blue255;}
|
||||
{\fonttbl{\f1 Monospaced;}}
|
||||
|
||||
\s0\box\brdrhair\brdrcf2\brsp317\cbpat4\cb4\cf1\f1\plain
|
||||
\cf1\f1\b\fs24
|
||||
public class \cf2\plain
|
||||
\cf2\f1\fs24
|
||||
Basic \{\line
|
||||
|
||||
\cf1\plain
|
||||
\cf1\f1\fs24\b
|
||||
int \cf2\plain
|
||||
\cf2\f1\fs24
|
||||
field;\line
|
||||
|
||||
\line
|
||||
|
||||
\cf1\plain
|
||||
\cf1\f1\fs24\b
|
||||
public static void \cf2\plain
|
||||
\cf2\f1\fs24
|
||||
main(String[] args) \{\line
|
||||
|
||||
System.out.println(\cf3\plain
|
||||
\cf3\f1\fs24\b
|
||||
"Hello\cf1
|
||||
\\t\cf3
|
||||
world!"\cf2\plain
|
||||
\cf2\f1\fs24
|
||||
);\line
|
||||
|
||||
\}\line
|
||||
|
||||
\}\line
|
||||
\par}
|
||||
+76
@@ -0,0 +1,76 @@
|
||||
/*
|
||||
* Copyright 2000-2014 JetBrains s.r.o.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* 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.openapi.editor.richcopy;
|
||||
|
||||
import com.intellij.openapi.application.ex.PathManagerEx;
|
||||
import com.intellij.openapi.ide.CopyPasteManager;
|
||||
import com.intellij.openapi.util.io.FileUtilRt;
|
||||
import com.intellij.testFramework.fixtures.LightPlatformCodeInsightFixtureTestCase;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
|
||||
import java.awt.datatransfer.Transferable;
|
||||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.io.Reader;
|
||||
|
||||
public class RichCopyTest extends LightPlatformCodeInsightFixtureTestCase {
|
||||
public void testNormalSelection() throws Exception {
|
||||
doTest(false);
|
||||
}
|
||||
|
||||
public void testBlockSelection() throws Exception {
|
||||
doTest(true);
|
||||
}
|
||||
|
||||
private void doTest(boolean columnMode) throws Exception {
|
||||
myFixture.configureByFile(getTestName(false) + ".java");
|
||||
if (columnMode) {
|
||||
myFixture.performEditorAction("EditorToggleColumnMode");
|
||||
}
|
||||
myFixture.performEditorAction("EditorCopy");
|
||||
Transferable contents = CopyPasteManager.getInstance().getContents();
|
||||
assertNotNull(contents);
|
||||
|
||||
assertTrue(contents.isDataFlavorSupported(HtmlCopyPasteProcessor.FLAVOR));
|
||||
String expectedHtml = getFileContents(getTestName(false) + ".html");
|
||||
String actualHtml = readFully((Reader)contents.getTransferData(HtmlCopyPasteProcessor.FLAVOR));
|
||||
assertEquals("HTML contents differs", expectedHtml, actualHtml);
|
||||
|
||||
assertTrue(contents.isDataFlavorSupported(RtfCopyPasteProcessor.FLAVOR));
|
||||
String expectedRtf = getFileContents(getTestName(false) + ".rtf");
|
||||
String actualRtf = readFully((InputStream)contents.getTransferData(RtfCopyPasteProcessor.FLAVOR));
|
||||
assertEquals("RTF contents differs", expectedRtf, actualRtf);
|
||||
}
|
||||
|
||||
private static String readFully(InputStream inputStream) throws IOException {
|
||||
return new String(FileUtilRt.loadBytes(inputStream));
|
||||
}
|
||||
|
||||
private static String readFully(Reader reader) throws IOException {
|
||||
return new String(FileUtilRt.loadText(reader, 10000));
|
||||
}
|
||||
|
||||
private String getFileContents(String fileName) throws IOException {
|
||||
return FileUtilRt.loadFile(new File(getTestDataPath() + fileName), true);
|
||||
}
|
||||
|
||||
@NotNull
|
||||
@Override
|
||||
protected String getTestDataPath() {
|
||||
return PathManagerEx.getHomePath(getClass()) + "/platform/platform-tests/testData/editor/richcopy/";
|
||||
}
|
||||
}
|
||||
+276
@@ -0,0 +1,276 @@
|
||||
/*
|
||||
* Copyright 2000-2014 JetBrains s.r.o.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* 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.openapi.editor.richcopy;
|
||||
|
||||
import com.intellij.openapi.editor.Editor;
|
||||
import com.intellij.openapi.editor.LogicalPosition;
|
||||
import com.intellij.openapi.editor.SelectionModel;
|
||||
import com.intellij.openapi.editor.ex.EditorEx;
|
||||
import com.intellij.openapi.editor.richcopy.model.*;
|
||||
import com.intellij.openapi.util.Ref;
|
||||
import com.intellij.testFramework.fixtures.LightPlatformCodeInsightFixtureTestCase;
|
||||
import junit.framework.TestCase;
|
||||
|
||||
import java.awt.*;
|
||||
import java.util.Arrays;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* @author Denis Zhdanov
|
||||
* @since 3/27/13 11:11 AM
|
||||
*/
|
||||
public class SyntaxInfoConstructionTest extends LightPlatformCodeInsightFixtureTestCase {
|
||||
public void testBlockSelection() {
|
||||
String text =
|
||||
"package org;\n" +
|
||||
"\n" +
|
||||
"public class TestClass {\n" +
|
||||
"\n" +
|
||||
" int field;\n" +
|
||||
"\n" +
|
||||
" public int getField() {\n" +
|
||||
" return field;\n" +
|
||||
" }\n" +
|
||||
"}";
|
||||
init(text);
|
||||
|
||||
int blockSelectionStartOffset = text.indexOf("public int");
|
||||
Editor editor = myFixture.getEditor();
|
||||
LogicalPosition blockSelectionStartPosition = editor.offsetToLogicalPosition(blockSelectionStartOffset);
|
||||
LogicalPosition blockSelectionEndPosition = new LogicalPosition(
|
||||
blockSelectionStartPosition.line + 2,
|
||||
editor.offsetToLogicalPosition(text.indexOf('{', blockSelectionStartOffset)).column + 1);
|
||||
editor.getSelectionModel().setBlockSelection(blockSelectionStartPosition, blockSelectionEndPosition);
|
||||
|
||||
verifySyntaxInfo(
|
||||
new Foreground(1), new FontFamilyName(1), new FontStyle(Font.BOLD), new FontSize(12), new Text(60, 71), // 'public int '
|
||||
new Foreground(2), new FontStyle(Font.PLAIN), new Text(71, 83), // 'getField() {'
|
||||
new Text('\n'), // '\n'
|
||||
new Text(88, 92), // ' ' - indent before 'return field;'
|
||||
new Foreground(1), new FontStyle(Font.BOLD), new Text(92, 99), // 'return '
|
||||
new Foreground(3), new Text(99, 104), // 'field';
|
||||
new Foreground(2), new FontStyle(Font.PLAIN), new Text(104, 105), // ';'
|
||||
new Text('\n'), // '\n'
|
||||
new Text(110, 111) // '}'
|
||||
);
|
||||
}
|
||||
|
||||
public void testColumnModeBlockSelection() {
|
||||
String text =
|
||||
"package org;\n" +
|
||||
"\n" +
|
||||
"public class TestClass {\n" +
|
||||
"\n" +
|
||||
" int field;\n" +
|
||||
"\n" +
|
||||
" public int getField() {\n" +
|
||||
" return field;\n" +
|
||||
" }\n" +
|
||||
"}";
|
||||
init(text);
|
||||
|
||||
int blockSelectionStartOffset = text.indexOf("public int");
|
||||
Editor editor = myFixture.getEditor();
|
||||
((EditorEx) editor).setColumnMode(true);
|
||||
LogicalPosition blockSelectionStartPosition = editor.offsetToLogicalPosition(blockSelectionStartOffset);
|
||||
LogicalPosition blockSelectionEndPosition = new LogicalPosition(
|
||||
blockSelectionStartPosition.line + 2,
|
||||
editor.offsetToLogicalPosition(text.indexOf('{', blockSelectionStartOffset)).column + 1);
|
||||
editor.getSelectionModel().setBlockSelection(blockSelectionStartPosition, blockSelectionEndPosition);
|
||||
|
||||
verifySyntaxInfo(
|
||||
new Foreground(1), new FontFamilyName(1), new FontStyle(Font.BOLD), new FontSize(12), new Text(60, 71), // 'public int '
|
||||
new Foreground(2), new FontStyle(Font.PLAIN), new Text(71, 83), // 'getField() {'
|
||||
new Text('\n'), // '\n'
|
||||
new Text(88, 92), // ' ' - indent before 'return field;'
|
||||
new Foreground(1), new FontStyle(Font.BOLD), new Text(92, 99), // 'return '
|
||||
new Foreground(3), new Text(99, 104), // 'field';
|
||||
new Foreground(2), new FontStyle(Font.PLAIN), new Text(104, 105), // ';'
|
||||
new Text('\n'), // '\n'
|
||||
new Text(110, 111) // '}'
|
||||
);
|
||||
}
|
||||
|
||||
public void testColumnModeBlockSelectionWithGaps() {
|
||||
String text =
|
||||
"public class TestClass {\n" +
|
||||
"\n" +
|
||||
" int field;\n" +
|
||||
"\n" +
|
||||
" int otherField;\n" +
|
||||
"}";
|
||||
init(text);
|
||||
|
||||
int blockSelectionStartOffset = text.indexOf("int");
|
||||
Editor editor = myFixture.getEditor();
|
||||
((EditorEx) editor).setColumnMode(true);
|
||||
LogicalPosition blockSelectionStartPosition = editor.offsetToLogicalPosition(blockSelectionStartOffset);
|
||||
LogicalPosition blockSelectionEndPosition = new LogicalPosition(blockSelectionStartPosition.line + 2, blockSelectionStartPosition.column + 16);
|
||||
editor.getSelectionModel().setBlockSelection(blockSelectionStartPosition, blockSelectionEndPosition);
|
||||
|
||||
verifySyntaxInfo(
|
||||
new Foreground(1), new FontFamilyName(1), new FontStyle(Font.BOLD), new FontSize(12), new Text(30, 34), // 'int '
|
||||
new Foreground(2), new Text(34, 39), // 'field'
|
||||
new Foreground(3), new FontStyle(Font.PLAIN), new Text(39, 40), // ';'
|
||||
new Text('\n'), // '\n'
|
||||
new Text('\n'), // '\n'
|
||||
new Foreground(1), new FontStyle(Font.BOLD), new Text(46, 50), // 'int '
|
||||
new Foreground(2), new Text(50, 60), // 'otherField';
|
||||
new Foreground(3), new FontStyle(Font.PLAIN), new Text(60, 61) // ';'
|
||||
);
|
||||
}
|
||||
|
||||
public void testRegularSelection() {
|
||||
// We want to exclude unnecessary indents from the pasted results.
|
||||
String text =
|
||||
"package org;\n" +
|
||||
"\n" +
|
||||
"public class TestClass {\n" +
|
||||
"\n" +
|
||||
" int field;\n" +
|
||||
"\n" +
|
||||
" public int getField() {\n" +
|
||||
" return field;\n" +
|
||||
" }\n" +
|
||||
"}";
|
||||
init(text);
|
||||
|
||||
int selectionStart = text.indexOf("public int");
|
||||
int selectionEnd = text.indexOf('}', selectionStart) + 1;
|
||||
SelectionModel selectionModel = myFixture.getEditor().getSelectionModel();
|
||||
selectionModel.setSelection(selectionStart, selectionEnd);
|
||||
|
||||
List<OutputInfo> expected = Arrays.asList(
|
||||
new Foreground(1), new FontFamilyName(1), new FontStyle(Font.BOLD), new FontSize(12), new Text(60, 71), // 'public int '
|
||||
new Foreground(2), new FontStyle(Font.PLAIN), new Text(71, 84), // 'getField() {\n'
|
||||
new Text(88, 92), // ' ' - indent before 'return field;'
|
||||
new Foreground(1), new FontStyle(Font.BOLD), new Text(92, 99), // 'return '
|
||||
new Foreground(3), new Text(99, 104), // 'field'
|
||||
new Foreground(2), new FontStyle(Font.PLAIN), new Text(104, 106), // ';\n'
|
||||
new Text(110, 111) // '}'
|
||||
);
|
||||
|
||||
TestCase.assertEquals(expected, getSyntaxInfo().getOutputInfos());
|
||||
|
||||
selectionModel.setSelection(selectionStart - 2, selectionEnd);
|
||||
TestCase.assertEquals(expected, getSyntaxInfo().getOutputInfos());
|
||||
|
||||
selectionModel.setSelection(selectionStart - 4, selectionEnd);
|
||||
TestCase.assertEquals(expected, getSyntaxInfo().getOutputInfos());
|
||||
}
|
||||
|
||||
public void testIncorrectFirstLineCalculationOffset() {
|
||||
init("\"tr\" #> <selection>template.statusList.sortBy</selection>(_.index).map(fromStatus =>");
|
||||
|
||||
verifySyntaxInfo(
|
||||
new FontFamilyName(1), new FontStyle(Font.PLAIN), new FontSize(12), new Text(8, 34)
|
||||
);
|
||||
}
|
||||
|
||||
public void testJavadoc() {
|
||||
init(
|
||||
"package org;\n" +
|
||||
"\n" +
|
||||
"import java.io.Serializable;\n" +
|
||||
"\n" +
|
||||
"<selection>/**\n" +
|
||||
" * Code in <code>here</code>\n" +
|
||||
" * <strong>Hi</strong> man\n" +
|
||||
" * @param <T>\n" +
|
||||
" </selection>*/\n" +
|
||||
"public interface SampleTest<T> extends Serializable {\n" +
|
||||
" boolean isNotNull();\n" +
|
||||
" T getValue();\n" +
|
||||
"}"
|
||||
);
|
||||
verifySyntaxInfo(
|
||||
new Foreground(1), new FontFamilyName(1), new FontStyle(Font.ITALIC), new FontSize(12), new Text(44, 48),
|
||||
new Text(48, 59),
|
||||
new Background(2), new Text(59, 65),
|
||||
new Background(3), new Text(65, 69),
|
||||
new Background(2), new Text(69, 76),
|
||||
new Background(3), new Text(76, 77),
|
||||
new Text(77, 80),
|
||||
new Background(2), new Text(80, 88),
|
||||
new Background(3), new Text(88, 90),
|
||||
new Background(2), new Text(90, 99),
|
||||
new Background(3), new Text(99, 104),
|
||||
new Text(104, 107),
|
||||
new FontStyle(Font.BOLD + Font.ITALIC), new Text(107, 114),
|
||||
new Foreground(4), new Text(114, 118),
|
||||
new Text(118, 119)
|
||||
);
|
||||
}
|
||||
|
||||
public void testIndentStrippingWhenFirstLineIsMostIndented() throws Exception {
|
||||
init("public class Test {\n" +
|
||||
"<selection> int field;\n" +
|
||||
"}</selection>");
|
||||
verifySyntaxInfo(
|
||||
new Text(20, 22), // ' '
|
||||
new Foreground(1), new FontFamilyName(1), new FontStyle(Font.BOLD), new FontSize(12), new Text(22, 26), // 'int '
|
||||
new Foreground(2), new Text(26, 31), // 'field'
|
||||
new Foreground(3), new FontStyle(Font.PLAIN), new Text(31, 33), // ';\n'
|
||||
new Text(33, 34) // '}'
|
||||
);
|
||||
}
|
||||
|
||||
public void testIndentStrippingWhenSelectionEndIsBeforeNonWsCharactersOnTheLine() throws Exception {
|
||||
init("public class Test {\n" +
|
||||
"<selection> int field;\n" +
|
||||
"</selection>}");
|
||||
verifySyntaxInfo(
|
||||
new Foreground(1), new FontFamilyName(1), new FontStyle(Font.BOLD), new FontSize(12), new Text(22, 26), // 'int '
|
||||
new Foreground(2), new Text(26, 31), // 'field'
|
||||
new Foreground(3), new FontStyle(Font.PLAIN), new Text(31, 33) // ';\n'
|
||||
);
|
||||
}
|
||||
|
||||
private SyntaxInfo getSyntaxInfo() {
|
||||
final Ref<SyntaxInfo> syntaxInfo = new Ref<SyntaxInfo>();
|
||||
Editor editor = myFixture.getEditor();
|
||||
|
||||
TextWithMarkupProcessor processor = new TextWithMarkupProcessor();
|
||||
processor.addBuilder(new TextWithMarkupBuilder() {
|
||||
@Override
|
||||
public void reset() {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void build(CharSequence charSequence, SyntaxInfo info) {
|
||||
syntaxInfo.set(info);
|
||||
}
|
||||
});
|
||||
SelectionModel selectionModel = editor.getSelectionModel();
|
||||
processor.collectTransferableData(myFixture.getFile(), editor, selectionModel.getBlockSelectionStarts(), selectionModel.getBlockSelectionEnds());
|
||||
|
||||
return syntaxInfo.get();
|
||||
}
|
||||
|
||||
private void init(String text) {
|
||||
myFixture.configureByText(getTestName(true) + ".java", text);
|
||||
myFixture.doHighlighting();
|
||||
}
|
||||
|
||||
private void verifySyntaxInfo(OutputInfo... infos) {
|
||||
assertEquals(Arrays.asList(infos), getSyntaxInfo().getOutputInfos());
|
||||
}
|
||||
|
||||
@Override
|
||||
protected boolean isWriteActionRequired() {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
@@ -4327,6 +4327,13 @@
|
||||
<option name="wraps" hit="Use soft wraps in editor" />
|
||||
<option name="space" hit="Virtual Space" />
|
||||
<option name="virtual" hit="Virtual Space" />
|
||||
<option name="rich" hit="Rich-text copy"/>
|
||||
<option name="copy" hit="Rich-text copy"/>
|
||||
<option name="color" hit="Color scheme"/>
|
||||
<option name="scheme" hit="Color scheme"/>
|
||||
<option name="strip" hit="Strip common indent"/>
|
||||
<option name="common" hit="Strip common indent"/>
|
||||
<option name="indent" hit="Strip common indent"/>
|
||||
</configurable>
|
||||
<configurable id="vcs.Subversion" configurable_name="Subversion">
|
||||
<option name="" path="General" hit="" />
|
||||
|
||||
Reference in New Issue
Block a user