mirror of
https://gitflic.ru/project/openide/openide.git
synced 2026-09-27 10:03:11 +07:00
Merge branch 'master' of git.labs.intellij.net:idea/community
This commit is contained in:
@@ -359,9 +359,8 @@ public class PostHighlightingPass extends TextEditorHighlightingPass {
|
||||
|
||||
@Nullable
|
||||
private HighlightInfo processField(final PsiField field, final PsiIdentifier identifier, ProgressIndicator progress) {
|
||||
if (isImplicitUsage(field, progress)) return null;
|
||||
if (field.hasModifierProperty(PsiModifier.PRIVATE)) {
|
||||
if (!myRefCountHolder.isReferenced(field)) {
|
||||
if (!myRefCountHolder.isReferenced(field) && !isImplicitUsage(field, progress)) {
|
||||
if (HighlightUtil.isSerializationImplicitlyUsedField(field)) {
|
||||
return null;
|
||||
}
|
||||
@@ -397,6 +396,9 @@ public class PostHighlightingPass extends TextEditorHighlightingPass {
|
||||
return info;
|
||||
}
|
||||
}
|
||||
else if (isImplicitUsage(field, progress)) {
|
||||
return null;
|
||||
}
|
||||
else if (!myRefCountHolder.isReferenced(field) && weAreSureThereAreNoUsages(field, progress)) {
|
||||
return formatUnusedSymbolHighlightInfo("field.is.not.used", field, "fields", myDeadCodeKey, myDeadCodeInfoType);
|
||||
}
|
||||
|
||||
@@ -32,6 +32,7 @@ import com.intellij.psi.*;
|
||||
import com.intellij.psi.controlFlow.DefUseUtil;
|
||||
import com.intellij.psi.search.GlobalSearchScope;
|
||||
import com.intellij.psi.search.searches.ReferencesSearch;
|
||||
import com.intellij.psi.tree.IElementType;
|
||||
import com.intellij.psi.util.PsiTreeUtil;
|
||||
import com.intellij.psi.util.PsiUtil;
|
||||
import com.intellij.refactoring.HelpID;
|
||||
@@ -247,8 +248,8 @@ public class InlineLocalHandler extends JavaInlineActionHandler {
|
||||
parent = parent.getParent();
|
||||
}
|
||||
|
||||
if (parent instanceof PsiAssignmentExpression && element == ((PsiAssignmentExpression)parent).getLExpression() ||
|
||||
parent instanceof PsiPrefixExpression || parent instanceof PsiPostfixExpression ) {
|
||||
if (parent instanceof PsiAssignmentExpression && element == ((PsiAssignmentExpression)parent).getLExpression()
|
||||
|| isUnaryWriteExpression(parent)) {
|
||||
|
||||
EditorColorsManager manager = EditorColorsManager.getInstance();
|
||||
final TextAttributes writeAttributes = manager.getGlobalScheme().getAttributes(EditorColors.WRITE_SEARCH_RESULT_ATTRIBUTES);
|
||||
@@ -262,6 +263,17 @@ public class InlineLocalHandler extends JavaInlineActionHandler {
|
||||
return false;
|
||||
}
|
||||
|
||||
private static boolean isUnaryWriteExpression(PsiElement parent) {
|
||||
IElementType tokenType = null;
|
||||
if (parent instanceof PsiPrefixExpression) {
|
||||
tokenType = ((PsiPrefixExpression)parent).getOperationTokenType();
|
||||
}
|
||||
if (parent instanceof PsiPostfixExpression) {
|
||||
tokenType = ((PsiPostfixExpression)parent).getOperationTokenType();
|
||||
}
|
||||
return tokenType == JavaTokenType.PLUSPLUS || tokenType == JavaTokenType.MINUSMINUS;
|
||||
}
|
||||
|
||||
private static boolean isSameDefinition(final PsiElement def, final PsiExpression defToInline) {
|
||||
if (def instanceof PsiLocalVariable) return defToInline.equals(((PsiLocalVariable)def).getInitializer());
|
||||
final PsiElement parent = def.getParent();
|
||||
|
||||
@@ -0,0 +1,7 @@
|
||||
class Test {
|
||||
|
||||
void get(){
|
||||
boolean toInline = false;
|
||||
if (!to<caret>Inline) return;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,6 @@
|
||||
class Test {
|
||||
|
||||
void get(){
|
||||
if (!false) return;
|
||||
}
|
||||
}
|
||||
@@ -62,6 +62,10 @@ public class InlineLocalTest extends LightCodeInsightTestCase {
|
||||
doTest(false);
|
||||
}
|
||||
|
||||
public void testNonWriteUnaryExpression() throws Exception {
|
||||
doTest(true);
|
||||
}
|
||||
|
||||
public void testNewExpression() throws Exception {
|
||||
doTest(false);
|
||||
}
|
||||
|
||||
+14
-5
@@ -2,7 +2,7 @@
|
||||
<form xmlns="http://www.intellij.com/uidesigner/form/" version="1" bind-to-class="com.intellij.application.options.editor.EditorOptionsPanel">
|
||||
<tabbedpane id="97a1e">
|
||||
<constraints>
|
||||
<xy x="22" y="49" width="622" height="455"/>
|
||||
<xy x="22" y="49" width="781" height="495"/>
|
||||
</constraints>
|
||||
<properties/>
|
||||
<border type="none"/>
|
||||
@@ -64,7 +64,7 @@
|
||||
</component>
|
||||
</children>
|
||||
</grid>
|
||||
<grid id="5342a" layout-manager="GridLayoutManager" row-count="3" column-count="1" same-size-horizontally="false" same-size-vertically="false" hgap="-1" vgap="-1">
|
||||
<grid id="5342a" layout-manager="GridLayoutManager" row-count="4" column-count="1" same-size-horizontally="false" same-size-vertically="false" hgap="-1" vgap="-1">
|
||||
<margin top="0" left="0" bottom="0" right="0"/>
|
||||
<constraints>
|
||||
<grid row="0" column="1" row-span="1" col-span="1" vsize-policy="0" hsize-policy="0" anchor="1" fill="3" indent="0" use-parent-layout="false">
|
||||
@@ -76,7 +76,7 @@
|
||||
<children>
|
||||
<component id="400d4" class="javax.swing.JCheckBox" binding="myCbVirtualPageAtBottom">
|
||||
<constraints>
|
||||
<grid row="2" column="0" row-span="1" col-span="1" vsize-policy="0" hsize-policy="7" anchor="8" fill="0" indent="0" use-parent-layout="false"/>
|
||||
<grid row="3" column="0" row-span="1" col-span="1" vsize-policy="0" hsize-policy="7" anchor="8" fill="0" indent="0" use-parent-layout="false"/>
|
||||
</constraints>
|
||||
<properties>
|
||||
<text resource-bundle="messages/ApplicationBundle" key="checkbox.show.virtual.space.at.file.bottom"/>
|
||||
@@ -84,7 +84,7 @@
|
||||
</component>
|
||||
<component id="f762" class="javax.swing.JCheckBox" binding="myCbCaretInsideTabs">
|
||||
<constraints>
|
||||
<grid row="1" column="0" row-span="1" col-span="1" vsize-policy="0" hsize-policy="3" anchor="8" fill="0" indent="0" use-parent-layout="false"/>
|
||||
<grid row="2" column="0" row-span="1" col-span="1" vsize-policy="0" hsize-policy="3" anchor="8" fill="0" indent="0" use-parent-layout="false"/>
|
||||
</constraints>
|
||||
<properties>
|
||||
<text resource-bundle="messages/ApplicationBundle" key="checkbox.allow.placement.of.caret.inside.tabs"/>
|
||||
@@ -92,13 +92,22 @@
|
||||
</component>
|
||||
<component id="bb18b" class="javax.swing.JCheckBox" binding="myCbVirtualSpace">
|
||||
<constraints>
|
||||
<grid row="0" column="0" row-span="1" col-span="1" vsize-policy="0" hsize-policy="3" anchor="8" fill="0" indent="0" use-parent-layout="false"/>
|
||||
<grid row="1" column="0" row-span="1" col-span="1" 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.allow.placement.of.caret.after.end.of.line"/>
|
||||
</properties>
|
||||
</component>
|
||||
<component id="37683" class="javax.swing.JCheckBox" binding="myCbUseSoftWraps">
|
||||
<constraints>
|
||||
<grid row="0" column="0" row-span="1" col-span="1" 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.use.soft.wraps"/>
|
||||
</properties>
|
||||
</component>
|
||||
</children>
|
||||
</grid>
|
||||
<grid id="dcf48" layout-manager="GridLayoutManager" row-count="3" column-count="1" same-size-horizontally="false" same-size-vertically="false" hgap="-1" vgap="-1">
|
||||
|
||||
@@ -72,6 +72,7 @@ public class EditorOptionsPanel {
|
||||
private JCheckBox myCbEnsureBlankLineBeforeCheckBox;
|
||||
private JCheckBox myShowReformatCodeDialogCheckBox;
|
||||
private JCheckBox myShowOptimizeImportsDialogCheckBox;
|
||||
private JCheckBox myCbUseSoftWraps;
|
||||
private final ErrorHighlightingPanel myErrorHighlightingPanel = new ErrorHighlightingPanel();
|
||||
private final MyConfigurable myConfigurable;
|
||||
|
||||
@@ -115,6 +116,7 @@ public class EditorOptionsPanel {
|
||||
|
||||
// Virtual space
|
||||
|
||||
myCbUseSoftWraps.setSelected(editorSettings.isUseSoftWraps());
|
||||
myCbVirtualSpace.setSelected(editorSettings.isVirtualSpace());
|
||||
myCbCaretInsideTabs.setSelected(editorSettings.isCaretInsideTabs());
|
||||
myCbVirtualPageAtBottom.setSelected(editorSettings.isAdditionalPageAtBottom());
|
||||
@@ -175,6 +177,7 @@ public class EditorOptionsPanel {
|
||||
|
||||
// Virtual space
|
||||
|
||||
editorSettings.setUseSoftWraps(myCbUseSoftWraps.isSelected());
|
||||
editorSettings.setVirtualSpace(myCbVirtualSpace.isSelected());
|
||||
editorSettings.setCaretInsideTabs(myCbCaretInsideTabs.isSelected());
|
||||
editorSettings.setAdditionalPageAtBottom(myCbVirtualPageAtBottom.isSelected());
|
||||
@@ -292,6 +295,7 @@ public class EditorOptionsPanel {
|
||||
isModified |= isModified(myCbHighlightIdentifierUnderCaret, codeInsightSettings.HIGHLIGHT_IDENTIFIER_UNDER_CARET);
|
||||
|
||||
// Virtual space
|
||||
isModified |= isModified(myCbUseSoftWraps, editorSettings.isUseSoftWraps());
|
||||
isModified |= isModified(myCbVirtualSpace, editorSettings.isVirtualSpace());
|
||||
isModified |= isModified(myCbCaretInsideTabs, editorSettings.isCaretInsideTabs());
|
||||
isModified |= isModified(myCbVirtualPageAtBottom, editorSettings.isAdditionalPageAtBottom());
|
||||
|
||||
@@ -26,8 +26,6 @@ import org.jetbrains.annotations.Nullable;
|
||||
* @author Maxim.Mossienko
|
||||
*/
|
||||
public interface SelfManagingCommenter<T extends CommenterDataHolder> {
|
||||
ExtensionPointName<SelfManagingCommenter> EP_NAME = ExtensionPointName.create("com.intellij.selfManagingCommenter");
|
||||
|
||||
@Nullable T createLineCommentingState(int startLine, int endLine, @NotNull Document document, @NotNull PsiFile file);
|
||||
@Nullable T createBlockCommentingState(int selectionStart, int selectionEnd, @NotNull Document document, @NotNull PsiFile file);
|
||||
|
||||
|
||||
@@ -87,6 +87,11 @@ class LazyEditor extends UserDataHolderBase implements Editor {
|
||||
return getEditor().getCaretModel();
|
||||
}
|
||||
|
||||
@NotNull
|
||||
public SoftWrapModel getSoftWrapModel() {
|
||||
return getEditor().getSoftWrapModel();
|
||||
}
|
||||
|
||||
@NotNull
|
||||
public EditorSettings getSettings() {
|
||||
return getEditor().getSettings();
|
||||
|
||||
@@ -19,50 +19,87 @@ package com.intellij.ide.actions;
|
||||
import com.intellij.ide.IdeBundle;
|
||||
import com.intellij.ide.IdeView;
|
||||
import com.intellij.openapi.actionSystem.*;
|
||||
import com.intellij.openapi.project.Project;
|
||||
import com.intellij.openapi.project.DumbAware;
|
||||
import com.intellij.openapi.project.Project;
|
||||
import com.intellij.openapi.ui.popup.JBPopupFactory;
|
||||
import com.intellij.openapi.ui.popup.ListPopup;
|
||||
import com.intellij.openapi.util.Condition;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
public class NewElementAction extends AnAction implements DumbAware {
|
||||
|
||||
public void actionPerformed(final AnActionEvent e) {
|
||||
showPopup(e.getDataContext());
|
||||
/**
|
||||
* @author Konstantin Bulenkov
|
||||
*/
|
||||
@SuppressWarnings({"MethodMayBeStatic"})
|
||||
public class NewElementAction extends AnAction implements DumbAware {
|
||||
public void actionPerformed(final AnActionEvent event) {
|
||||
showPopup(event.getDataContext());
|
||||
}
|
||||
|
||||
protected void showPopup(DataContext dataContext) {
|
||||
final ListPopup popup = JBPopupFactory.getInstance()
|
||||
.createActionGroupPopup(getPopupTitle(),
|
||||
getGroup(),
|
||||
dataContext,
|
||||
false, false, false,
|
||||
null, -1, LangDataKeys.PRESELECT_NEW_ACTION_CONDITION.getData(dataContext));
|
||||
protected void showPopup(DataContext context) {
|
||||
createPopup(context).showInBestPositionFor(context);
|
||||
}
|
||||
|
||||
popup.showInBestPositionFor(dataContext);
|
||||
protected ListPopup createPopup(DataContext dataContext) {
|
||||
return JBPopupFactory.getInstance()
|
||||
.createActionGroupPopup(getPopupTitle(),
|
||||
getGroup(dataContext),
|
||||
dataContext,
|
||||
isShowNumbers(),
|
||||
isShowDisabledActions(),
|
||||
isHonorActionMnemonics(),
|
||||
getDisposeCallback(),
|
||||
getMaxRowCount(),
|
||||
getPreselectActionCondition(dataContext));
|
||||
}
|
||||
|
||||
protected int getMaxRowCount() {
|
||||
return -1;
|
||||
}
|
||||
|
||||
@Nullable
|
||||
protected Condition<AnAction> getPreselectActionCondition(DataContext dataContext) {
|
||||
return LangDataKeys.PRESELECT_NEW_ACTION_CONDITION.getData(dataContext);
|
||||
}
|
||||
|
||||
@Nullable
|
||||
protected Runnable getDisposeCallback() {
|
||||
return null;
|
||||
}
|
||||
|
||||
protected boolean isHonorActionMnemonics() {
|
||||
return false;
|
||||
}
|
||||
|
||||
protected boolean isShowDisabledActions() {
|
||||
return false;
|
||||
}
|
||||
|
||||
protected boolean isShowNumbers() {
|
||||
return false;
|
||||
}
|
||||
|
||||
protected String getPopupTitle() {
|
||||
return IdeBundle.message("title.popup.new.element");
|
||||
}
|
||||
|
||||
public void update(AnActionEvent event){
|
||||
Presentation presentation = event.getPresentation();
|
||||
DataContext dataContext = event.getDataContext();
|
||||
Project project = PlatformDataKeys.PROJECT.getData(dataContext);
|
||||
public void update(AnActionEvent e){
|
||||
final Presentation presentation = e.getPresentation();
|
||||
final DataContext context = e.getDataContext();
|
||||
final Project project = PlatformDataKeys.PROJECT.getData(context);
|
||||
if (project == null) {
|
||||
presentation.setEnabled(false);
|
||||
return;
|
||||
}
|
||||
IdeView ideView = LangDataKeys.IDE_VIEW.getData(dataContext);
|
||||
final IdeView ideView = LangDataKeys.IDE_VIEW.getData(context);
|
||||
if (ideView == null) {
|
||||
presentation.setEnabled(false);
|
||||
return;
|
||||
}
|
||||
|
||||
presentation.setEnabled(!ActionGroupUtil.isGroupEmpty(getGroup(), event));
|
||||
presentation.setEnabled(!ActionGroupUtil.isGroupEmpty(getGroup(context), e));
|
||||
}
|
||||
|
||||
private static ActionGroup getGroup() {
|
||||
protected ActionGroup getGroup(DataContext dataContext) {
|
||||
return (ActionGroup)ActionManager.getInstance().getAction(IdeActions.GROUP_WEIGHING_NEW);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -203,6 +203,11 @@ public class EditorWindow implements EditorEx, UserDataHolderEx {
|
||||
return myDelegate.getScrollingModel();
|
||||
}
|
||||
|
||||
@NotNull
|
||||
public SoftWrapModel getSoftWrapModel() {
|
||||
return myDelegate.getSoftWrapModel();
|
||||
}
|
||||
|
||||
@NotNull
|
||||
public EditorSettings getSettings() {
|
||||
return myDelegate.getSettings();
|
||||
|
||||
+11
-2
@@ -78,17 +78,26 @@ public class CopyFilesOrDirectoriesHandler implements CopyHandlerDelegate {
|
||||
CopyFilesOrDirectoriesDialog dialog = new CopyFilesOrDirectoriesDialog(elements, defaultTargetDirectory, project, false);
|
||||
dialog.show();
|
||||
if (dialog.isOK()) {
|
||||
final String newName = elements.length == 1 ? dialog.getNewName() : null;
|
||||
final PsiManager psiManager = PsiManager.getInstance(project);
|
||||
try {
|
||||
for (PsiElement element : elements) {
|
||||
psiManager.checkMove(element, dialog.getTargetDirectory());
|
||||
PsiFileSystemItem psiElement = (PsiFileSystemItem)element;
|
||||
if (newName != null) {
|
||||
if (!psiElement.isDirectory()) {
|
||||
psiElement = (PsiFileSystemItem)psiElement.copy();
|
||||
psiElement.setName(newName);
|
||||
} else {
|
||||
dialog.getTargetDirectory().checkCreateSubdirectory(newName);
|
||||
}
|
||||
}
|
||||
psiManager.checkMove(psiElement, dialog.getTargetDirectory());
|
||||
}
|
||||
}
|
||||
catch (IncorrectOperationException e) {
|
||||
CommonRefactoringUtil.showErrorHint(project, null, e.getMessage(), CommonBundle.getErrorTitle(), null);
|
||||
return;
|
||||
}
|
||||
String newName = elements.length == 1 ? dialog.getNewName() : null;
|
||||
copyImpl(elements, newName, dialog.getTargetDirectory(), false);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -275,18 +275,21 @@ public class OSProcessHandler extends ProcessHandler {
|
||||
}
|
||||
|
||||
public void readAvailable(char[] buffer) throws IOException {
|
||||
int fairCount = 0;
|
||||
while (myReader.ready()) {
|
||||
int n = myReader.read(buffer);
|
||||
if (n > 0) {
|
||||
int start = 0;
|
||||
int end = 0;
|
||||
while (start < n) {
|
||||
while (end < n && buffer[end++] != '\n');
|
||||
if (n <= 0) break;
|
||||
|
||||
myNotificationQueue.offer(new String(buffer, start, end - start));
|
||||
start = end;
|
||||
}
|
||||
int start = 0;
|
||||
int end = 0;
|
||||
while (start < n) {
|
||||
while (end < n && buffer[end++] != '\n');
|
||||
|
||||
myNotificationQueue.offer(new String(buffer, start, end - start));
|
||||
start = end;
|
||||
}
|
||||
|
||||
if (++fairCount > 10) return;
|
||||
}
|
||||
|
||||
if (isProcessTerminated()) {
|
||||
|
||||
@@ -32,6 +32,13 @@ import java.util.HashSet;
|
||||
public abstract class ActionGroup extends AnAction implements DumbAware {
|
||||
private boolean myPopup;
|
||||
private final PropertyChangeSupport myChangeSupport = new PropertyChangeSupport(this);
|
||||
public static final ActionGroup EMPTY_GROUP = new ActionGroup() {
|
||||
@NotNull
|
||||
@Override
|
||||
public AnAction[] getChildren(@Nullable AnActionEvent e) {
|
||||
return EMPTY_ARRAY;
|
||||
}
|
||||
};
|
||||
|
||||
private Set<AnAction> mySecondaryActions;
|
||||
|
||||
@@ -145,4 +152,4 @@ public abstract class ActionGroup extends AnAction implements DumbAware {
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -96,8 +96,14 @@ public interface CaretModel {
|
||||
*/
|
||||
void removeCaretListener(CaretListener listener);
|
||||
|
||||
/**
|
||||
* @return document offset for the start of the logical line where caret is located
|
||||
*/
|
||||
int getVisualLineStart();
|
||||
|
||||
/**
|
||||
* @return document offset for the end of the logical line where caret is located
|
||||
*/
|
||||
int getVisualLineEnd();
|
||||
|
||||
TextAttributes getTextAttributes();
|
||||
|
||||
@@ -111,6 +111,14 @@ public interface Editor extends UserDataHolder {
|
||||
*/
|
||||
@NotNull CaretModel getCaretModel();
|
||||
|
||||
/**
|
||||
* Returns the soft wrap model for the document, which can be used to get information about soft wraps registered
|
||||
* for the editor document at the moment and provides basic management functions for them.
|
||||
*
|
||||
* @return the soft wrap model instance
|
||||
*/
|
||||
@NotNull SoftWrapModel getSoftWrapModel();
|
||||
|
||||
/**
|
||||
* Returns the editor settings for this editor instance. Changes to these settings affect
|
||||
* only the current editor instance.
|
||||
|
||||
@@ -83,9 +83,12 @@ public class EditorModificationUtil {
|
||||
editor.getCaretModel().moveToOffset(selectionModel.getSelectionStart());
|
||||
}
|
||||
|
||||
// There is a possible case that particular soft wraps become hard wraps if the caret is located at soft wrap-introduced virtual
|
||||
// space, hence, we need to give editor a chance to react accordingly.
|
||||
editor.getSoftWrapModel().beforeDocumentChange(editor.getCaretModel().getVisualPosition());
|
||||
int oldOffset = editor.getCaretModel().getOffset();
|
||||
|
||||
String filler = calcStringToFillVitualSpace(editor);
|
||||
String filler = calcStringToFillVirtualSpace(editor);
|
||||
if (filler.length() > 0) {
|
||||
s = filler + s;
|
||||
}
|
||||
@@ -231,7 +234,7 @@ public class EditorModificationUtil {
|
||||
return columnNumber - lineEndColumnNumber;
|
||||
}
|
||||
|
||||
public static String calcStringToFillVitualSpace(Editor editor) {
|
||||
public static String calcStringToFillVirtualSpace(Editor editor) {
|
||||
int afterLineEnd = calcAfterLineEnd(editor);
|
||||
if (afterLineEnd > 0) {
|
||||
final Project project = editor.getProject();
|
||||
|
||||
@@ -96,6 +96,8 @@ public interface EditorSettings {
|
||||
void setRefrainFromScrolling(boolean b);
|
||||
|
||||
boolean isIndentGuidesShown();
|
||||
|
||||
void setIndentGuidesShown(boolean val);
|
||||
|
||||
boolean isUseSoftWraps();
|
||||
void setUseSoftWraps(boolean use);
|
||||
}
|
||||
|
||||
@@ -21,6 +21,11 @@ import org.jetbrains.annotations.NonNls;
|
||||
* Represents a logical position in the editor. Logical positions ignore folding -
|
||||
* for example, if the top 10 lines of the document are folded, the 10th line in the document
|
||||
* will have the line number 10 in its logical position.
|
||||
* <p/>
|
||||
* Logical position may store additional parameters that define its mapping to {@link VisualPosition}. Rationale is that
|
||||
* single logical <code>(line; column)</code> pair matches soft wrap-introduced virtual space, i.e. different visual positions
|
||||
* correspond to the same logical position. It's convenient to store exact visual location details within the logical
|
||||
* position in order to relief further {@code 'logical position' -> 'visual position'} mapping.
|
||||
*
|
||||
* @see Editor#offsetToLogicalPosition(int)
|
||||
* @see Editor#logicalPositionToOffset(LogicalPosition)
|
||||
@@ -34,29 +39,126 @@ public class LogicalPosition implements Comparable<LogicalPosition> {
|
||||
public final int line;
|
||||
public final int column;
|
||||
|
||||
/**
|
||||
* Identifies if current logical position may be correctly mapped to visual position. E.g. we can define properties like
|
||||
* {@link #softWrapLines}, {@link #softWrapColumnDiff} etc during {@code 'visual position' -> 'logical position'} conversion
|
||||
* in order to be able to easy match it back to visual position.
|
||||
*/
|
||||
public final boolean visualPositionAware;
|
||||
|
||||
/**
|
||||
* Number of virtual soft wrap-introduced lines before the visual position that corresponds to the current logical position.
|
||||
*
|
||||
* @see #visualPositionAware
|
||||
*/
|
||||
public final int softWrapLines;
|
||||
|
||||
/**
|
||||
* Number of virtual soft lines introduced by the current soft wrap before the visual position that corresponds
|
||||
* to the current logical position.
|
||||
* <p/>
|
||||
* This value is assumed to be not greater than {@link #softWrapLines} all the time.
|
||||
*
|
||||
* @see #visualPositionAware
|
||||
*/
|
||||
public final int linesFromActiveSoftWrap;
|
||||
|
||||
/**
|
||||
* Number to add to the {@link #column logical column} in order to get soft wrap-introduced visual column offset.
|
||||
*
|
||||
* @see #visualPositionAware
|
||||
*/
|
||||
public final int softWrapColumnDiff;
|
||||
|
||||
/**
|
||||
* Number of folded line feeds before the current position.
|
||||
*
|
||||
* @see #visualPositionAware
|
||||
*/
|
||||
public final int foldedLines;
|
||||
|
||||
/**
|
||||
* Number to add to the {@link #column logical column} in order to get folding-introduced visual column offset.
|
||||
*
|
||||
* @see #visualPositionAware
|
||||
*/
|
||||
public final int foldingColumnDiff;
|
||||
|
||||
public LogicalPosition(int line, int column) {
|
||||
this(line, column, 0, 0, 0, 0, 0, false);
|
||||
}
|
||||
|
||||
public LogicalPosition(int line, int column, int softWrapLines, int linesFromActiveSoftWrap, int softWrapColumnDiff,
|
||||
int foldedLines, int foldingColumnDiff)
|
||||
{
|
||||
this(line, column, softWrapLines, linesFromActiveSoftWrap, softWrapColumnDiff, foldedLines, foldingColumnDiff, true);
|
||||
}
|
||||
|
||||
private LogicalPosition(int line, int column, int softWrapLines, int linesFromActiveSoftWrap, int softWrapColumnDiff, int foldedLines,
|
||||
int foldingColumnDiff, boolean visualPositionAware)
|
||||
{
|
||||
assert linesFromActiveSoftWrap <= softWrapLines;
|
||||
|
||||
this.line = line;
|
||||
this.column = column;
|
||||
this.softWrapLines = softWrapLines;
|
||||
this.linesFromActiveSoftWrap = linesFromActiveSoftWrap;
|
||||
this.softWrapColumnDiff = softWrapColumnDiff;
|
||||
this.foldedLines = foldedLines;
|
||||
this.foldingColumnDiff = foldingColumnDiff;
|
||||
this.visualPositionAware = visualPositionAware;
|
||||
}
|
||||
|
||||
/**
|
||||
* Allows to answer if current position points to soft wrap-introduced visual line.
|
||||
*
|
||||
* @return <code>true</code> if current position points to soft wrap-introduced visual line; <code>false</code> otherwise
|
||||
*/
|
||||
public boolean isOnSoftWrappedLine() {
|
||||
return softWrapColumnDiff != 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* Builds visual position based on a state of the current logical position.
|
||||
* <p/>
|
||||
* Such visual position is considered to make sense only if current logical position
|
||||
* is {@link #visualPositionAware visual position aware}.
|
||||
*
|
||||
* @return visual position based on a state of the current logical position
|
||||
*/
|
||||
public VisualPosition toVisualPosition() {
|
||||
return new VisualPosition(line + softWrapLines - foldedLines, column + softWrapColumnDiff + foldingColumnDiff);
|
||||
}
|
||||
|
||||
public boolean equals(Object o) {
|
||||
if (!(o instanceof LogicalPosition)) return false;
|
||||
final LogicalPosition logicalPosition = (LogicalPosition) o;
|
||||
|
||||
return column == logicalPosition.column && line == logicalPosition.line;
|
||||
return column == logicalPosition.column && line == logicalPosition.line && softWrapLines == logicalPosition.softWrapLines
|
||||
&& linesFromActiveSoftWrap == logicalPosition.linesFromActiveSoftWrap
|
||||
&& softWrapColumnDiff == logicalPosition.softWrapColumnDiff && foldedLines == logicalPosition.foldedLines
|
||||
&& foldingColumnDiff == logicalPosition.foldingColumnDiff;
|
||||
}
|
||||
|
||||
public int hashCode() {
|
||||
return 29 * line + column;
|
||||
int result = 29 * line + column;
|
||||
result = result * 29 + softWrapLines;
|
||||
result = 29 * result + softWrapColumnDiff;
|
||||
result = 29 * result + foldedLines;
|
||||
return 29 * result + foldingColumnDiff;
|
||||
}
|
||||
|
||||
@NonNls
|
||||
public String toString() {
|
||||
return "LogicalPosition: line = " + line + " column = " + column;
|
||||
return "LogicalPosition: line=" + line + " column=" + column + "; visual position aware=" + visualPositionAware
|
||||
+ " soft wrap: lines=" + softWrapLines + " (active=" + linesFromActiveSoftWrap + ") columns diff=" + softWrapColumnDiff
|
||||
+ "; folding: lines = " + foldedLines + " columns diff=" + foldingColumnDiff;
|
||||
}
|
||||
|
||||
public int compareTo(LogicalPosition position) {
|
||||
if (line != position.line) return line - position.line;
|
||||
return column - position.column;
|
||||
if (column != position.column) return column - position.column;
|
||||
if (softWrapLines != position.softWrapLines) return softWrapLines - position.softWrapLines;
|
||||
return softWrapColumnDiff - position.softWrapColumnDiff;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,97 @@
|
||||
/*
|
||||
* Copyright 2000-2010 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;
|
||||
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
/**
|
||||
* Defines contract for the services that bring <code>'soft wrapping'</code> to the editor.
|
||||
* <p/>
|
||||
* <code>'Soft wrap'</code> here means representation of the line that exceeds right component viewport margin as if it is wrapped.
|
||||
* No changes are introduced to the actual document/file system during that, i.e. it's a pure <code>'view'</code> facility.
|
||||
* <p/>
|
||||
* <b>Example:</b>
|
||||
* <p/>
|
||||
* <b>Raw text (as stored at file system)</b>
|
||||
* <p/>
|
||||
* <pre>
|
||||
* public class Test { | <- right margin
|
||||
* |
|
||||
* public void baz() { |
|
||||
* foo("test1", "test2", "test3", "test4", "test5", "test6");
|
||||
* } |
|
||||
* |
|
||||
* public void foo(Object ... args) { |
|
||||
* } |
|
||||
* } |
|
||||
* </pre>
|
||||
* <p/>
|
||||
* <b>The same text with soft wraps as shown to end-user at editor</b>
|
||||
* <p/>
|
||||
* <pre>
|
||||
* public class Test { | <- right margin
|
||||
* |
|
||||
* public void baz() { |
|
||||
* foo("test1", "test2", "test3", "test4", "test5", |
|
||||
* "test6"); |
|
||||
* } |
|
||||
* |
|
||||
* public void foo(Object ... args) { |
|
||||
* } |
|
||||
* } |
|
||||
* </pre>
|
||||
* <p/>
|
||||
* Another important soft wrap feature is that as soon as the user starts typing on a line which representation is affected by
|
||||
* soft wrap (e.g. starts adding new call argument after <code>"test6"</code> at example above), that soft wraps becomes
|
||||
* <code>'hard wrap'</code>, i.e. virtual changes introduced by it are flushed to the underlying document.
|
||||
* <p/>
|
||||
* <b>Note:</b> soft wrap is assumed to provide as user-friendly indentation for those wrapped line as possible
|
||||
* (note that <code>"test6"</code> at example below is aligned to the parameters start).
|
||||
* <p/>
|
||||
* Implementations of this interface are not obliged to be thread-safe.
|
||||
*
|
||||
* @author Denis Zhdanov
|
||||
* @since Jun 8, 2010 3:15:18 PM
|
||||
*/
|
||||
public interface SoftWrapModel {
|
||||
|
||||
/**
|
||||
* Allows to answer if <code>'soft wrap'</code> feature is enabled.
|
||||
*
|
||||
* @return <code>true</code> if <code>'soft wraps'</code> are enabled; <code>false</code> otherwise
|
||||
*/
|
||||
boolean isSoftWrappingEnabled();
|
||||
|
||||
/**
|
||||
* Asks current model for the soft wrap registered for the given document offset if any.
|
||||
*
|
||||
* @param offset target document offset
|
||||
* @return soft wrap registered for the given offset within the current model if any; <code>null</code> otherwise
|
||||
*/
|
||||
@Nullable
|
||||
TextChange getSoftWrap(int offset);
|
||||
|
||||
/**
|
||||
* Notifies current model that target document is about to be changed at location identified by the given visual position.
|
||||
* <p/>
|
||||
* Primary purpose of this method is to perform {@code 'soft wrap' -> 'hard wrap'} conversion if the user types in virtual
|
||||
* soft wraps-introduced space.
|
||||
*
|
||||
* @param position position where the document is to be modified
|
||||
*/
|
||||
void beforeDocumentChange(@NotNull VisualPosition position);
|
||||
}
|
||||
@@ -0,0 +1,175 @@
|
||||
/*
|
||||
* Copyright 2000-2010 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;
|
||||
|
||||
import com.intellij.util.text.CharArrayUtil;
|
||||
import com.intellij.util.text.CharSequenceBackedByArray;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
|
||||
import java.util.concurrent.atomic.AtomicReference;
|
||||
|
||||
/**
|
||||
* Provides generic contract for object encapsulating information about single unit of text change.
|
||||
* <p/>
|
||||
* Thread-safe.
|
||||
*
|
||||
* @author Denis Zhdanov
|
||||
* @since May 31, 2010 12:26:51 PM
|
||||
*/
|
||||
public class TextChange {
|
||||
|
||||
private final AtomicReference<char[]> myChars = new AtomicReference<char[]>();
|
||||
private final CharSequence myText;
|
||||
private final int myStart;
|
||||
private final int myEnd;
|
||||
|
||||
/**
|
||||
* Shorthand for creating {@link TextChange} with the given arguments where <code>'end index'</code> has the same value as
|
||||
* <code>'start index'</code>.
|
||||
*
|
||||
* @param text text affected by the current change
|
||||
* @param start start index (inclusive) of text range affected by the change encapsulated by the current object
|
||||
* @throws IllegalArgumentException if given start index is invalid
|
||||
*/
|
||||
public TextChange(@NotNull CharSequence text, int start) throws IllegalArgumentException {
|
||||
this(text, start, start);
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates new <code>TextChange</code> object with the given arguments. It encapsulates information about the change that
|
||||
* may be applied to the target document.
|
||||
*
|
||||
* @param text text that is introduced by the current change
|
||||
* @param start start index of the target document location where current change is to be applied
|
||||
* @param end end index of the target document where current change is to be applied, i.e. it's assumed that current text
|
||||
* change appliance to particular document causes replacement of its original text at <code>[start; end)</code>
|
||||
* interval by the text encapsulated by the current change. I.e. original text is replaced by the new one
|
||||
* @throws IllegalArgumentException if given start or end index in invalid or they are inconsistent to each other
|
||||
*/
|
||||
public TextChange(@NotNull CharSequence text, int start, int end) throws IllegalArgumentException {
|
||||
if (start < 0) {
|
||||
throw new IllegalArgumentException(String.format("Can't construct new %s object. Reason: given start index (%d) is negative. "
|
||||
+ "End index: %d, text: '%s'", getClass().getName(), start, end, text));
|
||||
}
|
||||
if (end < start) {
|
||||
throw new IllegalArgumentException(String.format("Can't construct new %s object. Reason: given end index (%d) is less than "
|
||||
+ "start index (%d). Text: '%s'", getClass().getName(), end, start, text));
|
||||
}
|
||||
myText = text;
|
||||
myStart = start;
|
||||
myEnd = end;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return start index (inclusive) of text range affected by the change encapsulated at the current object
|
||||
*/
|
||||
public int getStart() {
|
||||
return myStart;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return end index (exclusive) of text range affected by the change encapsulated at the current object
|
||||
*/
|
||||
public int getEnd() {
|
||||
return myEnd;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Allows to retrieve text that is directly affected by the change encapsulated by the current object.
|
||||
*
|
||||
* @return text related to the change encapsulated by the current object
|
||||
*/
|
||||
@NotNull
|
||||
public CharSequence getText() {
|
||||
return myText;
|
||||
}
|
||||
|
||||
/**
|
||||
* Allows to get change text as a char array. Note that it's not guaranteed that change text directly maps to the returned char array,
|
||||
* i.e. change to array content is not obeyed to be reflected in {@link #getText()} result.
|
||||
* <p/>
|
||||
* Generally speaking, this method is introduced just as a step toward existing high-performance services that work in terms
|
||||
* of char arrays. Resulting array is instantiated on-demand via {@link CharArrayUtil#fromSequence(CharSequence)}, hence, it
|
||||
* doesn't hit memory if, for example, {@link CharSequenceBackedByArray} is used as initial change text.
|
||||
*
|
||||
* @return stored change text as a char array
|
||||
*/
|
||||
@NotNull
|
||||
public char[] getChars() {
|
||||
char[] result = myChars.get();
|
||||
if (result != null) {
|
||||
return result;
|
||||
}
|
||||
myChars.compareAndSet(null, CharArrayUtil.fromSequence(myText));
|
||||
return myChars.get();
|
||||
}
|
||||
|
||||
/**
|
||||
* Difference in document symbols number after current change appliance.
|
||||
* <p/>
|
||||
* <b>Note:</b> returned number may be either positive or not. For example it may be negative for <code>'remove'</code>
|
||||
* or <code>'replace'</code> changes (number of text symbols is less than number of symbols at target change interval)
|
||||
*
|
||||
* @return difference in document symbols number after current change appliance
|
||||
*/
|
||||
public int getDiff() {
|
||||
return myText.length() - myEnd + myStart;
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates new {@link TextChange} on the basis of the current object with given offset applied to its {@link #getStart() start}
|
||||
* and {@link #getEnd() end} properties.
|
||||
*
|
||||
* @param offset offset to apply to the current change object
|
||||
* @return text change that is built on the basis of the current object that with {@link #getStart() start}
|
||||
* and {@link #getEnd() end} positions shifted to the given offset
|
||||
* @throws IllegalArgumentException if start index becomes zero after given offset appliance (it is not applied then)
|
||||
*/
|
||||
public TextChange advance(int offset) throws IllegalArgumentException {
|
||||
if (offset == 0) {
|
||||
return this;
|
||||
}
|
||||
int newStart = myStart + offset;
|
||||
if (newStart < 0) {
|
||||
throw new IllegalArgumentException(String.format("Can't apply given offset (%d) to the current text change object (%s). Reason: "
|
||||
+ "new start index becomes negative after that (%d)", offset, this, newStart));
|
||||
}
|
||||
|
||||
return new TextChange(myText, newStart, myEnd + offset);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(Object o) {
|
||||
if (this == o) return true;
|
||||
if (o == null || getClass() != o.getClass()) return false;
|
||||
|
||||
TextChange that = (TextChange)o;
|
||||
return myText.equals(that.myText) && myStart == that.myStart && myEnd == that.myEnd;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
int result = myText.hashCode();
|
||||
result = 31 * result + myStart;
|
||||
return 31 * result + myEnd;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return String.format("%d-%d: '%s'", myStart, myEnd, myText);
|
||||
}
|
||||
}
|
||||
@@ -25,6 +25,7 @@ import com.intellij.ui.ListScrollingUtil;
|
||||
import com.intellij.ui.speedSearch.ListWithFilter;
|
||||
import com.intellij.ui.treeStructure.treetable.TreeTable;
|
||||
import com.intellij.util.Function;
|
||||
import com.intellij.util.ui.ButtonlessScrollBarUI;
|
||||
import com.intellij.util.ui.UIUtil;
|
||||
import com.intellij.util.ui.tree.TreeUtil;
|
||||
import org.jetbrains.annotations.Nls;
|
||||
@@ -185,7 +186,6 @@ public class PopupChooserBuilder {
|
||||
}
|
||||
}
|
||||
|
||||
myChooserComponent.setBorder(BorderFactory.createEmptyBorder(5, 5, 5, 5));
|
||||
|
||||
(list != null ? list : myChooserComponent).addMouseListener(new MouseAdapter() {
|
||||
@Override
|
||||
@@ -217,6 +217,9 @@ public class PopupChooserBuilder {
|
||||
}
|
||||
|
||||
scrollPane.getViewport().setCursor(Cursor.getPredefinedCursor(Cursor.HAND_CURSOR));
|
||||
((JComponent)scrollPane.getViewport().getView()).setBorder(BorderFactory.createEmptyBorder(5, 5, 5, 5));
|
||||
|
||||
scrollPane.getVerticalScrollBar().setUI(ButtonlessScrollBarUI.createMini());
|
||||
|
||||
if (myChooserComponent instanceof ListWithFilter) {
|
||||
contentPane.add(myChooserComponent, BorderLayout.CENTER);
|
||||
|
||||
@@ -0,0 +1,210 @@
|
||||
/*
|
||||
* Copyright 2000-2010 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.
|
||||
*/
|
||||
|
||||
/*
|
||||
* @author max
|
||||
*/
|
||||
package com.intellij.util.ui;
|
||||
|
||||
import com.intellij.openapi.util.Disposer;
|
||||
import com.intellij.ui.LightColors;
|
||||
|
||||
import javax.swing.*;
|
||||
import javax.swing.plaf.basic.BasicScrollBarUI;
|
||||
import java.awt.*;
|
||||
import java.awt.event.AdjustmentEvent;
|
||||
import java.awt.event.AdjustmentListener;
|
||||
|
||||
public class ButtonlessScrollBarUI extends BasicScrollBarUI {
|
||||
|
||||
private static final Color GRADIENT_LIGHT = new SameColor(0xfb);
|
||||
private static final Color GRADIENT_DARK = new SameColor(0xd7);
|
||||
private static final Color GRADIENT_THUMB_BORDER = new SameColor(0xc9);
|
||||
private static final Color PLAIN_THUMB_FILL = new SameColor(0xe5);
|
||||
private static final Color PLAIN_THUMB_BORDER = new SameColor(0xd0);
|
||||
private static final Color TRACK_BACKGROUND = LightColors.SLIGHTLY_GRAY;
|
||||
private static final Color TRACK_BORDER = new SameColor(230);
|
||||
|
||||
private final boolean myIsMini;
|
||||
private final AdjustmentListener myAdjustmentListener;
|
||||
private final Animator myAnimator;
|
||||
|
||||
private int myAnimationColorShift = 0;
|
||||
|
||||
private ButtonlessScrollBarUI(boolean isMini) {
|
||||
myIsMini = isMini;
|
||||
myAdjustmentListener = new AdjustmentListener() {
|
||||
@Override
|
||||
public void adjustmentValueChanged(AdjustmentEvent e) {
|
||||
myAnimator.reset();
|
||||
myAnimator.resume();
|
||||
}
|
||||
};
|
||||
|
||||
myAnimator = new Animator("Adjustment fadeout", myIsMini ? 5 : 10, myIsMini ? 300 : 500, false, 1, 0) {
|
||||
@Override
|
||||
public void paintNow(float frame, float totalFrames, float cycle) {
|
||||
myAnimationColorShift = (int)((totalFrames - frame) * 3);
|
||||
scrollbar.repaint();
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
public static BasicScrollBarUI createNormal() {
|
||||
return new ButtonlessScrollBarUI(false);
|
||||
}
|
||||
|
||||
public static BasicScrollBarUI createMini() {
|
||||
return new ButtonlessScrollBarUI(true);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void installUI(JComponent c) {
|
||||
if (myIsMini) {
|
||||
c.putClientProperty("JComponent.sizeVariant", "mini");
|
||||
}
|
||||
|
||||
super.installUI(c);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void installListeners() {
|
||||
super.installListeners();
|
||||
scrollbar.addAdjustmentListener(myAdjustmentListener);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void uninstallListeners() {
|
||||
super.uninstallListeners();
|
||||
scrollbar.removeAdjustmentListener(myAdjustmentListener);
|
||||
Disposer.dispose(myAnimator);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void paintTrack(Graphics g, JComponent c, Rectangle trackBounds) {
|
||||
g.setColor(TRACK_BACKGROUND);
|
||||
g.fillRect(trackBounds.x, trackBounds.y, trackBounds.width, trackBounds.height);
|
||||
|
||||
g.setColor(TRACK_BORDER);
|
||||
if (isVertical()) {
|
||||
g.drawLine(trackBounds.x, trackBounds.y, trackBounds.x, trackBounds.y + trackBounds.height);
|
||||
}
|
||||
else {
|
||||
g.drawLine(trackBounds.x, trackBounds.y, trackBounds.x + trackBounds.width, trackBounds.y);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
protected Dimension getMinimumThumbSize() {
|
||||
final int thickness = myIsMini ? 10 : 18;
|
||||
return isVertical() ? new Dimension(thickness, thickness * 2) : new Dimension(thickness * 2, thickness);
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
protected void paintThumb(Graphics g, JComponent c, Rectangle thumbBounds) {
|
||||
if (thumbBounds.isEmpty() || !scrollbar.isEnabled()) {
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
g.translate(thumbBounds.x, thumbBounds.y);
|
||||
|
||||
if (myIsMini) {
|
||||
paintMiniThumb(g, thumbBounds);
|
||||
}
|
||||
else {
|
||||
paintMaxiThumb(g, thumbBounds);
|
||||
}
|
||||
|
||||
g.translate(-thumbBounds.x, -thumbBounds.y);
|
||||
|
||||
}
|
||||
|
||||
private void paintMaxiThumb(Graphics g, Rectangle thumbBounds) {
|
||||
final int gap = 3;
|
||||
|
||||
int w = thumbBounds.width - gap * 2;
|
||||
int h = thumbBounds.height - gap * 2;
|
||||
|
||||
final GradientPaint paint;
|
||||
final Color start = adjustColor(GRADIENT_LIGHT);
|
||||
final Color end = adjustColor(GRADIENT_DARK);
|
||||
|
||||
if (isVertical()) {
|
||||
paint = new GradientPaint(1, 0, start, w + 1, 0, end);
|
||||
}
|
||||
else {
|
||||
paint = new GradientPaint(0, 1, start, 0, h + 1, end);
|
||||
}
|
||||
|
||||
((Graphics2D)g).setPaint(paint);
|
||||
g.fillRoundRect(gap, gap, w, h, 4, 4);
|
||||
|
||||
g.setColor(GRADIENT_THUMB_BORDER);
|
||||
g.drawRoundRect(gap, gap, w, h, 4, 4);
|
||||
}
|
||||
|
||||
private Color adjustColor(Color c) {
|
||||
if (myAnimationColorShift == 0) return c;
|
||||
return new SameColor(c.getRed() - myAnimationColorShift);
|
||||
}
|
||||
|
||||
private void paintMiniThumb(Graphics g, Rectangle thumbBounds) {
|
||||
final int gap = 2;
|
||||
|
||||
int w = thumbBounds.width - gap * 2;
|
||||
int h = thumbBounds.height - gap * 2;
|
||||
|
||||
g.setColor(adjustColor(PLAIN_THUMB_FILL));
|
||||
g.fillRect(gap, gap, w, h);
|
||||
|
||||
g.setColor(PLAIN_THUMB_BORDER);
|
||||
g.drawRect(gap, gap, w, h);
|
||||
}
|
||||
|
||||
|
||||
private boolean isVertical() {
|
||||
return scrollbar.getOrientation() == JScrollBar.VERTICAL;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected JButton createIncreaseButton(int orientation) {
|
||||
return new EmptyButton();
|
||||
}
|
||||
|
||||
@Override
|
||||
protected JButton createDecreaseButton(int orientation) {
|
||||
return new EmptyButton();
|
||||
}
|
||||
|
||||
private static class EmptyButton extends JButton {
|
||||
@Override
|
||||
public Dimension getMaximumSize() {
|
||||
return new Dimension(0, 0);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Dimension getPreferredSize() {
|
||||
return getMaximumSize();
|
||||
}
|
||||
|
||||
@Override
|
||||
public Dimension getMinimumSize() {
|
||||
return getMaximumSize();
|
||||
}
|
||||
}
|
||||
}
|
||||
+9
@@ -40,6 +40,7 @@ public class EditorSettingsExternalizable implements NamedJDOMExternalizable, Ex
|
||||
//Q: make it interface?
|
||||
public static class OptionSet implements Cloneable {
|
||||
public String LINE_SEPARATOR;
|
||||
public boolean USE_SOFT_WRAPS = false;
|
||||
public boolean IS_VIRTUAL_SPACE = true;
|
||||
public boolean IS_CARET_INSIDE_TABS;
|
||||
@NonNls public String STRIP_TRAILING_SPACES = "Changed";
|
||||
@@ -211,6 +212,14 @@ public class EditorSettingsExternalizable implements NamedJDOMExternalizable, Ex
|
||||
myOptions.SMART_HOME = val;
|
||||
}
|
||||
|
||||
public boolean isUseSoftWraps() {
|
||||
return myOptions.USE_SOFT_WRAPS;
|
||||
}
|
||||
|
||||
public void setUseSoftWraps(boolean use) {
|
||||
myOptions.USE_SOFT_WRAPS = use;
|
||||
}
|
||||
|
||||
public boolean isVirtualSpace() {
|
||||
return myOptions.IS_VIRTUAL_SPACE;
|
||||
}
|
||||
|
||||
@@ -30,7 +30,8 @@ public interface MarkupModelEx extends MarkupModel {
|
||||
|
||||
@Nullable
|
||||
HighlighterList getHighlighterList();
|
||||
|
||||
|
||||
@Nullable
|
||||
RangeHighlighter addPersistentLineHighlighter(int lineNumber, int layer, TextAttributes textAttributes);
|
||||
boolean containsHighlighter(RangeHighlighter highlighter);
|
||||
|
||||
|
||||
@@ -0,0 +1,71 @@
|
||||
/*
|
||||
* Copyright 2000-2010 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.ex;
|
||||
|
||||
import com.intellij.openapi.editor.LogicalPosition;
|
||||
import com.intellij.openapi.editor.SoftWrapModel;
|
||||
import com.intellij.openapi.editor.TextChange;
|
||||
import com.intellij.openapi.editor.VisualPosition;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* Extends {@link SoftWrapModel} in order to define implementation-specific contract.
|
||||
*
|
||||
* @author Denis Zhdanov
|
||||
* @since Jun 16, 2010 10:53:59 AM
|
||||
*/
|
||||
public interface SoftWrapModelEx extends SoftWrapModel {
|
||||
|
||||
/**
|
||||
* Asks current model to adjust logical position for the given visual position if necessary.
|
||||
* <p/>
|
||||
* Given logical position is allowed to be non-soft wrap aware, i.e. the one calculated as there are no soft wraps at the moment.
|
||||
*
|
||||
* @param defaultLogical default logical position that corresponds to the given visual position
|
||||
* @param visual target visual position for which logical position should be adjusted if necessary
|
||||
* @return logical position that corresponds to the given visual position
|
||||
*/
|
||||
@NotNull
|
||||
LogicalPosition adjustLogicalPosition(@NotNull LogicalPosition defaultLogical, @NotNull VisualPosition visual);
|
||||
|
||||
/**
|
||||
* Asks current model to adjust visual position that corresponds to the given logical position if necessary.
|
||||
* <p/>
|
||||
* Given visual position is assumed to be the one that is obtained during soft wraps unaware processing.
|
||||
*
|
||||
* @param logical target logical position for {@code 'logical' -> visual} conversion
|
||||
* @param defaultVisual visual position of {@code 'logical' -> visual} conversion that is unaware about soft wraps
|
||||
* @return resulting visual position for the given logical position
|
||||
*/
|
||||
@NotNull
|
||||
VisualPosition adjustVisualPosition(@NotNull LogicalPosition logical, @NotNull VisualPosition defaultVisual);
|
||||
|
||||
/**
|
||||
* Asks current model to calculate logical position for the given editor document offset.
|
||||
*
|
||||
* @param offset target editor document offset
|
||||
* @return logical position for the given editor document offset
|
||||
*/
|
||||
@NotNull
|
||||
LogicalPosition offsetToLogicalPosition(int offset);
|
||||
|
||||
/**
|
||||
* @return unmodifiable collection of soft wraps currently registered within the current model
|
||||
*/
|
||||
List<TextChange> getRegisteredSoftWraps();
|
||||
}
|
||||
@@ -26,7 +26,6 @@ import com.intellij.openapi.editor.ex.EditorEx;
|
||||
import com.intellij.openapi.editor.impl.ComplementaryFontsRegistry;
|
||||
import com.intellij.openapi.editor.impl.FontInfo;
|
||||
import com.intellij.openapi.editor.impl.IterationState;
|
||||
import com.intellij.openapi.project.Project;
|
||||
|
||||
import java.awt.*;
|
||||
|
||||
@@ -77,7 +76,7 @@ public class EditorUtil {
|
||||
|
||||
public static void fillVirtualSpaceUntil(final Editor editor, int columnNumber, int lineNumber) {
|
||||
final int offset = editor.logicalPositionToOffset(new LogicalPosition(lineNumber, columnNumber));
|
||||
final String filler = EditorModificationUtil.calcStringToFillVitualSpace(editor);
|
||||
final String filler = EditorModificationUtil.calcStringToFillVirtualSpace(editor);
|
||||
if (filler.length() > 0) {
|
||||
new WriteAction(){
|
||||
protected void run(final Result result) throws Throwable {
|
||||
|
||||
@@ -19,6 +19,13 @@ import com.intellij.openapi.diagnostic.Logger;
|
||||
|
||||
import java.lang.reflect.Array;
|
||||
|
||||
/**
|
||||
* This class is a data structure specialized for working with the indexed segments, i.e. it holds numerous mappings like
|
||||
* {@code 'index <-> (start; end)'} and provides convenient way for working with them, e.g. find index by particular offset that
|
||||
* belongs to target <code>(start; end)</code> segment etc.
|
||||
* <p/>
|
||||
* Not thread-safe.
|
||||
*/
|
||||
public class SegmentArray {
|
||||
private static final Logger LOG = Logger.getInstance("#com.intellij.openapi.editor.ex.util.SegmentArray");
|
||||
|
||||
@@ -101,7 +108,7 @@ public class SegmentArray {
|
||||
public final int findSegmentIndex(int offset) {
|
||||
if (mySegmentCount <= 0) {
|
||||
if (offset == 0) return 0;
|
||||
throw new IllegalStateException("no segments avaliable");
|
||||
throw new IllegalStateException("no segments available");
|
||||
}
|
||||
|
||||
final int lastValidOffset = getLastValidOffset();
|
||||
|
||||
+6
@@ -15,6 +15,12 @@
|
||||
*/
|
||||
package com.intellij.openapi.editor.ex.util;
|
||||
|
||||
/**
|
||||
* Expands {@link SegmentArray} contract in providing ability to attach additional <code>'short'</code> variable to target segment,
|
||||
* i.e. holds mappings like {@code 'index <-> (data, (start; end))'}.
|
||||
* <p/>
|
||||
* Not thread-safe.
|
||||
*/
|
||||
public class SegmentArrayWithData extends SegmentArray {
|
||||
private short[] myData;
|
||||
|
||||
|
||||
@@ -47,6 +47,7 @@ public class CaretModelImpl implements CaretModel, PrioritizedDocumentListener {
|
||||
private final EditorImpl myEditor;
|
||||
private final CopyOnWriteArrayList<CaretListener> myCaretListeners = ContainerUtil.createEmptyCOWList();
|
||||
private LogicalPosition myLogicalCaret;
|
||||
private VerticalInfo myCaretInfo;
|
||||
private VisualPosition myVisibleCaret;
|
||||
private int myOffset;
|
||||
private int myVisualLineStart;
|
||||
@@ -58,6 +59,7 @@ public class CaretModelImpl implements CaretModel, PrioritizedDocumentListener {
|
||||
myEditor = editor;
|
||||
myLogicalCaret = new LogicalPosition(0, 0);
|
||||
myVisibleCaret = new VisualPosition(0, 0);
|
||||
myCaretInfo = new VerticalInfo(0, 0);
|
||||
myOffset = 0;
|
||||
myVisualLineStart = 0;
|
||||
Document doc = editor.getDocument();
|
||||
@@ -97,26 +99,23 @@ public class CaretModelImpl implements CaretModel, PrioritizedDocumentListener {
|
||||
}
|
||||
}
|
||||
|
||||
int oldY = myEditor.visibleLineNumberToYPosition(myVisibleCaret.line);
|
||||
|
||||
myVisibleCaret = new VisualPosition(line, column);
|
||||
|
||||
VerticalInfo oldInfo = myCaretInfo;
|
||||
LogicalPosition oldPosition = myLogicalCaret;
|
||||
|
||||
myLogicalCaret = myEditor.visualToLogicalPosition(myVisibleCaret);
|
||||
setCurrentLogicalCaret(myEditor.visualToLogicalPosition(myVisibleCaret));
|
||||
myOffset = myEditor.logicalPositionToOffset(myLogicalCaret);
|
||||
LOG.assertTrue(myOffset >= 0 && myOffset <= myEditor.getDocument().getTextLength());
|
||||
|
||||
myVisualLineStart =
|
||||
myEditor.logicalPositionToOffset(myEditor.visualToLogicalPosition(new VisualPosition(myVisibleCaret.line, 0)));
|
||||
myVisualLineEnd =
|
||||
myEditor.logicalPositionToOffset(myEditor.visualToLogicalPosition(new VisualPosition(myVisibleCaret.line + 1, 0)));
|
||||
myVisualLineStart = myEditor.logicalPositionToOffset(myEditor.visualToLogicalPosition(new VisualPosition(myVisibleCaret.line, 0)));
|
||||
myVisualLineEnd = myEditor.logicalPositionToOffset(myEditor.visualToLogicalPosition(new VisualPosition(myVisibleCaret.line + 1, 0)));
|
||||
|
||||
((FoldingModelImpl)myEditor.getFoldingModel()).flushCaretPosition();
|
||||
|
||||
myEditor.setLastColumnNumber(myVisibleCaret.column);
|
||||
myEditor.updateCaretCursor();
|
||||
requestRepaint(oldY);
|
||||
requestRepaint(oldInfo);
|
||||
|
||||
if (oldPosition.column != myLogicalCaret.column || oldPosition.line != myLogicalCaret.line) {
|
||||
CaretEvent event = new CaretEvent(myEditor, oldPosition, myLogicalCaret);
|
||||
@@ -144,7 +143,8 @@ public class CaretModelImpl implements CaretModel, PrioritizedDocumentListener {
|
||||
int lineShift,
|
||||
boolean withSelection,
|
||||
boolean blockSelection,
|
||||
boolean scrollToCaret) {
|
||||
boolean scrollToCaret)
|
||||
{
|
||||
assertIsDispatchThread();
|
||||
SelectionModel selectionModel = myEditor.getSelectionModel();
|
||||
int selectionStart = selectionModel.getLeadSelectionOffset();
|
||||
@@ -227,11 +227,21 @@ public class CaretModelImpl implements CaretModel, PrioritizedDocumentListener {
|
||||
validateCallContext();
|
||||
int column = pos.column;
|
||||
int line = pos.line;
|
||||
int softWrapLines = pos.softWrapLines;
|
||||
int linesFromCurrentSoftWrap = pos.linesFromActiveSoftWrap;
|
||||
int softWrapColumns = pos.softWrapColumnDiff;
|
||||
|
||||
Document doc = myEditor.getDocument();
|
||||
|
||||
if (column < 0) column = 0;
|
||||
if (line < 0) line = 0;
|
||||
if (column < 0) {
|
||||
column = 0;
|
||||
softWrapColumns = 0;
|
||||
}
|
||||
if (line < 0) {
|
||||
line = 0;
|
||||
softWrapLines = 0;
|
||||
linesFromCurrentSoftWrap = 0;
|
||||
}
|
||||
|
||||
int lineCount = doc.getLineCount();
|
||||
if (lineCount == 0) {
|
||||
@@ -239,6 +249,8 @@ public class CaretModelImpl implements CaretModel, PrioritizedDocumentListener {
|
||||
}
|
||||
else if (line > lineCount - 1) {
|
||||
line = lineCount - 1;
|
||||
softWrapLines = 0;
|
||||
linesFromCurrentSoftWrap = 0;
|
||||
}
|
||||
|
||||
EditorSettings editorSettings = myEditor.getSettings();
|
||||
@@ -248,16 +260,20 @@ public class CaretModelImpl implements CaretModel, PrioritizedDocumentListener {
|
||||
int lineEndColumnNumber = myEditor.offsetToLogicalPosition(lineEndOffset).column;
|
||||
if (column > lineEndColumnNumber) {
|
||||
column = lineEndColumnNumber;
|
||||
if (softWrapColumns != 0) {
|
||||
softWrapColumns -= column - lineEndColumnNumber;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
((FoldingModelImpl)myEditor.getFoldingModel()).flushCaretPosition();
|
||||
|
||||
int oldY = myEditor.visibleLineNumberToYPosition(myVisibleCaret.line);
|
||||
|
||||
VerticalInfo oldInfo = myCaretInfo;
|
||||
LogicalPosition oldCaretPosition = myLogicalCaret;
|
||||
|
||||
myLogicalCaret = new LogicalPosition(line, column);
|
||||
setCurrentLogicalCaret(new LogicalPosition(
|
||||
line, column, softWrapLines, linesFromCurrentSoftWrap, softWrapColumns, pos.foldedLines, pos.foldingColumnDiff
|
||||
));
|
||||
|
||||
final int offset = myEditor.logicalPositionToOffset(myLogicalCaret);
|
||||
|
||||
@@ -282,13 +298,17 @@ public class CaretModelImpl implements CaretModel, PrioritizedDocumentListener {
|
||||
myOffset = myEditor.logicalPositionToOffset(myLogicalCaret);
|
||||
LOG.assertTrue(myOffset >= 0 && myOffset <= myEditor.getDocument().getTextLength());
|
||||
|
||||
myVisualLineStart = myEditor.logicalPositionToOffset(myEditor.visualToLogicalPosition(new VisualPosition(myVisibleCaret.line, 0)));
|
||||
myVisualLineEnd = myEditor.logicalPositionToOffset(myEditor.visualToLogicalPosition(new VisualPosition(myVisibleCaret.line + 1, 0)));
|
||||
int caretOffset = myEditor.logicalPositionToOffset(myEditor.visualToLogicalPosition(new VisualPosition(myVisibleCaret.line, 0)));
|
||||
int caretLine = doc.getLineNumber(caretOffset);
|
||||
myVisualLineStart = doc.getLineStartOffset(caretLine);
|
||||
myVisualLineEnd = doc.getLineEndOffset(caretLine) + 1;
|
||||
|
||||
myEditor.updateCaretCursor();
|
||||
requestRepaint(oldY);
|
||||
requestRepaint(oldInfo);
|
||||
|
||||
if (oldCaretPosition.column != myLogicalCaret.column || oldCaretPosition.line != myLogicalCaret.line) {
|
||||
if (oldCaretPosition.column + oldCaretPosition.softWrapColumnDiff != myLogicalCaret.column + myLogicalCaret.softWrapColumnDiff
|
||||
|| oldCaretPosition.line + oldCaretPosition.softWrapLines != myLogicalCaret.line + myLogicalCaret.softWrapLines)
|
||||
{
|
||||
CaretEvent event = new CaretEvent(myEditor, oldCaretPosition, myLogicalCaret);
|
||||
for (CaretListener listener : myCaretListeners) {
|
||||
listener.caretPositionChanged(event);
|
||||
@@ -296,27 +316,25 @@ public class CaretModelImpl implements CaretModel, PrioritizedDocumentListener {
|
||||
}
|
||||
}
|
||||
|
||||
private void requestRepaint(int oldY) {
|
||||
int newY = myEditor.visibleLineNumberToYPosition(myVisibleCaret.line);
|
||||
private void requestRepaint(VerticalInfo oldCaretInfo) {
|
||||
int lineHeight = myEditor.getLineHeight();
|
||||
Rectangle visibleRect = myEditor.getScrollingModel().getVisibleArea();
|
||||
Rectangle visibleArea = myEditor.getScrollingModel().getVisibleArea();
|
||||
final EditorGutterComponentEx gutter = myEditor.getGutterComponentEx();
|
||||
final EditorComponentImpl content = (EditorComponentImpl)myEditor.getContentComponent();
|
||||
|
||||
int updateWidth = myEditor.getScrollPane().getHorizontalScrollBar().getValue() + visibleRect.width;
|
||||
if (Math.abs(newY - oldY) <= 2 * lineHeight) {
|
||||
int minY = Math.min(oldY, newY);
|
||||
int maxY = Math.max(oldY + lineHeight, newY + lineHeight);
|
||||
int updateWidth = myEditor.getScrollPane().getHorizontalScrollBar().getValue() + visibleArea.width;
|
||||
if (Math.abs(myCaretInfo.y - oldCaretInfo.y) <= 2 * lineHeight) {
|
||||
int minY = Math.min(oldCaretInfo.y, myCaretInfo.y);
|
||||
int maxY = Math.max(oldCaretInfo.y + oldCaretInfo.height, myCaretInfo.y + myCaretInfo.height);
|
||||
content.repaintEditorComponent(0, minY, updateWidth, maxY - minY);
|
||||
gutter.repaint(0, minY, gutter.getWidth(), maxY - minY);
|
||||
}
|
||||
else {
|
||||
content.repaintEditorComponent(0, oldY, updateWidth, 2 * lineHeight);
|
||||
gutter.repaint(0, oldY, updateWidth, 2 * lineHeight);
|
||||
content.repaintEditorComponent(0, newY, updateWidth, 2 * lineHeight);
|
||||
gutter.repaint(0, newY, updateWidth, 2 * lineHeight);
|
||||
content.repaintEditorComponent(0, oldCaretInfo.y, updateWidth, oldCaretInfo.height + lineHeight);
|
||||
gutter.repaint(0, oldCaretInfo.y, updateWidth, oldCaretInfo.height + lineHeight);
|
||||
content.repaintEditorComponent(0, myCaretInfo.y, updateWidth, myCaretInfo.height + lineHeight);
|
||||
gutter.repaint(0, myCaretInfo.y, updateWidth, myCaretInfo.height + lineHeight);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
public LogicalPosition getLogicalPosition() {
|
||||
@@ -338,6 +356,18 @@ public class CaretModelImpl implements CaretModel, PrioritizedDocumentListener {
|
||||
return myOffset;
|
||||
}
|
||||
|
||||
/**
|
||||
* There is a possible case that single logical line is spread to more than one visual lines because of soft wraps. This method
|
||||
* allows to receive information about vertical range occupied by the active logical line, i.e. it identifies
|
||||
* <code>'y'</code> coordinate of the first visual line that corresponds to the logical line and total height
|
||||
* of all visual lines that correspond to the active logical line.
|
||||
*
|
||||
* @return object that encapsulates information about visual vertical range occupied by the current logical line on a screen
|
||||
*/
|
||||
public VerticalInfo getVisualCaretInfo() {
|
||||
return myCaretInfo;
|
||||
}
|
||||
|
||||
public int getVisualLineStart() {
|
||||
return myVisualLineStart;
|
||||
}
|
||||
@@ -391,7 +421,7 @@ public class CaretModelImpl implements CaretModel, PrioritizedDocumentListener {
|
||||
|
||||
int newOffset = myOffset;
|
||||
|
||||
if (myOffset > oldEndOffset || myOffset == oldEndOffset && needToShiftWhitespaces(e)) {
|
||||
if (myOffset > oldEndOffset || myOffset == oldEndOffset && needToShiftWhiteSpaces(e)) {
|
||||
newOffset += e.getNewLength() - e.getOldLength();
|
||||
}
|
||||
else if (myOffset >= startOffset && myOffset <= oldEndOffset) {
|
||||
@@ -413,7 +443,7 @@ public class CaretModelImpl implements CaretModel, PrioritizedDocumentListener {
|
||||
myVisualLineEnd = myEditor.logicalPositionToOffset(myEditor.visualToLogicalPosition(new VisualPosition(myVisibleCaret.line + 1, 0)));
|
||||
}
|
||||
|
||||
private boolean needToShiftWhitespaces(final DocumentEvent e) {
|
||||
private boolean needToShiftWhiteSpaces(final DocumentEvent e) {
|
||||
if(!CharArrayUtil.containsOnlyWhiteSpaces(e.getNewFragment()) || CharArrayUtil.containLineBreaks(e.getNewFragment()))
|
||||
return e.getOldLength() > 0;
|
||||
if(e.getOffset() == 0) return false;
|
||||
@@ -429,4 +459,45 @@ public class CaretModelImpl implements CaretModel, PrioritizedDocumentListener {
|
||||
public int getPriority() {
|
||||
return 3;
|
||||
}
|
||||
|
||||
private void setCurrentLogicalCaret(LogicalPosition position) {
|
||||
myLogicalCaret = position;
|
||||
myCaretInfo = createVerticalInfo(position);
|
||||
}
|
||||
|
||||
private VerticalInfo createVerticalInfo(LogicalPosition position) {
|
||||
Document document = myEditor.getDocument();
|
||||
int line = position.line;
|
||||
|
||||
// There is a possible case that active logical line is represented on multiple lines due to soft wraps processing.
|
||||
// We want to highlight those visual lines as 'active' then, so, we calculate 'y' position for the logical line start
|
||||
// and height in accordance with the number of occupied visual lines.
|
||||
int y = myEditor.logicalPositionToXY(myEditor.offsetToLogicalPosition(document.getLineStartOffset(line))).y;
|
||||
int height = myEditor.getLineHeight();
|
||||
if (line < document.getLineCount() - 1) {
|
||||
int nextLineY = myEditor.logicalPositionToXY(myEditor.offsetToLogicalPosition(document.getLineStartOffset(line + 1))).y;
|
||||
int heightCandidate = nextLineY - y;
|
||||
|
||||
// There is a possible case that active line is the one that ends with folding, so, 'y' position
|
||||
// of its next logical line is the same as the previous. We explicitly check that in order to use non-standard
|
||||
// line height only in case of visible soft-wrapped line.
|
||||
if (heightCandidate > height) {
|
||||
height = heightCandidate;
|
||||
}
|
||||
}
|
||||
return new VerticalInfo(y, height);
|
||||
}
|
||||
|
||||
/**
|
||||
* Encapsulates information about target vertical range info - its <code>'y'</code> coordinate and height in pixels.
|
||||
*/
|
||||
public static class VerticalInfo {
|
||||
public final int y;
|
||||
public final int height;
|
||||
|
||||
private VerticalInfo(int y, int height) {
|
||||
this.y = y;
|
||||
this.height = height;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
+53
-48
@@ -85,7 +85,7 @@ class EditorGutterComponentImpl extends EditorGutterComponentEx implements Mouse
|
||||
private static final int GAP_BETWEEN_ANNOTATIONS = 6;
|
||||
private Color myBackgroundColor = null;
|
||||
private GutterDraggableObject myGutterDraggableObject;
|
||||
private String myLastGutterTooltip = null;
|
||||
private String myLastGutterToolTip = null;
|
||||
|
||||
|
||||
public EditorGutterComponentImpl(EditorImpl editor) {
|
||||
@@ -121,7 +121,7 @@ class EditorGutterComponentImpl extends EditorGutterComponentEx implements Mouse
|
||||
myBackgroundColor = null;
|
||||
repaint();
|
||||
}
|
||||
|
||||
|
||||
public void paint(Graphics g) {
|
||||
((ApplicationImpl)ApplicationManager.getApplication()).editorPaintStart();
|
||||
|
||||
@@ -143,7 +143,7 @@ class EditorGutterComponentImpl extends EditorGutterComponentEx implements Mouse
|
||||
paintLineNumbers(g, clip);
|
||||
paintAnnotations(g, clip);
|
||||
|
||||
Object antialiasing = g2.getRenderingHint(RenderingHints.KEY_ANTIALIASING);
|
||||
Object hint = g2.getRenderingHint(RenderingHints.KEY_ANTIALIASING);
|
||||
g2.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_OFF);
|
||||
|
||||
try {
|
||||
@@ -152,7 +152,7 @@ class EditorGutterComponentImpl extends EditorGutterComponentEx implements Mouse
|
||||
paintFoldingTree(g, clip);
|
||||
}
|
||||
finally {
|
||||
g2.setRenderingHint(RenderingHints.KEY_ANTIALIASING, antialiasing);
|
||||
g2.setRenderingHint(RenderingHints.KEY_ANTIALIASING, hint);
|
||||
}
|
||||
|
||||
g2.setTransform(old);
|
||||
@@ -258,12 +258,11 @@ class EditorGutterComponentImpl extends EditorGutterComponentEx implements Mouse
|
||||
}
|
||||
|
||||
private void paintCaretRowBackground(final Graphics g, final int x, final int width) {
|
||||
final VisualPosition visCaret = myEditor.getCaretModel().getVisualPosition();
|
||||
CaretModelImpl.VerticalInfo caretInfo = myEditor.getCaretModel().getVisualCaretInfo();
|
||||
Color caretRowColor = myEditor.getColorsScheme().getColor(EditorColors.CARET_ROW_COLOR);
|
||||
if (caretRowColor != null) {
|
||||
g.setColor(caretRowColor);
|
||||
final Point caretPoint = myEditor.visualPositionToXY(visCaret);
|
||||
g.fillRect(x, caretPoint.y, width, myEditor.getLineHeight());
|
||||
g.fillRect(x, caretInfo.y, width, caretInfo.height);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -327,7 +326,11 @@ class EditorGutterComponentImpl extends EditorGutterComponentEx implements Mouse
|
||||
}
|
||||
|
||||
for (int i = startLineNumber; i < endLineNumber; i++) {
|
||||
int logLine = myEditor.visualToLogicalPosition(new VisualPosition(i, 0)).line;
|
||||
LogicalPosition logicalPosition = myEditor.visualToLogicalPosition(new VisualPosition(i, 0));
|
||||
if (logicalPosition.isOnSoftWrappedLine()) {
|
||||
continue;
|
||||
}
|
||||
int logLine = logicalPosition.line;
|
||||
String s = String.valueOf(logLine + 1);
|
||||
g.drawString(s,
|
||||
getLineNumberAreaOffset() + getLineNumberAreaWidth() -
|
||||
@@ -504,7 +507,7 @@ class EditorGutterComponentImpl extends EditorGutterComponentEx implements Mouse
|
||||
|
||||
Graphics2D g2 = (Graphics2D)g;
|
||||
|
||||
Object antialiasing = g2.getRenderingHint(RenderingHints.KEY_ANTIALIASING);
|
||||
Object hint = g2.getRenderingHint(RenderingHints.KEY_ANTIALIASING);
|
||||
g2.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON);
|
||||
try {
|
||||
processRangeHighlighters(new RangeHighlighterProcessor() {
|
||||
@@ -514,7 +517,7 @@ class EditorGutterComponentImpl extends EditorGutterComponentEx implements Mouse
|
||||
}, firstVisibleOffset, lastVisibleOffset);
|
||||
}
|
||||
finally {
|
||||
g2.setRenderingHint(RenderingHints.KEY_ANTIALIASING, antialiasing);
|
||||
g2.setRenderingHint(RenderingHints.KEY_ANTIALIASING, hint);
|
||||
}
|
||||
|
||||
int firstVisibleLine = myEditor.getDocument().getLineNumber(firstVisibleOffset);
|
||||
@@ -549,16 +552,17 @@ class EditorGutterComponentImpl extends EditorGutterComponentEx implements Mouse
|
||||
}
|
||||
|
||||
private void paintLineMarkerRenderer(RangeHighlighter highlighter, Graphics g) {
|
||||
Rectangle rect = getLineRendererRect(highlighter);
|
||||
Rectangle rectangle = getLineRendererRectangle(highlighter);
|
||||
|
||||
if (rect != null) {
|
||||
if (rectangle != null) {
|
||||
final LineMarkerRenderer lineMarkerRenderer = highlighter.getLineMarkerRenderer();
|
||||
assert lineMarkerRenderer != null;
|
||||
lineMarkerRenderer.paint(myEditor, g, rect);
|
||||
lineMarkerRenderer.paint(myEditor, g, rectangle);
|
||||
}
|
||||
}
|
||||
|
||||
private Rectangle getLineRendererRect(RangeHighlighter highlighter) {
|
||||
@Nullable
|
||||
private Rectangle getLineRendererRectangle(RangeHighlighter highlighter) {
|
||||
LineMarkerRenderer renderer = highlighter.getLineMarkerRenderer();
|
||||
if (renderer == null) return null;
|
||||
|
||||
@@ -694,10 +698,10 @@ class EditorGutterComponentImpl extends EditorGutterComponentEx implements Mouse
|
||||
|
||||
paintCaretRowBackground(g, lineX, getFoldingAnchorWidth());
|
||||
|
||||
paintFoldingBoxBacgrounds((Graphics2D)g);
|
||||
paintFoldingBoxBackground((Graphics2D)g);
|
||||
}
|
||||
|
||||
private void paintFoldingBoxBacgrounds(Graphics2D g) {
|
||||
private void paintFoldingBoxBackground(Graphics2D g) {
|
||||
if (!isFoldingOutlineShown()) return;
|
||||
Rectangle clip = g.getClipBounds();
|
||||
|
||||
@@ -965,14 +969,14 @@ class EditorGutterComponentImpl extends EditorGutterComponentEx implements Mouse
|
||||
continue;
|
||||
}
|
||||
|
||||
if (rectByFoldOffset(foldStart, anchorWidth, anchorX).contains(x, y)) return foldRange;
|
||||
if ((group == null || foldRange.isExpanded()) && rectByFoldOffset(foldEnd, anchorWidth, anchorX).contains(x, y)) return foldRange;
|
||||
if (rectangleByFoldOffset(foldStart, anchorWidth, anchorX).contains(x, y)) return foldRange;
|
||||
if ((group == null || foldRange.isExpanded()) && rectangleByFoldOffset(foldEnd, anchorWidth, anchorX).contains(x, y)) return foldRange;
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
private Rectangle rectByFoldOffset(VisualPosition foldStart, int anchorWidth, int anchorX) {
|
||||
private Rectangle rectangleByFoldOffset(VisualPosition foldStart, int anchorWidth, int anchorX) {
|
||||
int anchorY = myEditor.visibleLineNumberToYPosition(foldStart.line) + myEditor.getLineHeight() -
|
||||
myEditor.getDescent() - anchorWidth;
|
||||
return new Rectangle(anchorX, anchorY, anchorWidth, anchorWidth);
|
||||
@@ -983,11 +987,11 @@ class EditorGutterComponentImpl extends EditorGutterComponentEx implements Mouse
|
||||
}
|
||||
|
||||
public void mouseMoved(final MouseEvent e) {
|
||||
String tooltip = null;
|
||||
String toolTip = null;
|
||||
GutterIconRenderer renderer = getGutterRenderer(e);
|
||||
TooltipController controller = TooltipController.getInstance();
|
||||
if (renderer != null) {
|
||||
tooltip = renderer.getTooltipText();
|
||||
toolTip = renderer.getTooltipText();
|
||||
if (renderer.isNavigateAction()) {
|
||||
setCursor(Cursor.getPredefinedCursor(Cursor.HAND_CURSOR));
|
||||
}
|
||||
@@ -1002,10 +1006,10 @@ class EditorGutterComponentImpl extends EditorGutterComponentEx implements Mouse
|
||||
TextAnnotationGutterProvider provider = getProviderAtPoint(e.getPoint());
|
||||
if (provider != null) {
|
||||
final int line = getLineNumAtPoint(e.getPoint());
|
||||
tooltip = provider.getToolTip(line, myEditor);
|
||||
if (!Comparing.equal(tooltip, myLastGutterTooltip)) {
|
||||
toolTip = provider.getToolTip(line, myEditor);
|
||||
if (!Comparing.equal(toolTip, myLastGutterToolTip)) {
|
||||
controller.cancelTooltip(GUTTER_TOOLTIP_GROUP);
|
||||
myLastGutterTooltip = tooltip;
|
||||
myLastGutterToolTip = toolTip;
|
||||
}
|
||||
if (myProviderToListener.containsKey(provider)) {
|
||||
final EditorGutterAction action = myProviderToListener.get(provider);
|
||||
@@ -1017,8 +1021,8 @@ class EditorGutterComponentImpl extends EditorGutterComponentEx implements Mouse
|
||||
}
|
||||
}
|
||||
|
||||
if (tooltip != null && tooltip.length() != 0) {
|
||||
controller.showTooltipByMouseMove(myEditor, e, ((EditorMarkupModel)myEditor.getMarkupModel()).getErrorStripTooltipRendererProvider().calcTooltipRenderer(tooltip), false, GUTTER_TOOLTIP_GROUP);
|
||||
if (toolTip != null && toolTip.length() != 0) {
|
||||
controller.showTooltipByMouseMove(myEditor, e, ((EditorMarkupModel)myEditor.getMarkupModel()).getErrorStripTooltipRendererProvider().calcTooltipRenderer(toolTip), false, GUTTER_TOOLTIP_GROUP);
|
||||
}
|
||||
else {
|
||||
controller.cancelTooltip(GUTTER_TOOLTIP_GROUP);
|
||||
@@ -1056,6 +1060,7 @@ class EditorGutterComponentImpl extends EditorGutterComponentEx implements Mouse
|
||||
return myEditor.xyToLogicalPosition(new Point(0, clickPoint.y)).line;
|
||||
}
|
||||
|
||||
@Nullable
|
||||
private TextAnnotationGutterProvider getProviderAtPoint(final Point clickPoint) {
|
||||
int current = getAnnotationsAreaOffset();
|
||||
if (clickPoint.x < current) return null;
|
||||
@@ -1126,11 +1131,11 @@ class EditorGutterComponentImpl extends EditorGutterComponentEx implements Mouse
|
||||
processRangeHighlighters(new RangeHighlighterProcessor() {
|
||||
public void process(RangeHighlighter highlighter) {
|
||||
if (gutterRenderer[0] != null) return;
|
||||
Rectangle rect = getLineRendererRect(highlighter);
|
||||
if (rect == null) return;
|
||||
Rectangle rectangle = getLineRendererRectangle(highlighter);
|
||||
if (rectangle == null) return;
|
||||
|
||||
int startY = rect.y;
|
||||
int endY = startY + rect.height;
|
||||
int startY = rectangle.y;
|
||||
int endY = startY + rectangle.height;
|
||||
if (startY == endY) {
|
||||
endY += myEditor.getLineHeight();
|
||||
}
|
||||
@@ -1315,7 +1320,7 @@ class EditorGutterComponentImpl extends EditorGutterComponentEx implements Mouse
|
||||
}
|
||||
}, dragSourceListener);
|
||||
}
|
||||
catch (InvalidDnDOperationException idoe) {
|
||||
catch (InvalidDnDOperationException e) {
|
||||
// OK, can't dnd
|
||||
}
|
||||
}
|
||||
@@ -1325,21 +1330,21 @@ class EditorGutterComponentImpl extends EditorGutterComponentEx implements Mouse
|
||||
}
|
||||
|
||||
private class MyDragSourceListener extends DragSourceAdapter {
|
||||
public void dragEnter(DragSourceDragEvent dsde) {
|
||||
updateCursor(dsde);
|
||||
public void dragEnter(DragSourceDragEvent e) {
|
||||
updateCursor(e);
|
||||
}
|
||||
|
||||
public void dragOver(DragSourceDragEvent dsde) {
|
||||
updateCursor(dsde);
|
||||
public void dragOver(DragSourceDragEvent e) {
|
||||
updateCursor(e);
|
||||
}
|
||||
|
||||
public void dropActionChanged(DragSourceDragEvent dsde) {
|
||||
dsde.getDragSourceContext().setCursor(null);//setCursor (dsde.getDragSourceContext());
|
||||
public void dropActionChanged(DragSourceDragEvent e) {
|
||||
e.getDragSourceContext().setCursor(null);//setCursor (e.getDragSourceContext());
|
||||
}
|
||||
|
||||
private void updateCursor(final DragSourceDragEvent dsde) {
|
||||
final DragSourceContext context = dsde.getDragSourceContext();
|
||||
final Point screenPoint = dsde.getLocation();
|
||||
private void updateCursor(final DragSourceDragEvent e) {
|
||||
final DragSourceContext context = e.getDragSourceContext();
|
||||
final Point screenPoint = e.getLocation();
|
||||
if (screenPoint != null) {
|
||||
final Point gutterPoint = new Point(screenPoint);
|
||||
SwingUtilities.convertPointFromScreen(gutterPoint, EditorGutterComponentImpl.this);
|
||||
@@ -1355,26 +1360,26 @@ class EditorGutterComponentImpl extends EditorGutterComponentEx implements Mouse
|
||||
context.setCursor(null);
|
||||
}
|
||||
|
||||
public void dragDropEnd(DragSourceDropEvent dsde) {
|
||||
if (!dsde.getDropSuccess()) return;
|
||||
public void dragDropEnd(DragSourceDropEvent e) {
|
||||
if (!e.getDropSuccess()) return;
|
||||
|
||||
if (dsde.getDropAction() == DnDConstants.ACTION_MOVE) {
|
||||
if (e.getDropAction() == DnDConstants.ACTION_MOVE) {
|
||||
myGutterDraggableObject.removeSelf();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private class MyDropTargetListener extends DropTargetAdapter {
|
||||
public void drop(DropTargetDropEvent dtde) {
|
||||
public void drop(DropTargetDropEvent e) {
|
||||
if (myGutterDraggableObject != null) {
|
||||
int dropAction = dtde.getDropAction();
|
||||
int dropAction = e.getDropAction();
|
||||
if ((dropAction & DnDConstants.ACTION_MOVE) != 0) {
|
||||
int line = myEditor.xyToLogicalPosition(new Point(0, (int)dtde.getLocation().getY())).line;
|
||||
dtde.dropComplete(myGutterDraggableObject.copy(line));
|
||||
int line = myEditor.xyToLogicalPosition(new Point(0, (int)e.getLocation().getY())).line;
|
||||
e.dropComplete(myGutterDraggableObject.copy(line));
|
||||
return;
|
||||
}
|
||||
}
|
||||
dtde.rejectDrop();
|
||||
e.rejectDrop();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -49,6 +49,7 @@ import com.intellij.openapi.editor.highlighter.EditorHighlighter;
|
||||
import com.intellij.openapi.editor.highlighter.HighlighterClient;
|
||||
import com.intellij.openapi.editor.impl.event.MarkupModelEvent;
|
||||
import com.intellij.openapi.editor.impl.event.MarkupModelListener;
|
||||
import com.intellij.openapi.editor.TextChange;
|
||||
import com.intellij.openapi.editor.markup.*;
|
||||
import com.intellij.openapi.fileEditor.FileDocumentManager;
|
||||
import com.intellij.openapi.progress.ProgressManager;
|
||||
@@ -64,10 +65,11 @@ import com.intellij.util.Alarm;
|
||||
import com.intellij.util.IJSwingUtilities;
|
||||
import com.intellij.util.containers.ContainerUtil;
|
||||
import com.intellij.util.containers.HashMap;
|
||||
import com.intellij.util.ui.ButtonlessScrollBarUI;
|
||||
import com.intellij.util.ui.EmptyClipboardOwner;
|
||||
import com.intellij.util.ui.UIUtil;
|
||||
import com.intellij.util.ui.update.UiNotifyConnector;
|
||||
import gnu.trove.TIntArrayList;
|
||||
import gnu.trove.*;
|
||||
import org.jdom.Element;
|
||||
import org.jetbrains.annotations.NonNls;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
@@ -97,6 +99,7 @@ import java.text.AttributedCharacterIterator;
|
||||
import java.text.AttributedString;
|
||||
import java.text.CharacterIterator;
|
||||
import java.util.*;
|
||||
import java.util.List;
|
||||
import java.util.concurrent.CopyOnWriteArrayList;
|
||||
import java.util.concurrent.ScheduledFuture;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
@@ -156,6 +159,7 @@ public final class EditorImpl extends UserDataHolderBase implements EditorEx, Hi
|
||||
private final FoldingModelImpl myFoldingModel;
|
||||
private final ScrollingModelImpl myScrollingModel;
|
||||
private final CaretModelImpl myCaretModel;
|
||||
private final SoftWrapModelImpl mySoftWrapModel;
|
||||
|
||||
private static final RepaintCursorCommand ourCaretBlinkingCommand;
|
||||
|
||||
@@ -169,10 +173,12 @@ public final class EditorImpl extends UserDataHolderBase implements EditorEx, Hi
|
||||
private static final int MOUSE_SELECTION_STATE_LINE_SELECTED = 2;
|
||||
|
||||
private final MarkupModelListener myMarkupModelListener;
|
||||
private final List<TextChange> mySoftWrapsOnLastRepaint = new ArrayList<TextChange>();
|
||||
private boolean myUnderRepainting;
|
||||
|
||||
private EditorHighlighter myHighlighter;
|
||||
|
||||
private int myScrollbarOrientation;
|
||||
private int myScrollBarOrientation;
|
||||
private boolean myMousePressedInsideSelection;
|
||||
private FontMetrics myPlainFontMetrics;
|
||||
private FontMetrics myBoldFontMetrics;
|
||||
@@ -242,6 +248,7 @@ public final class EditorImpl extends UserDataHolderBase implements EditorEx, Hi
|
||||
myMarkupModel = new EditorMarkupModelImpl(this);
|
||||
myFoldingModel = new FoldingModelImpl(this);
|
||||
myCaretModel = new CaretModelImpl(this);
|
||||
mySoftWrapModel = new SoftWrapModelImpl(this);
|
||||
mySizeContainer.reset();
|
||||
|
||||
myCommandProcessor = CommandProcessor.getInstance();
|
||||
@@ -315,7 +322,7 @@ public final class EditorImpl extends UserDataHolderBase implements EditorEx, Hi
|
||||
myCaretCursor = new CaretCursor();
|
||||
|
||||
myFoldingModel.flushCaretShift();
|
||||
myScrollbarOrientation = VERTICAL_SCROLLBAR_RIGHT;
|
||||
myScrollBarOrientation = VERTICAL_SCROLLBAR_RIGHT;
|
||||
|
||||
EditorHighlighter highlighter = new EmptyEditorHighlighter(myScheme.getAttributes(HighlighterColors.TEXT));
|
||||
setHighlighter(highlighter);
|
||||
@@ -420,7 +427,7 @@ public final class EditorImpl extends UserDataHolderBase implements EditorEx, Hi
|
||||
}
|
||||
|
||||
@NotNull
|
||||
public CaretModel getCaretModel() {
|
||||
public CaretModelImpl getCaretModel() {
|
||||
return myCaretModel;
|
||||
}
|
||||
|
||||
@@ -429,6 +436,11 @@ public final class EditorImpl extends UserDataHolderBase implements EditorEx, Hi
|
||||
return myScrollingModel;
|
||||
}
|
||||
|
||||
@NotNull
|
||||
public SoftWrapModelImpl getSoftWrapModel() {
|
||||
return mySoftWrapModel;
|
||||
}
|
||||
|
||||
@NotNull
|
||||
public EditorSettings getSettings() {
|
||||
assertReadAccess();
|
||||
@@ -496,7 +508,7 @@ public final class EditorImpl extends UserDataHolderBase implements EditorEx, Hi
|
||||
myScrollingModel.dispose();
|
||||
myGutterComponent.dispose();
|
||||
clearCaretThread();
|
||||
//myFoldingModel.dispose(); TODO rangemarker tree
|
||||
//myFoldingModel.dispose(); TODO range marker tree
|
||||
}
|
||||
|
||||
private void clearCaretThread() {
|
||||
@@ -610,11 +622,11 @@ public final class EditorImpl extends UserDataHolderBase implements EditorEx, Hi
|
||||
final DropTarget dropTarget = myEditorComponent.getDropTarget();
|
||||
if (dropTarget != null) { // might be null in headless environment
|
||||
dropTarget.addDropTargetListener(new DropTargetAdapter() {
|
||||
public void drop(DropTargetDropEvent dtde) {
|
||||
public void drop(DropTargetDropEvent e) {
|
||||
}
|
||||
|
||||
public void dragOver(DropTargetDragEvent dtde) {
|
||||
Point location = dtde.getLocation();
|
||||
public void dragOver(DropTargetDragEvent e) {
|
||||
Point location = e.getLocation();
|
||||
|
||||
moveCaretToScreenPos(location.x, location.y);
|
||||
getScrollingModel().scrollToCaret(ScrollType.RELATIVE);
|
||||
@@ -781,11 +793,19 @@ public final class EditorImpl extends UserDataHolderBase implements EditorEx, Hi
|
||||
}
|
||||
}
|
||||
|
||||
int offset = logicalPositionToOffset(visualToLogicalPosition(new VisualPosition(line, 0)));
|
||||
LogicalPosition logicalPosition = visualToLogicalPosition(new VisualPosition(line, 0));
|
||||
int offset = logicalPositionToOffset(logicalPosition);
|
||||
int textLength = myDocument.getTextLength();
|
||||
|
||||
if (offset >= textLength) return new VisualPosition(line, 0);
|
||||
|
||||
// There is a possible case that starting logical line is split by soft-wraps and it's part after the split should be drawn.
|
||||
// We mark that we're under such circumstances then.
|
||||
boolean activeSoftWrapProcessed = true;
|
||||
if (logicalPosition.isOnSoftWrappedLine()) {
|
||||
activeSoftWrapProcessed = false;
|
||||
}
|
||||
|
||||
int column = 0;
|
||||
int prevX = 0;
|
||||
CharSequence text = myDocument.getCharsNoThreadCheck();
|
||||
@@ -798,7 +818,9 @@ public final class EditorImpl extends UserDataHolderBase implements EditorEx, Hi
|
||||
int x = 0;
|
||||
outer:
|
||||
while (true) {
|
||||
if (offset >= textLength) break;
|
||||
if (offset >= textLength) {
|
||||
break;
|
||||
}
|
||||
|
||||
if (offset >= state.getEndOffset()) {
|
||||
state.advance();
|
||||
@@ -811,32 +833,56 @@ public final class EditorImpl extends UserDataHolderBase implements EditorEx, Hi
|
||||
for (char aPlaceholder : placeholder) {
|
||||
c = aPlaceholder;
|
||||
x += EditorUtil.charWidth(c, fontType, this);
|
||||
if (x >= px) break outer;
|
||||
if (x >= px) {
|
||||
break outer;
|
||||
}
|
||||
column++;
|
||||
}
|
||||
offset = region.getEndOffset();
|
||||
}
|
||||
else {
|
||||
TextChange softWrap = mySoftWrapModel.getSoftWrap(offset);
|
||||
if (softWrap != null) {
|
||||
// There is a possible case that soft wrap contains more than one line feed inside and we need to start counting not
|
||||
// from its first line.
|
||||
int softWrapLinesToSkip = activeSoftWrapProcessed ? 0 : logicalPosition.linesFromActiveSoftWrap;
|
||||
CharSequence softWrapText = softWrap.getText();
|
||||
for (int i = 0; i < softWrapText.length(); i++) {
|
||||
c = softWrapText.charAt(i);
|
||||
if (softWrapLinesToSkip > 0) {
|
||||
if (c == '\n') {
|
||||
softWrapLinesToSkip--;
|
||||
}
|
||||
continue;
|
||||
}
|
||||
prevX = x;
|
||||
int diff = charToVisibleWidth(c, fontType, x);
|
||||
if (diff == 0) {
|
||||
break outer;
|
||||
}
|
||||
|
||||
x += diff;
|
||||
if (x >= px) {
|
||||
break outer;
|
||||
}
|
||||
column += columnsNumber(c, x, prevX, spaceSize);
|
||||
}
|
||||
activeSoftWrapProcessed = true;
|
||||
}
|
||||
|
||||
prevX = x;
|
||||
c = text.charAt(offset);
|
||||
if (c == '\n') {
|
||||
int diff = charToVisibleWidth(c, fontType, x);
|
||||
if (diff == 0) {
|
||||
break;
|
||||
} else {
|
||||
x += diff;
|
||||
}
|
||||
|
||||
if (x >= px) {
|
||||
break;
|
||||
}
|
||||
if (c == '\t') {
|
||||
x = EditorUtil.nextTabStop(x, this);
|
||||
}
|
||||
else {
|
||||
x += EditorUtil.charWidth(c, fontType, this);
|
||||
}
|
||||
|
||||
if (x >= px) break;
|
||||
|
||||
if (c == '\t') {
|
||||
column += (x - prevX) / spaceSize;
|
||||
}
|
||||
else {
|
||||
column++;
|
||||
}
|
||||
column += columnsNumber(c, x, prevX, spaceSize);
|
||||
|
||||
offset++;
|
||||
}
|
||||
@@ -867,6 +913,42 @@ public final class EditorImpl extends UserDataHolderBase implements EditorEx, Hi
|
||||
return new VisualPosition(line, column);
|
||||
}
|
||||
|
||||
/**
|
||||
* Allows to answer how much width requires given char to be represented on a screen.
|
||||
*
|
||||
* @param c target character
|
||||
* @param fontType font type to use for representation of the given character
|
||||
* @param currentX current <code>'x'</code> position on a line where given character should be displayed
|
||||
* @return width required to represent given char with the given settings on a screen;
|
||||
* <code>'0'</code> if given char is a line break
|
||||
*/
|
||||
private int charToVisibleWidth(char c, int fontType, int currentX) {
|
||||
if (c == '\n') {
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (c == '\t') {
|
||||
return EditorUtil.nextTabStop(currentX, this) - currentX;
|
||||
}
|
||||
return EditorUtil.charWidth(c, fontType, this);
|
||||
}
|
||||
|
||||
/**
|
||||
* Allows to answer how many columns are necessary for representation of the given char on a screen.
|
||||
*
|
||||
* @param c target char
|
||||
* @param x <code>'x'</code> coordinate of the line where given char is represented that indicates char end location
|
||||
* @param prevX <code>'x'</code> coordinate of the line where given char is represented that indicates char start location
|
||||
* @param spaceSize <code>'space'</code> symbol width
|
||||
* @return number of columns necessary for representation of the given char on a screen.
|
||||
*/
|
||||
private static int columnsNumber(char c, int x, int prevX, int spaceSize) {
|
||||
if (c != '\t') {
|
||||
return 1;
|
||||
}
|
||||
return (x - prevX) / spaceSize;
|
||||
}
|
||||
|
||||
@NotNull
|
||||
public VisualPosition offsetToVisualPosition(int offset) {
|
||||
return logicalToVisualPosition(offsetToLogicalPosition(offset));
|
||||
@@ -874,8 +956,16 @@ public final class EditorImpl extends UserDataHolderBase implements EditorEx, Hi
|
||||
|
||||
@NotNull
|
||||
public LogicalPosition offsetToLogicalPosition(int offset) {
|
||||
int line = calcLogicalLineNumber(offset);
|
||||
int column = calcColumnNumber(offset, line);
|
||||
return offsetToLogicalPosition(offset, true);
|
||||
}
|
||||
|
||||
public LogicalPosition offsetToLogicalPosition(int offset, boolean softWrapAware) {
|
||||
if (softWrapAware) {
|
||||
return mySoftWrapModel.offsetToLogicalPosition(offset);
|
||||
}
|
||||
|
||||
int line = calcLogicalLineNumber(offset, false);
|
||||
int column = calcColumnNumber(offset, line, false);
|
||||
return new LogicalPosition(line, column);
|
||||
}
|
||||
|
||||
@@ -1030,31 +1120,31 @@ public final class EditorImpl extends UserDataHolderBase implements EditorEx, Hi
|
||||
return myGutterComponent != null && myGutterComponent.isShowing();
|
||||
}
|
||||
|
||||
private void repaintToScreenBotton(int startLine) {
|
||||
Rectangle visibleRect = getScrollingModel().getVisibleArea();
|
||||
private void repaintToScreenBottom(int startLine) {
|
||||
Rectangle visibleArea = getScrollingModel().getVisibleArea();
|
||||
int yStartLine = logicalLineToY(startLine);
|
||||
int yEndLine = visibleRect.y + visibleRect.height;
|
||||
int yEndLine = visibleArea.y + visibleArea.height;
|
||||
|
||||
myEditorComponent.repaintEditorComponent(visibleRect.x, yStartLine, visibleRect.x + visibleRect.width, yEndLine - yStartLine);
|
||||
myEditorComponent.repaintEditorComponent(visibleArea.x, yStartLine, visibleArea.x + visibleArea.width, yEndLine - yStartLine);
|
||||
myGutterComponent.repaint(0, yStartLine, myGutterComponent.getWidth(), yEndLine - yStartLine);
|
||||
}
|
||||
|
||||
public void repaintLines(int startLine, int endLine) {
|
||||
if (!isShowing()) return;
|
||||
|
||||
Rectangle visibleRect = getScrollingModel().getVisibleArea();
|
||||
Rectangle visibleArea = getScrollingModel().getVisibleArea();
|
||||
int yStartLine = logicalLineToY(startLine);
|
||||
int yEndLine = logicalLineToY(endLine) + getLineHeight() + WAVE_HEIGHT;
|
||||
|
||||
myEditorComponent.repaintEditorComponent(visibleRect.x, yStartLine, visibleRect.x + visibleRect.width, yEndLine - yStartLine);
|
||||
myEditorComponent.repaintEditorComponent(visibleArea.x, yStartLine, visibleArea.x + visibleArea.width, yEndLine - yStartLine);
|
||||
myGutterComponent.repaint(0, yStartLine, myGutterComponent.getWidth(), yEndLine - yStartLine);
|
||||
}
|
||||
|
||||
private void beforeChangedUpdate(DocumentEvent e) {
|
||||
if (!myDocument.isInBulkUpdate()) {
|
||||
Rectangle viewRect = getScrollingModel().getVisibleArea();
|
||||
Rectangle visibleArea = getScrollingModel().getVisibleArea();
|
||||
Point pos = visualPositionToXY(getCaretModel().getVisualPosition());
|
||||
myCaretUpdateVShift = pos.y - viewRect.y;
|
||||
myCaretUpdateVShift = pos.y - visibleArea.y;
|
||||
}
|
||||
mySizeContainer.beforeChange(e);
|
||||
}
|
||||
@@ -1081,7 +1171,7 @@ public final class EditorImpl extends UserDataHolderBase implements EditorEx, Hi
|
||||
|
||||
if (countLineFeeds(e.getOldFragment()) != countLineFeeds(e.getNewFragment())) {
|
||||
// Lines removed. Need to repaint till the end of the screen
|
||||
repaintToScreenBotton(startLine);
|
||||
repaintToScreenBottom(startLine);
|
||||
painted = true;
|
||||
}
|
||||
}
|
||||
@@ -1139,7 +1229,7 @@ public final class EditorImpl extends UserDataHolderBase implements EditorEx, Hi
|
||||
}
|
||||
}
|
||||
|
||||
void recalcSizeAndRepaint() {
|
||||
void recalculateSizeAndRepaint() {
|
||||
mySizeContainer.reset();
|
||||
validateSize();
|
||||
myEditorComponent.repaintEditorComponent();
|
||||
@@ -1177,6 +1267,10 @@ public final class EditorImpl extends UserDataHolderBase implements EditorEx, Hi
|
||||
return isReleased;
|
||||
}
|
||||
|
||||
public boolean isUnderRepainting() {
|
||||
return myUnderRepainting;
|
||||
}
|
||||
|
||||
void paint(Graphics g) {
|
||||
startOptimizedScrolling();
|
||||
|
||||
@@ -1203,22 +1297,28 @@ public final class EditorImpl extends UserDataHolderBase implements EditorEx, Hi
|
||||
return;
|
||||
}
|
||||
|
||||
paintBackgrounds(g, clip);
|
||||
paintRectangularSelection(g);
|
||||
paintRightMargin(g, clip);
|
||||
paintCustomRenderers((Graphics2D)g, clip);
|
||||
final MarkupModel docMarkup = myDocument.getMarkupModel(myProject);
|
||||
paintLineMarkersSeparators(g, clip, docMarkup);
|
||||
paintLineMarkersSeparators(g, clip, myMarkupModel);
|
||||
paintText(g, clip);
|
||||
paintSegmentHighlightersBorderAndAfterEndOfLine(g, clip);
|
||||
BorderEffect borderEffect = new BorderEffect(this, g);
|
||||
borderEffect.paintHighlighters(getHighlighter());
|
||||
borderEffect.paintHighlighters(docMarkup.getAllHighlighters());
|
||||
borderEffect.paintHighlighters(getMarkupModel().getAllHighlighters());
|
||||
paintCaretCursor(g);
|
||||
myUnderRepainting = true;
|
||||
try {
|
||||
paintBackgrounds(g, clip);
|
||||
paintRectangularSelection(g);
|
||||
paintRightMargin(g, clip);
|
||||
paintCustomRenderers((Graphics2D)g, clip);
|
||||
final MarkupModel docMarkup = myDocument.getMarkupModel(myProject);
|
||||
paintLineMarkersSeparators(g, clip, docMarkup);
|
||||
paintLineMarkersSeparators(g, clip, myMarkupModel);
|
||||
paintText(g, clip);
|
||||
paintSegmentHighlightersBorderAndAfterEndOfLine(g, clip);
|
||||
BorderEffect borderEffect = new BorderEffect(this, g);
|
||||
borderEffect.paintHighlighters(getHighlighter());
|
||||
borderEffect.paintHighlighters(docMarkup.getAllHighlighters());
|
||||
borderEffect.paintHighlighters(getMarkupModel().getAllHighlighters());
|
||||
paintCaretCursor(g);
|
||||
|
||||
paintComposedTextDecoration((Graphics2D)g);
|
||||
paintComposedTextDecoration((Graphics2D)g);
|
||||
}
|
||||
finally {
|
||||
myUnderRepainting = false;
|
||||
}
|
||||
}
|
||||
|
||||
private void paintCustomRenderers(Graphics2D g, Rectangle clip) {
|
||||
@@ -1400,6 +1500,7 @@ public final class EditorImpl extends UserDataHolderBase implements EditorEx, Hi
|
||||
return width;
|
||||
}
|
||||
|
||||
@SuppressWarnings({"StatementWithEmptyBody"})
|
||||
private void paintBackgrounds(Graphics g, Rectangle clip) {
|
||||
Color defaultBackground = getBackroundColor();
|
||||
g.setColor(defaultBackground);
|
||||
@@ -1440,6 +1541,8 @@ public final class EditorImpl extends UserDataHolderBase implements EditorEx, Hi
|
||||
int fontType = attributes.getFontType();
|
||||
CharSequence text = myDocument.getCharsNoThreadCheck();
|
||||
int lastLineIndex = Math.max(0, myDocument.getLineCount() - 1);
|
||||
|
||||
outer:
|
||||
while (!iterationState.atEnd() && !lIterator.atEnd()) {
|
||||
int hEnd = iterationState.getEndOffset();
|
||||
int lEnd = lIterator.getEnd();
|
||||
@@ -1478,6 +1581,37 @@ public final class EditorImpl extends UserDataHolderBase implements EditorEx, Hi
|
||||
position.x = drawBackground(g, backColor, collapsedFolderAt.getPlaceholderText(), position, fontType, defaultBackground, clip);
|
||||
}
|
||||
else {
|
||||
TextChange softWrap = getSoftWrapModel().getSoftWrap(start);
|
||||
if (softWrap == null && getSoftWrapModel().shouldWrap(myDocument.getRawChars(), start, hEnd, position)) {
|
||||
softWrap = getSoftWrapModel().wrap(myDocument.getRawChars(), start, hEnd);
|
||||
}
|
||||
if (softWrap != null) {
|
||||
// Draw soft wrap text background if any.
|
||||
CharSequence softWrapText = softWrap.getText();
|
||||
int softWrapStart = 0;
|
||||
int softWrapEnd = 0;
|
||||
while (softWrapEnd < softWrapText.length()) {
|
||||
for (; softWrapEnd < softWrapText.length() && softWrapText.charAt(softWrapEnd) != '\n'; softWrapEnd++) ;
|
||||
if (softWrapStart >= softWrapText.length() || softWrapEnd >= softWrapText.length()) {
|
||||
position.x = drawBackground(
|
||||
g, backColor, softWrapText.subSequence(softWrapStart, softWrapText.length()), position, fontType, defaultBackground, clip
|
||||
);
|
||||
}
|
||||
else {
|
||||
if (softWrapEnd > softWrapStart + 1) {
|
||||
drawBackground(
|
||||
g, backColor, softWrapText.subSequence(softWrapStart, softWrapEnd - 1), position, fontType, defaultBackground, clip
|
||||
);
|
||||
}
|
||||
if (position.y > clip.y + clip.height) break outer;
|
||||
position.x = 0;
|
||||
position.y += lineHeight;
|
||||
softWrapStart = softWrapEnd + 1;
|
||||
softWrapEnd = softWrapStart;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (hEnd > lEnd - lIterator.getSeparatorLength()) {
|
||||
position.x = drawBackground(g, backColor, text.subSequence(start, lEnd - lIterator.getSeparatorLength()), position, fontType,
|
||||
defaultBackground, clip);
|
||||
@@ -1500,6 +1634,24 @@ public final class EditorImpl extends UserDataHolderBase implements EditorEx, Hi
|
||||
if (lIterator.getLineNumber() >= lastLineIndex && position.y <= clip.y + clip.height) {
|
||||
paintAfterFileEndBackground(iterationState, g, position, clip, lineHeight, defaultBackground);
|
||||
}
|
||||
|
||||
// Perform additional activity if soft wrap is added or removed during repainting.
|
||||
// Note: this code lives in this method in assumption that background repainting is the very first activity performed
|
||||
// during whole editor component repaint.
|
||||
List<TextChange> softWrapsAfterRepaint = mySoftWrapModel.getRegisteredSoftWraps();
|
||||
if (!mySoftWrapsOnLastRepaint.equals(softWrapsAfterRepaint)) {
|
||||
// Repaint editor to the bottom in order to ensure that its content is shown correctly after new soft wrap introduction.
|
||||
repaintToScreenBottom(xyToLogicalPosition(position).line);
|
||||
|
||||
// Repaint gutter at all space that is located after active clip in order to ensure that line numbers are correctly redrawn
|
||||
// in accordance with the newly introduced soft wrap(s).
|
||||
myGutterComponent.repaint(0, clip.y, myGutterComponent.getWidth(), myGutterComponent.getHeight() - clip.y);
|
||||
|
||||
// Ask caret model to update visual caret position.
|
||||
getCaretModel().moveToOffset(getCaretModel().getOffset());
|
||||
}
|
||||
mySoftWrapsOnLastRepaint.clear();
|
||||
mySoftWrapsOnLastRepaint.addAll(softWrapsAfterRepaint);
|
||||
}
|
||||
|
||||
private void paintRectangularSelection(Graphics g) {
|
||||
@@ -1597,7 +1749,8 @@ public final class EditorImpl extends UserDataHolderBase implements EditorEx, Hi
|
||||
|
||||
int visibleLineNumber = clip.y / lineHeight;
|
||||
|
||||
int startLineNumber = xyToLogicalPosition(new Point(0, clip.y)).line;
|
||||
LogicalPosition logicalPosition = xyToLogicalPosition(new Point(0, clip.y));
|
||||
int startLineNumber = logicalPosition.line;
|
||||
|
||||
Point position = new Point(0, visibleLineNumber * lineHeight);
|
||||
if (startLineNumber == 0 && myPrefixText != null) {
|
||||
@@ -1609,7 +1762,16 @@ public final class EditorImpl extends UserDataHolderBase implements EditorEx, Hi
|
||||
return;
|
||||
}
|
||||
|
||||
// There is a possible case that starting logical line is split by soft-wraps and it's part after the split should be drawn.
|
||||
// We do the following then:
|
||||
// 1. Mark that we're under such circumstances ('activeSoftWrapProcessed = false');
|
||||
// 2. Use active soft wrap offset as a start offset in order to paint its symbols in correct way;
|
||||
boolean activeSoftWrapProcessed = true;
|
||||
int start = myDocument.getLineStartOffset(startLineNumber);
|
||||
if (logicalPosition.isOnSoftWrappedLine()) {
|
||||
start = logicalPositionToOffset(logicalPosition);
|
||||
activeSoftWrapProcessed = false;
|
||||
}
|
||||
|
||||
IterationState iterationState = new IterationState(this, start, paintSelection());
|
||||
|
||||
@@ -1639,7 +1801,9 @@ public final class EditorImpl extends UserDataHolderBase implements EditorEx, Hi
|
||||
drawString(g, chars, start, lEnd - lIterator.getSeparatorLength(), position, clip, effectColor, effectType, fontType,
|
||||
currentColor);
|
||||
position.x = 0;
|
||||
if (position.y > clip.y + clip.height) break;
|
||||
if (position.y > clip.y + clip.height) {
|
||||
break;
|
||||
}
|
||||
position.y += lineHeight;
|
||||
start = lEnd;
|
||||
}
|
||||
@@ -1662,6 +1826,62 @@ public final class EditorImpl extends UserDataHolderBase implements EditorEx, Hi
|
||||
fontType, currentColor);
|
||||
}
|
||||
else {
|
||||
boolean breakWholeIteration = false;
|
||||
TextChange softWrap = getSoftWrapModel().getSoftWrap(start);
|
||||
if (softWrap != null) {
|
||||
// There is a possible case that soft wrap contains more than one line feed inside and we need to start drawing not
|
||||
// from its first line.
|
||||
int softWrapLinesToSkip = activeSoftWrapProcessed ? 0 : logicalPosition.linesFromActiveSoftWrap;
|
||||
char[] softWrapChars = softWrap.getChars();
|
||||
|
||||
// We don't draw every soft wrap symbol one-by-one but whole visual line. Current variable holds index that points
|
||||
// to the first soft wrap symbol that is not drawn yet.
|
||||
int softWrapSegmentStartIndex = 0;
|
||||
for (int i = 0; i < softWrapChars.length; i++) {
|
||||
// Delay soft wraps symbols drawing until EOL if found.
|
||||
if (softWrapChars[i] != '\n') {
|
||||
continue;
|
||||
}
|
||||
|
||||
// Skip top soft wrap lines if necessary.
|
||||
if (softWrapLinesToSkip-- > 0) {
|
||||
position.x = 0;
|
||||
softWrapSegmentStartIndex = i + 1;
|
||||
continue;
|
||||
}
|
||||
|
||||
// Draw soft wrap symbols on current visual line if any.
|
||||
if (i - softWrapSegmentStartIndex > 0) {
|
||||
drawString(
|
||||
g, softWrapChars, softWrapSegmentStartIndex, i, position, clip, currentColor, effectType, fontType, currentColor
|
||||
);
|
||||
}
|
||||
|
||||
// Reset 'x' coordinate because of new line start.
|
||||
position.x = 0;
|
||||
|
||||
// Stop the processing if we drew the whole clip.
|
||||
if (position.y > clip.y + clip.height) {
|
||||
breakWholeIteration = true;
|
||||
break;
|
||||
}
|
||||
position.y += lineHeight;
|
||||
softWrapSegmentStartIndex = i + 1;
|
||||
}
|
||||
|
||||
// Draw remaining soft wrap symbols from its last line if any.
|
||||
if (softWrapSegmentStartIndex < softWrapChars.length - 1) {
|
||||
position.x += drawString(
|
||||
g, softWrapChars, softWrapSegmentStartIndex, softWrapChars.length, position, clip, effectColor, effectType,
|
||||
fontType, currentColor
|
||||
);
|
||||
}
|
||||
|
||||
activeSoftWrapProcessed = true;
|
||||
}
|
||||
if (breakWholeIteration) {
|
||||
break;
|
||||
}
|
||||
position.x = drawString(g, chars, start, hEnd, position, clip, effectColor, effectType, fontType, currentColor);
|
||||
}
|
||||
}
|
||||
@@ -1773,6 +1993,9 @@ public final class EditorImpl extends UserDataHolderBase implements EditorEx, Hi
|
||||
}
|
||||
|
||||
private void paintCaretCursor(Graphics g) {
|
||||
// There is a possible case that visual caret position is changed because of newly added or removed soft wraps.
|
||||
// We check if that's the case and ask caret model to recalculate visual position if necessary.
|
||||
|
||||
myCaretCursor.paint(g);
|
||||
}
|
||||
|
||||
@@ -2201,8 +2424,14 @@ public final class EditorImpl extends UserDataHolderBase implements EditorEx, Hi
|
||||
|
||||
@NotNull
|
||||
public VisualPosition logicalToVisualPosition(@NotNull LogicalPosition logicalPos) {
|
||||
return logicalToVisualPosition(logicalPos, true);
|
||||
}
|
||||
|
||||
private VisualPosition logicalToVisualPosition(@NotNull LogicalPosition logicalPos, boolean softWrapAware) {
|
||||
assertReadAccess();
|
||||
if (!myFoldingModel.isFoldingEnabled()) return new VisualPosition(logicalPos.line, logicalPos.column);
|
||||
if (!myFoldingModel.isFoldingEnabled() && !mySoftWrapModel.isSoftWrappingEnabled()) {
|
||||
return new VisualPosition(logicalPos.line, logicalPos.column);
|
||||
}
|
||||
|
||||
int offset = logicalPositionToOffset(logicalPos);
|
||||
|
||||
@@ -2223,16 +2452,17 @@ public final class EditorImpl extends UserDataHolderBase implements EditorEx, Hi
|
||||
|
||||
line -= myFoldingModel.getFoldedLinesCountBefore(offset);
|
||||
|
||||
FoldRegion[] toplevel = myFoldingModel.fetchTopLevel();
|
||||
FoldRegion[] topLevel = myFoldingModel.fetchTopLevel();
|
||||
LogicalPosition anchorFoldingPosition = logicalPos;
|
||||
for (int idx = myFoldingModel.getLastTopLevelIndexBefore(offset); idx >= 0; idx--) {
|
||||
FoldRegion region = toplevel[idx];
|
||||
FoldRegion region = topLevel[idx];
|
||||
if (region.isValid()) {
|
||||
if (region.getDocument().getLineNumber(region.getEndOffset()) == logicalPos.line && region.getEndOffset() <= offset) {
|
||||
if (region.getDocument().getLineNumber(region.getEndOffset()) == anchorFoldingPosition.line && region.getEndOffset() <= offset) {
|
||||
LogicalPosition foldStart = offsetToLogicalPosition(region.getStartOffset());
|
||||
LogicalPosition foldEnd = offsetToLogicalPosition(region.getEndOffset());
|
||||
column += foldStart.column + region.getPlaceholderText().length() - foldEnd.column;
|
||||
offset = region.getStartOffset();
|
||||
logicalPos = foldStart;
|
||||
anchorFoldingPosition = foldStart;
|
||||
}
|
||||
else {
|
||||
break;
|
||||
@@ -2242,7 +2472,13 @@ public final class EditorImpl extends UserDataHolderBase implements EditorEx, Hi
|
||||
|
||||
LOG.assertTrue(line >= 0);
|
||||
|
||||
return new VisualPosition(line, Math.max(0, column));
|
||||
VisualPosition softWrapUnawarePosition = new VisualPosition(line, Math.max(0, column));
|
||||
if (softWrapAware) {
|
||||
return mySoftWrapModel.adjustVisualPosition(logicalPos, softWrapUnawarePosition);
|
||||
}
|
||||
else {
|
||||
return softWrapUnawarePosition;
|
||||
}
|
||||
}
|
||||
|
||||
@Nullable
|
||||
@@ -2311,7 +2547,9 @@ public final class EditorImpl extends UserDataHolderBase implements EditorEx, Hi
|
||||
@NotNull
|
||||
public LogicalPosition visualToLogicalPosition(@NotNull VisualPosition visiblePos) {
|
||||
assertReadAccess();
|
||||
if (!myFoldingModel.isFoldingEnabled()) return new LogicalPosition(visiblePos.line, visiblePos.column);
|
||||
if (!myFoldingModel.isFoldingEnabled() && !mySoftWrapModel.isSoftWrappingEnabled()) {
|
||||
return new LogicalPosition(visiblePos.line, visiblePos.column);
|
||||
}
|
||||
|
||||
int line = visiblePos.line;
|
||||
int column = visiblePos.column;
|
||||
@@ -2319,26 +2557,41 @@ public final class EditorImpl extends UserDataHolderBase implements EditorEx, Hi
|
||||
FoldRegion lastCollapsedBefore = getLastCollapsedBeforePosition(visiblePos);
|
||||
|
||||
if (lastCollapsedBefore != null) {
|
||||
LogicalPosition logFoldEnd = offsetToLogicalPosition(lastCollapsedBefore.getEndOffset());
|
||||
VisualPosition visFoldEnd = logicalToVisualPosition(logFoldEnd);
|
||||
|
||||
line = logFoldEnd.line + (visiblePos.line - visFoldEnd.line);
|
||||
if (visFoldEnd.line == visiblePos.line) {
|
||||
if (visiblePos.column >= visFoldEnd.column) {
|
||||
column = logFoldEnd.column + (visiblePos.column - visFoldEnd.column);
|
||||
LogicalPosition softWrapAwareLogFoldEnd = offsetToLogicalPosition(lastCollapsedBefore.getEndOffset());
|
||||
VisualPosition softWrapAwareVisFoldEnd = logicalToVisualPosition(softWrapAwareLogFoldEnd);
|
||||
if (softWrapAwareVisFoldEnd.line == visiblePos.line) {
|
||||
if (visiblePos.column == softWrapAwareVisFoldEnd.column) {
|
||||
return softWrapAwareLogFoldEnd;
|
||||
}
|
||||
else if (visiblePos.column > softWrapAwareVisFoldEnd.column) {
|
||||
int columnToUse = softWrapAwareLogFoldEnd.column + visiblePos.column - softWrapAwareVisFoldEnd.column;
|
||||
return new LogicalPosition(
|
||||
softWrapAwareLogFoldEnd.line, columnToUse, softWrapAwareLogFoldEnd.softWrapLines,
|
||||
softWrapAwareLogFoldEnd.linesFromActiveSoftWrap, visiblePos.column - columnToUse - softWrapAwareLogFoldEnd.foldingColumnDiff,
|
||||
softWrapAwareLogFoldEnd.foldedLines, softWrapAwareLogFoldEnd.foldingColumnDiff
|
||||
);
|
||||
}
|
||||
else {
|
||||
return offsetToLogicalPosition(lastCollapsedBefore.getStartOffset());
|
||||
}
|
||||
}
|
||||
|
||||
LogicalPosition softWrapUnawareLogFoldEnd = offsetToLogicalPosition(lastCollapsedBefore.getEndOffset(), false);
|
||||
VisualPosition softWrapUnawareVisFoldEnd = logicalToVisualPosition(softWrapUnawareLogFoldEnd, false);
|
||||
line = softWrapUnawareLogFoldEnd.line + (visiblePos.line - softWrapUnawareVisFoldEnd.line);
|
||||
}
|
||||
|
||||
if (column < 0) column = 0;
|
||||
|
||||
return new LogicalPosition(line, column);
|
||||
LogicalPosition softWrapUnawareResult = new LogicalPosition(line, column);
|
||||
return mySoftWrapModel.adjustLogicalPosition(softWrapUnawareResult, visiblePos);
|
||||
}
|
||||
|
||||
private int calcLogicalLineNumber(int offset) {
|
||||
return calcLogicalLineNumber(offset, true);
|
||||
}
|
||||
|
||||
private int calcLogicalLineNumber(int offset, boolean softWrapAware) {
|
||||
int textLength = myDocument.getTextLength();
|
||||
if (textLength == 0) return 0;
|
||||
|
||||
@@ -2346,20 +2599,32 @@ public final class EditorImpl extends UserDataHolderBase implements EditorEx, Hi
|
||||
throw new IndexOutOfBoundsException("Wrong offset: " + offset + " textLength: " + textLength);
|
||||
}
|
||||
|
||||
int lineIndex = myDocument.getLineNumber(offset);
|
||||
|
||||
LOG.assertTrue(lineIndex >= 0 && lineIndex < myDocument.getLineCount());
|
||||
|
||||
return lineIndex;
|
||||
if (softWrapAware) {
|
||||
return mySoftWrapModel.offsetToLogicalPosition(offset).line;
|
||||
}
|
||||
else {
|
||||
int lineIndex = myDocument.getLineNumber(offset);
|
||||
LOG.assertTrue(lineIndex >= 0 && lineIndex < myDocument.getLineCount());
|
||||
return lineIndex;
|
||||
}
|
||||
}
|
||||
|
||||
public int calcColumnNumber(int offset, int lineIndex) {
|
||||
return calcColumnNumber(offset, lineIndex, true);
|
||||
}
|
||||
|
||||
public int calcColumnNumber(int offset, int lineIndex, boolean softWrapAware) {
|
||||
if (myDocument.getTextLength() == 0) return 0;
|
||||
|
||||
CharSequence text = myDocument.getCharsSequence();
|
||||
int start = myDocument.getLineStartOffset(lineIndex);
|
||||
if (start == offset) return 0;
|
||||
return EditorUtil.calcColumnNumber(this, text, start, offset, EditorUtil.getTabSize(this));
|
||||
if (softWrapAware) {
|
||||
return mySoftWrapModel.offsetToLogicalPosition(offset).column;
|
||||
}
|
||||
else {
|
||||
CharSequence text = myDocument.getCharsSequence();
|
||||
int start = myDocument.getLineStartOffset(lineIndex);
|
||||
if (start == offset) return 0;
|
||||
return EditorUtil.calcColumnNumber(this, text, start, offset, EditorUtil.getTabSize(this));
|
||||
}
|
||||
}
|
||||
|
||||
private void moveCaretToScreenPos(int x, int y) {
|
||||
@@ -2371,10 +2636,16 @@ public final class EditorImpl extends UserDataHolderBase implements EditorEx, Hi
|
||||
|
||||
int columnNumber = pos.column;
|
||||
int lineNumber = pos.line;
|
||||
int softWrapLines = pos.softWrapLines;
|
||||
int linesFromCurrentSoftWrap = pos.linesFromActiveSoftWrap;
|
||||
int softWrapColumns = pos.softWrapColumnDiff;
|
||||
|
||||
if (lineNumber < 0) {
|
||||
lineNumber = 0;
|
||||
columnNumber = 0;
|
||||
softWrapLines = 0;
|
||||
linesFromCurrentSoftWrap = 0;
|
||||
softWrapColumns = 0;
|
||||
}
|
||||
|
||||
final int totalLines = myDocument.getLineCount();
|
||||
@@ -2393,6 +2664,9 @@ public final class EditorImpl extends UserDataHolderBase implements EditorEx, Hi
|
||||
int lineEndColumnNumber = calcColumnNumber(lineEndOffset, lineNumber);
|
||||
if (columnNumber > lineEndColumnNumber) {
|
||||
columnNumber = lineEndColumnNumber;
|
||||
if (softWrapColumns != 0) {
|
||||
softWrapColumns -= columnNumber - lineEndColumnNumber;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2405,7 +2679,9 @@ public final class EditorImpl extends UserDataHolderBase implements EditorEx, Hi
|
||||
}
|
||||
}
|
||||
}
|
||||
LogicalPosition pos1 = new LogicalPosition(lineNumber, columnNumber);
|
||||
LogicalPosition pos1 = new LogicalPosition(
|
||||
lineNumber, columnNumber, softWrapLines, linesFromCurrentSoftWrap, softWrapColumns, pos.foldedLines, pos.foldingColumnDiff
|
||||
);
|
||||
getCaretModel().moveToLogicalPosition(pos1);
|
||||
}
|
||||
|
||||
@@ -2547,7 +2823,7 @@ public final class EditorImpl extends UserDataHolderBase implements EditorEx, Hi
|
||||
if (SwingUtilities.isRightMouseButton(e)) {
|
||||
return;
|
||||
}
|
||||
Rectangle rect = getScrollingModel().getVisibleArea();
|
||||
Rectangle visibleArea = getScrollingModel().getVisibleArea();
|
||||
|
||||
int x = e.getX();
|
||||
|
||||
@@ -2556,23 +2832,23 @@ public final class EditorImpl extends UserDataHolderBase implements EditorEx, Hi
|
||||
}
|
||||
|
||||
int dx = 0;
|
||||
if (x < rect.x && rect.x > 0) {
|
||||
dx = x - rect.x;
|
||||
if (x < visibleArea.x && visibleArea.x > 0) {
|
||||
dx = x - visibleArea.x;
|
||||
}
|
||||
else {
|
||||
if (x > rect.x + rect.width) {
|
||||
dx = x - rect.x - rect.width;
|
||||
if (x > visibleArea.x + visibleArea.width) {
|
||||
dx = x - visibleArea.x - visibleArea.width;
|
||||
}
|
||||
}
|
||||
|
||||
int dy = 0;
|
||||
int y = e.getY();
|
||||
if (y < rect.y && rect.y > 0) {
|
||||
dy = y - rect.y;
|
||||
if (y < visibleArea.y && visibleArea.y > 0) {
|
||||
dy = y - visibleArea.y;
|
||||
}
|
||||
else {
|
||||
if (y > rect.y + rect.height) {
|
||||
dy = y - rect.y - rect.height;
|
||||
if (y > visibleArea.y + visibleArea.height) {
|
||||
dy = y - visibleArea.y - visibleArea.height;
|
||||
}
|
||||
}
|
||||
if (dx == 0 && dy == 0) {
|
||||
@@ -2858,8 +3134,8 @@ public final class EditorImpl extends UserDataHolderBase implements EditorEx, Hi
|
||||
int lineHeight = getLineHeight();
|
||||
int y = myLocation.y;
|
||||
|
||||
Rectangle viewRect = getScrollingModel().getVisibleArea();
|
||||
if (x - viewRect.x < 0) {
|
||||
Rectangle viewRectangle = getScrollingModel().getVisibleArea();
|
||||
if (x - viewRectangle.x < 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -3013,8 +3289,8 @@ public final class EditorImpl extends UserDataHolderBase implements EditorEx, Hi
|
||||
}
|
||||
|
||||
class MyScrollBar extends JScrollBar {
|
||||
@NonNls private static final String DECR_BUTTON_FIELD = "decrButton";
|
||||
@NonNls private static final String INCR_BUTTON_FIELD = "incrButton";
|
||||
@NonNls private static final String DECREASE_BUTTON_FIELD = "decrButton";
|
||||
@NonNls private static final String INCREASE_BUTTON_FIELD = "incrButton";
|
||||
@NonNls private static final String APPLE_LAF_AQUA_SCROLL_BAR_UI_CLASS = "apple.laf.AquaScrollBarUI";
|
||||
|
||||
private MyScrollBar(int orientation) {
|
||||
@@ -3023,9 +3299,14 @@ public final class EditorImpl extends UserDataHolderBase implements EditorEx, Hi
|
||||
putClientProperty("JScrollBar.fastWheelScrolling", Boolean.TRUE); // fast scrolling for JDK 6
|
||||
}
|
||||
|
||||
@Override
|
||||
public void updateUI() {
|
||||
setUI(ButtonlessScrollBarUI.createNormal());
|
||||
}
|
||||
|
||||
/**
|
||||
* This is helper method. It returns height of the top (descrease) scrollbar
|
||||
* button. Please note, that it's possible to return real height only if scrollbar
|
||||
* This is helper method. It returns height of the top (decrease) scroll bar
|
||||
* button. Please note, that it's possible to return real height only if scroll bar
|
||||
* is instance of BasicScrollBarUI. Otherwise it returns fake (but good enough :) )
|
||||
* value.
|
||||
*/
|
||||
@@ -3034,11 +3315,11 @@ public final class EditorImpl extends UserDataHolderBase implements EditorEx, Hi
|
||||
Insets insets = getInsets();
|
||||
if (barUI instanceof BasicScrollBarUI) {
|
||||
try {
|
||||
Field decrButtonField = BasicScrollBarUI.class.getDeclaredField(DECR_BUTTON_FIELD);
|
||||
decrButtonField.setAccessible(true);
|
||||
JButton decrButtonValue = (JButton)decrButtonField.get(barUI);
|
||||
LOG.assertTrue(decrButtonValue != null);
|
||||
return insets.top + decrButtonValue.getHeight();
|
||||
Field decreaseButtonField = BasicScrollBarUI.class.getDeclaredField(DECREASE_BUTTON_FIELD);
|
||||
decreaseButtonField.setAccessible(true);
|
||||
JButton decreaseButtonValue = (JButton)decreaseButtonField.get(barUI);
|
||||
LOG.assertTrue(decreaseButtonValue != null);
|
||||
return insets.top + decreaseButtonValue.getHeight();
|
||||
}
|
||||
catch (Exception exc) {
|
||||
throw new IllegalStateException(exc.getMessage());
|
||||
@@ -3050,8 +3331,8 @@ public final class EditorImpl extends UserDataHolderBase implements EditorEx, Hi
|
||||
}
|
||||
|
||||
/**
|
||||
* This is helper method. It returns height of the bottom (increase) scrollbar
|
||||
* button. Please note, that it's possible to return real height only if scrollbar
|
||||
* This is helper method. It returns height of the bottom (increase) scroll bar
|
||||
* button. Please note, that it's possible to return real height only if scroll bar
|
||||
* is instance of BasicScrollBarUI. Otherwise it returns fake (but good enough :) )
|
||||
* value.
|
||||
*/
|
||||
@@ -3060,11 +3341,11 @@ public final class EditorImpl extends UserDataHolderBase implements EditorEx, Hi
|
||||
Insets insets = getInsets();
|
||||
if (barUI instanceof BasicScrollBarUI) {
|
||||
try {
|
||||
Field incrButtonField = BasicScrollBarUI.class.getDeclaredField(INCR_BUTTON_FIELD);
|
||||
incrButtonField.setAccessible(true);
|
||||
JButton incrButtonValue = (JButton)incrButtonField.get(barUI);
|
||||
LOG.assertTrue(incrButtonValue != null);
|
||||
return insets.bottom + incrButtonValue.getHeight();
|
||||
Field increaseButtonField = BasicScrollBarUI.class.getDeclaredField(INCREASE_BUTTON_FIELD);
|
||||
increaseButtonField.setAccessible(true);
|
||||
JButton increaseButtonValue = (JButton)increaseButtonField.get(barUI);
|
||||
LOG.assertTrue(increaseButtonValue != null);
|
||||
return insets.bottom + increaseButtonValue.getHeight();
|
||||
}
|
||||
catch (Exception exc) {
|
||||
throw new IllegalStateException(exc.getMessage());
|
||||
@@ -3191,7 +3472,7 @@ public final class EditorImpl extends UserDataHolderBase implements EditorEx, Hi
|
||||
public void setVerticalScrollbarOrientation(int type) {
|
||||
assertIsDispatchThread();
|
||||
int currentHorOffset = myScrollingModel.getHorizontalScrollOffset();
|
||||
myScrollbarOrientation = type;
|
||||
myScrollBarOrientation = type;
|
||||
if (type == VERTICAL_SCROLLBAR_LEFT) {
|
||||
myScrollPane.setLayout(new LeftHandScrollbarLayout());
|
||||
}
|
||||
@@ -3220,7 +3501,7 @@ public final class EditorImpl extends UserDataHolderBase implements EditorEx, Hi
|
||||
}
|
||||
|
||||
int getVerticalScrollbarOrientation() {
|
||||
return myScrollbarOrientation;
|
||||
return myScrollBarOrientation;
|
||||
}
|
||||
|
||||
MyScrollBar getVerticalScrollBar() {
|
||||
@@ -3505,10 +3786,12 @@ public final class EditorImpl extends UserDataHolderBase implements EditorEx, Hi
|
||||
return length;
|
||||
}
|
||||
|
||||
@Nullable
|
||||
public AttributedCharacterIterator cancelLatestCommittedText(AttributedCharacterIterator.Attribute[] attributes) {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Nullable
|
||||
public AttributedCharacterIterator getSelectedText(AttributedCharacterIterator.Attribute[] attributes) {
|
||||
String text = getSelectionModel().getSelectedText();
|
||||
return text == null ? null : new AttributedString(text).getIterator();
|
||||
@@ -4128,6 +4411,7 @@ public final class EditorImpl extends UserDataHolderBase implements EditorEx, Hi
|
||||
return false;
|
||||
}
|
||||
|
||||
@Nullable
|
||||
protected Transferable createTransferable(JComponent c) {
|
||||
Editor editor = getEditor(c);
|
||||
String s = editor.getSelectionModel().getSelectedText();
|
||||
|
||||
@@ -48,6 +48,7 @@ import com.intellij.util.containers.ContainerUtil;
|
||||
import com.intellij.util.ui.UIUtil;
|
||||
import gnu.trove.THashSet;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
import javax.swing.*;
|
||||
import java.awt.*;
|
||||
@@ -334,6 +335,9 @@ public class EditorMarkupModelImpl extends MarkupModelImpl implements EditorMark
|
||||
g.setColor(color);
|
||||
g.fillRect(x + 1, yStart, paintWidth - 2, yEnd - yStart);
|
||||
|
||||
if (color == null) {
|
||||
return;
|
||||
}
|
||||
Color brighter = color.brighter();
|
||||
Color darker = color.darker();
|
||||
|
||||
@@ -388,6 +392,7 @@ public class EditorMarkupModelImpl extends MarkupModelImpl implements EditorMark
|
||||
fireErrorMarkerClicked(marker, e);
|
||||
}
|
||||
|
||||
@Nullable
|
||||
private RangeHighlighter getNearestRangeHighlighter(final MouseEvent e, final int width) {
|
||||
List<MarkSpot> nearestSpots = getNearestMarkSpots(e, width);
|
||||
RangeHighlighter nearestMarker = null;
|
||||
|
||||
@@ -301,7 +301,7 @@ public class FoldingModelImpl implements FoldingModelEx, PrioritizedDocumentList
|
||||
myFoldTree.rebuild();
|
||||
|
||||
myEditor.updateCaretCursor();
|
||||
myEditor.recalcSizeAndRepaint();
|
||||
myEditor.recalculateSizeAndRepaint();
|
||||
if (myEditor.getGutterComponentEx().isFoldingOutlineShown()) {
|
||||
myEditor.getGutterComponentEx().repaint();
|
||||
}
|
||||
|
||||
@@ -38,7 +38,7 @@ import java.util.ArrayList;
|
||||
import java.util.Comparator;
|
||||
import java.util.List;
|
||||
|
||||
@SuppressWarnings({"ForLoopReplaceableByForEach"}) // Way too many garbage in AbrstractList.iterator() produced otherwise.
|
||||
@SuppressWarnings({"ForLoopReplaceableByForEach"}) // Way too many garbage in AbstractList.iterator() produced otherwise.
|
||||
public final class IterationState {
|
||||
private static final Logger LOG = Logger.getInstance("#com.intellij.openapi.editor.impl.IterationState");
|
||||
private final TextAttributes myMergedAttributes = new TextAttributes();
|
||||
|
||||
@@ -24,7 +24,10 @@ import com.intellij.openapi.util.text.LineTokenizer;
|
||||
import com.intellij.util.text.MergingCharSequence;
|
||||
|
||||
/**
|
||||
*
|
||||
* Data structure specialized for working with document text lines, i.e. stores information about line mapping to document
|
||||
* offsets and provides convenient ways to work with that information like retrieving target line by document offset etc.
|
||||
* <p/>
|
||||
* Not thread-safe.
|
||||
*/
|
||||
public class LineSet{
|
||||
private SegmentArrayWithData mySegments = new SegmentArrayWithData();
|
||||
|
||||
@@ -72,6 +72,7 @@ public class SettingsImpl implements EditorSettings {
|
||||
private Boolean myIsMouseClickSelectionHonorsCamelWords = null;
|
||||
private Boolean myIsRenameVariablesInplace = null;
|
||||
private Boolean myIsRefrainFromScrolling = null;
|
||||
private Boolean myUseSoftWraps = null;
|
||||
|
||||
public boolean isRightMarginShown() {
|
||||
return myIsRightMarginShown != null
|
||||
@@ -383,6 +384,18 @@ public class SettingsImpl implements EditorSettings {
|
||||
myIsRefrainFromScrolling = b ? Boolean.TRUE : Boolean.FALSE;
|
||||
}
|
||||
|
||||
public boolean isUseSoftWraps() {
|
||||
return myUseSoftWraps != null ? myUseSoftWraps.booleanValue()
|
||||
: EditorSettingsExternalizable.getInstance().isUseSoftWraps();
|
||||
}
|
||||
|
||||
public void setUseSoftWraps(boolean use) {
|
||||
final Boolean newValue = use ? Boolean.TRUE : Boolean.FALSE;
|
||||
if (newValue.equals(myUseSoftWraps)) return;
|
||||
myUseSoftWraps = newValue;
|
||||
fireEditorRefresh();
|
||||
}
|
||||
|
||||
private void fireEditorRefresh() {
|
||||
if (myEditor != null) {
|
||||
myEditor.reinitSettings();
|
||||
|
||||
@@ -0,0 +1,882 @@
|
||||
/*
|
||||
* Copyright 2000-2010 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.impl;
|
||||
|
||||
import com.intellij.openapi.editor.*;
|
||||
import com.intellij.openapi.editor.event.DocumentEvent;
|
||||
import com.intellij.openapi.editor.event.DocumentListener;
|
||||
import com.intellij.openapi.editor.event.VisibleAreaEvent;
|
||||
import com.intellij.openapi.editor.event.VisibleAreaListener;
|
||||
import com.intellij.openapi.editor.ex.SoftWrapModelEx;
|
||||
import com.intellij.openapi.editor.ex.util.EditorUtil;
|
||||
import com.intellij.openapi.util.text.StringUtil;
|
||||
import com.intellij.util.text.CharArrayUtil;
|
||||
import gnu.trove.TIntHashSet;
|
||||
import gnu.trove.TIntIterator;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
import java.awt.*;
|
||||
import java.nio.CharBuffer;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collections;
|
||||
import java.util.Comparator;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* Default {@link SoftWrapModelEx} implementation.
|
||||
* <p/>
|
||||
* Design principles:
|
||||
* <ul>
|
||||
* <li>
|
||||
* remembers width of the visible area used last time; drops all of registered soft wraps on offset/position
|
||||
* recalculation/adjustment request (e.g. {@link #adjustLogicalPosition(LogicalPosition, VisualPosition)}) if current
|
||||
* visible area width differs from the one used last time;
|
||||
* </li>
|
||||
* <li>
|
||||
* performs {@code 'soft wrap' -> 'hard wrap'} conversion if the document is change in a soft wrap-introduced
|
||||
* virtual space (e.g. user start typing inside soft wrap-introduced indent). There is a dedicated method that
|
||||
* does that if necessary - {@link #beforeDocumentChange(VisualPosition)};
|
||||
* </li>
|
||||
* </ul>
|
||||
* <p/>
|
||||
* Not thread-safe.
|
||||
*
|
||||
* @author Denis Zhdanov
|
||||
* @since Jun 8, 2010 12:47:32 PM
|
||||
*/
|
||||
public class SoftWrapModelImpl implements SoftWrapModelEx {
|
||||
|
||||
private static final Comparator<TextChange> SOFT_WRAPS_BY_OFFSET_COMPARATOR = new Comparator<TextChange>() {
|
||||
public int compare(TextChange c1, TextChange c2) {
|
||||
return c1.getStart() - c2.getEnd();
|
||||
}
|
||||
};
|
||||
|
||||
private final CharBuffer myCharBuffer = CharBuffer.allocate(1);
|
||||
|
||||
/**
|
||||
* Holds lines where soft wraps should be removed.
|
||||
* <p/>
|
||||
* The general idea is to do the following:
|
||||
* <ul>
|
||||
* <li>listen for document changes, mark all soft wraps that belong to modified logical line as <code>'dirty'</code>;</li>
|
||||
* <li>remove soft wraps marked as 'dirty' on repaint;</li>
|
||||
* </ul>
|
||||
*/
|
||||
private final TIntHashSet myDirtyLines = new TIntHashSet();
|
||||
|
||||
/** Holds registered soft wraps sorted by offsets in ascending order. */
|
||||
private final List<TextChange> myWraps = new ArrayList<TextChange>();
|
||||
private final List<TextChange> myWrapsView = Collections.unmodifiableList(myWraps);
|
||||
|
||||
private final EditorImpl myEditor;
|
||||
private int myRightEdgeLocation = -1;
|
||||
private boolean myInitialized;
|
||||
|
||||
/**
|
||||
* There is a possible case that visible area is changed but document representation of shown at viewport is still the same
|
||||
* (e.g. the document doesn't contain long lines and viewport width is expanded).
|
||||
* <p/>
|
||||
* We don't drop previously registered soft wraps during visible area change then but mark them as <code>'dirty'</code> in order to
|
||||
* drop when document content is being repainted.
|
||||
*/
|
||||
private boolean myDataIsDirty;
|
||||
|
||||
/** Holds number of 'active' calls, i.e. number of methods calls of the current object within the current call stack. */
|
||||
private int myActive;
|
||||
|
||||
public SoftWrapModelImpl(@NotNull EditorImpl editor) {
|
||||
myEditor = editor;
|
||||
}
|
||||
|
||||
public boolean isSoftWrappingEnabled() {
|
||||
return myEditor.getSettings().isUseSoftWraps();
|
||||
}
|
||||
|
||||
@Nullable
|
||||
public TextChange getSoftWrap(int offset) {
|
||||
if (myActive <= 0) {
|
||||
dropDataIfNecessary();
|
||||
}
|
||||
int i = getSoftWrapIndex(offset);
|
||||
return i >= 0 ? myWraps.get(i) : null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Tries to find index of the target soft wrap stored at {@link #myWraps} collection. <code>'Target'</code> soft wrap is the one
|
||||
* that starts at the given offset.
|
||||
*
|
||||
* @param offset target offset
|
||||
* @return index that conforms to {@link Collections#binarySearch(List, Object)} contract, i.e. non-negative returned
|
||||
* index points to soft wrap that starts at the given offset; <code>'-(negative value) - 1'</code> points
|
||||
* to position at {@link #myWraps} collection where soft wrap for the given index should be inserted
|
||||
*/
|
||||
private int getSoftWrapIndex(int offset) {
|
||||
TextChange searchKey = new TextChange("", offset);
|
||||
return Collections.binarySearch(myWraps, searchKey, SOFT_WRAPS_BY_OFFSET_COMPARATOR);
|
||||
}
|
||||
|
||||
private boolean hasSoftWrapAt(int offset) {
|
||||
return getSoftWrapIndex(offset) >= 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* Inserts given soft wrap to {@link #myWraps} collection at the given index.
|
||||
*
|
||||
* @param softWrap soft wrap to store
|
||||
* @return previous soft wrap object stored for the same offset if any; <code>null</code> otherwise
|
||||
*/
|
||||
@Nullable
|
||||
private TextChange storeSoftWrap(TextChange softWrap) {
|
||||
int i = Collections.binarySearch(myWraps, softWrap, SOFT_WRAPS_BY_OFFSET_COMPARATOR);
|
||||
if (i >= 0) {
|
||||
return myWraps.set(i, softWrap);
|
||||
}
|
||||
|
||||
i = -i - 1;
|
||||
myWraps.add(i, softWrap);
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Allows to answer if symbols of the given char array located at <code>[start; end)</code> interval should be soft wrapped,
|
||||
* i.e. represented on a next line.
|
||||
*
|
||||
* @param chars symbols holder
|
||||
* @param start target symbols sub-sequence start within the given char array (inclusive)
|
||||
* @param end target symbols sub-sequence end within the given char array (exclusive)
|
||||
* @param position current drawing position
|
||||
* @return <code>true</code> if target symbols sub-sequence should be soft-wrapped; <code>false</code> otherwise
|
||||
*/
|
||||
public boolean shouldWrap(char[] chars, int start, int end, Point position) {
|
||||
if (!isSoftWrappingEnabled()) {
|
||||
return false;
|
||||
}
|
||||
initIfNecessary();
|
||||
dropDataIfNecessary();
|
||||
|
||||
if (hasSoftWrapAt(start)) {
|
||||
return true;
|
||||
}
|
||||
|
||||
if (myRightEdgeLocation < 0) {
|
||||
assert false;
|
||||
return false;
|
||||
}
|
||||
|
||||
//TODO den implement
|
||||
return position.x + (end - start) * 7 > myRightEdgeLocation;
|
||||
}
|
||||
|
||||
private static boolean containsOnlyWhiteSpaces(char[] chars, int start, int end) {
|
||||
int i = CharArrayUtil.shiftForward(chars, start, "\n \t");
|
||||
return i >= end;
|
||||
}
|
||||
|
||||
/**
|
||||
* Asks current model to perform soft wrapping for the characters sub-sequent identified by the given parameters.
|
||||
*
|
||||
* @param chars symbols holder
|
||||
* @param start target symbols sub-sequence start within the given char array (inclusive)
|
||||
* @param end target symbols sub-sequence end within the given char array (exclusive)
|
||||
* @return soft wrap registered for the given offset
|
||||
*/
|
||||
public TextChange wrap(char[] chars, int start, int end) {
|
||||
TextChange result = getSoftWrap(start);
|
||||
if (result != null) {
|
||||
return result;
|
||||
}
|
||||
|
||||
dropDataIfNecessary();
|
||||
//TODO den implement indent calculation on formatting options
|
||||
result = containsOnlyWhiteSpaces(chars, start, end) ? new TextChange("\n", start) : new TextChange("\n ", start);
|
||||
storeSoftWrap(result);
|
||||
return result;
|
||||
}
|
||||
|
||||
public List<TextChange> getRegisteredSoftWraps() {
|
||||
return myWrapsView;
|
||||
}
|
||||
|
||||
/**
|
||||
* Drops information about registered soft wraps if they are marked as <code>'dirty'</code>.
|
||||
* //TODO den add doc about editor repainting
|
||||
*
|
||||
* @see #myDataIsDirty
|
||||
*/
|
||||
private void dropDataIfNecessary() {
|
||||
if (!myEditor.isUnderRepainting()) {
|
||||
return;
|
||||
}
|
||||
Document document = myEditor.getDocument();
|
||||
for (TIntIterator it = myDirtyLines.iterator(); it.hasNext();) {
|
||||
int line = it.next();
|
||||
int start = document.getLineStartOffset(line);
|
||||
int end = document.getLineEndOffset(line);
|
||||
|
||||
int startIndex = getSoftWrapIndex(start);
|
||||
if (startIndex < 0) {
|
||||
startIndex = -startIndex - 1;
|
||||
}
|
||||
int endIndex = startIndex;
|
||||
for (; endIndex < myWraps.size(); endIndex++) {
|
||||
TextChange softWrap = myWraps.get(endIndex);
|
||||
if (softWrap.getStart() >= end) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
myWraps.subList(startIndex, endIndex).clear();
|
||||
}
|
||||
myDirtyLines.clear();
|
||||
|
||||
if (!myDataIsDirty) {
|
||||
return;
|
||||
|
||||
}
|
||||
myDataIsDirty = false;
|
||||
myWraps.clear();
|
||||
}
|
||||
|
||||
@NotNull
|
||||
public LogicalPosition adjustLogicalPosition(@NotNull LogicalPosition defaultLogical, @NotNull VisualPosition visual) {
|
||||
if (myActive > 0 || !isSoftWrappingEnabled() || myWraps.isEmpty()) {
|
||||
return defaultLogical;
|
||||
}
|
||||
|
||||
//TODO den check
|
||||
//if (defaultLogical.softWrapAware) {
|
||||
// return defaultLogical;
|
||||
//}
|
||||
|
||||
updateVisibleAreaIfNecessary();
|
||||
myActive++;
|
||||
try {
|
||||
return doAdjustLogicalPosition(defaultLogical, visual);
|
||||
}
|
||||
finally {
|
||||
myActive--;
|
||||
}
|
||||
}
|
||||
|
||||
private LogicalPosition doAdjustLogicalPosition(LogicalPosition defaultLogical, VisualPosition visual) {
|
||||
DocumentImpl document = (DocumentImpl)myEditor.getDocument();
|
||||
int maxOffset = document.getLineEndOffset(Math.min(defaultLogical.line, document.getLineCount() - 1));
|
||||
|
||||
// This index points to registered soft wrap that is guaranteed to be located after the target visual line.
|
||||
int endIndex = getSoftWrapIndex(maxOffset + 1);
|
||||
if (endIndex < 0) {
|
||||
endIndex = -endIndex - 1;
|
||||
}
|
||||
|
||||
int softWrapIntroducedLineFeeds = 0;
|
||||
|
||||
FoldingModel foldingModel = myEditor.getFoldingModel();
|
||||
int i = 0;
|
||||
int max = Math.min(myWraps.size(), endIndex);
|
||||
for (; i < max; i++) {
|
||||
TextChange softWrap = myWraps.get(i);
|
||||
if (foldingModel.isOffsetCollapsed(softWrap.getStart())) {
|
||||
continue;
|
||||
}
|
||||
|
||||
int currentSoftWrapLineFeeds = StringUtil.countNewLines(softWrap.getText());
|
||||
int softWrapLine = document.getLineNumber(softWrap.getStart());
|
||||
int visualLineBeforeSoftWrapAppliance
|
||||
= myEditor.logicalToVisualPosition(new LogicalPosition(softWrapLine, 0)).line + softWrapIntroducedLineFeeds;
|
||||
if (visualLineBeforeSoftWrapAppliance > visual.line) {
|
||||
int logicalLine = defaultLogical.line - softWrapIntroducedLineFeeds;
|
||||
LogicalPosition foldingUnawarePosition
|
||||
= new LogicalPosition(logicalLine, defaultLogical.column, softWrapIntroducedLineFeeds, 0, 0, 0, 0);
|
||||
return adjustFoldingData(foldingModel, foldingUnawarePosition);
|
||||
}
|
||||
|
||||
int visualLineAfterSoftWrapAppliance = visualLineBeforeSoftWrapAppliance + currentSoftWrapLineFeeds;
|
||||
if (visualLineAfterSoftWrapAppliance < visual.line) {
|
||||
softWrapIntroducedLineFeeds += currentSoftWrapLineFeeds;
|
||||
continue;
|
||||
}
|
||||
|
||||
// If we're here that means that current soft wrap affects logical line that is matched to the given visual line.
|
||||
// We iterate from the logical line start then in order to calculate resulting logical position.
|
||||
Context context = new Context(defaultLogical, visual, softWrapIntroducedLineFeeds, visualLineBeforeSoftWrapAppliance, foldingModel);
|
||||
int startLineOffset = document.getLineStartOffset(softWrapLine);
|
||||
int endLineOffset = document.getLineEndOffset(softWrapLine);
|
||||
CharSequence documentText = document.getCharsNoThreadCheck();
|
||||
for (int j = startLineOffset; j < endLineOffset; j++) {
|
||||
|
||||
// Process soft wrap at the current offset if any.
|
||||
if (j == softWrap.getStart()) {
|
||||
CharSequence softWrapText = softWrap.getText();
|
||||
for (int k = 0; k < softWrapText.length(); k++) {
|
||||
LogicalPosition result = context.onSoftWrapSymbol(softWrapText.charAt(k));
|
||||
if (result != null) {
|
||||
return result;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Process document symbol.
|
||||
LogicalPosition result = context.onNonSoftWrapSymbol(documentText.charAt(j));
|
||||
if (result != null) {
|
||||
return result;
|
||||
}
|
||||
}
|
||||
|
||||
// If we are here that means that target visual position is located at virtual space after the line end.
|
||||
int logicalLine = defaultLogical.line - softWrapIntroducedLineFeeds - context.lineFeedsFromCurrentSoftWrap;
|
||||
int logicalColumn = context.symbolsOnCurrentLogicalLine + visual.column - context.symbolsOnCurrentVisualLine;
|
||||
int softWrapColumnDiff = visual.column - logicalColumn;
|
||||
LogicalPosition foldingUnawarePosition = new LogicalPosition(
|
||||
logicalLine, logicalColumn, softWrapIntroducedLineFeeds + context.lineFeedsFromCurrentSoftWrap,
|
||||
context.lineFeedsFromCurrentSoftWrap, softWrapColumnDiff, 0, 0
|
||||
);
|
||||
return adjustFoldingData(foldingModel, foldingUnawarePosition);
|
||||
}
|
||||
|
||||
// If we are here that means that there is no soft wrap on a logical line that corresponds to the target visual line.
|
||||
int logicalLine = defaultLogical.line - softWrapIntroducedLineFeeds;
|
||||
LogicalPosition foldingUnaware = new LogicalPosition(logicalLine, defaultLogical.column, softWrapIntroducedLineFeeds, 0, 0, 0, 0);
|
||||
return adjustFoldingData(foldingModel, foldingUnaware);
|
||||
}
|
||||
|
||||
/**
|
||||
* Builds folding-aware logical position on the basis of the given folding-unaware position and folding model
|
||||
*
|
||||
* @param foldingModel folding model to use for retrieving information about folding
|
||||
* @param position folding-unaware logical position
|
||||
* @return folding-aware logical position
|
||||
*/
|
||||
private LogicalPosition adjustFoldingData(FoldingModel foldingModel, LogicalPosition position) {
|
||||
int offset = myEditor.logicalPositionToOffset(position);
|
||||
int foldedLines = 0;
|
||||
int foldColumnDiff = 0;
|
||||
int softWrapColumnDiff = position.softWrapColumnDiff;
|
||||
DocumentImpl document = (DocumentImpl)myEditor.getDocument();
|
||||
CharSequence text = document.getCharsNoThreadCheck();
|
||||
for (FoldRegion foldRegion : foldingModel.getAllFoldRegions()) {
|
||||
if (foldRegion.getStartOffset() >= offset) {
|
||||
break;
|
||||
}
|
||||
|
||||
if (foldRegion.isExpanded() || !foldRegion.isValid()) {
|
||||
continue;
|
||||
}
|
||||
|
||||
int foldingStartLine = document.getLineNumber(foldRegion.getStartOffset());
|
||||
int foldingEndLine = document.getLineNumber(foldRegion.getEndOffset());
|
||||
foldedLines += foldingEndLine - foldingStartLine;
|
||||
|
||||
// Process situation when target offset is located inside the folded region.
|
||||
if (offset >= foldRegion.getStartOffset() && offset < foldRegion.getEndOffset()) {
|
||||
// Our purpose is to define folding data in order to point to the visual folding start.
|
||||
int visualFoldingStartColumn = calculateVisualFoldingStartColumn(foldRegion);
|
||||
foldColumnDiff = visualFoldingStartColumn - position.column - softWrapColumnDiff;
|
||||
break;
|
||||
}
|
||||
|
||||
if (foldingEndLine != position.line) {
|
||||
continue;
|
||||
}
|
||||
|
||||
// We know here that offset is at the same line where folding ends and is located after it. Hence, we process that as follows:
|
||||
// 1. Check if the folding is single-line;
|
||||
// 2.1. Process as follows if the folding is single-line:
|
||||
// 3.1. Calculate column difference introduced by the folding;
|
||||
// 2.2. Process as follows if the folding is multi-line:
|
||||
// 3.2. Calculate visual column of folding start;
|
||||
// 4.2. Calculate number of columns between target offset and folding end;
|
||||
// 5.1. Calculate folding placeholder width in columns;
|
||||
// 6.1. Calculate resulting offset visual column;
|
||||
// 7.1. Calculate resulting folding column diff;
|
||||
|
||||
if (foldingStartLine == foldingEndLine) {
|
||||
foldColumnDiff = toVisualColumnSymbolsNumber(foldRegion.getPlaceholderText())
|
||||
- toVisualColumnSymbolsNumber(text, foldRegion.getStartOffset(), foldRegion.getEndOffset());
|
||||
}
|
||||
else {
|
||||
int endOffsetOfLineWithFoldingEnd = document.getLineEndOffset(foldingEndLine);
|
||||
int columnsBetweenFoldingEndAndOffset = toVisualColumnSymbolsNumber(text, foldRegion.getEndOffset(), endOffsetOfLineWithFoldingEnd);
|
||||
if (position.column > endOffsetOfLineWithFoldingEnd) {
|
||||
columnsBetweenFoldingEndAndOffset += position.column - endOffsetOfLineWithFoldingEnd;
|
||||
}
|
||||
int visualFoldingStartColumn = calculateVisualFoldingStartColumn(foldRegion);
|
||||
int foldingPlaceholderWidth = toVisualColumnSymbolsNumber(foldRegion.getPlaceholderText());
|
||||
int visual = columnsBetweenFoldingEndAndOffset + visualFoldingStartColumn + foldingPlaceholderWidth;
|
||||
foldColumnDiff = visual - position.column;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
return new LogicalPosition(
|
||||
position.line, position.column, position.softWrapLines, position.linesFromActiveSoftWrap,
|
||||
softWrapColumnDiff, foldedLines, foldColumnDiff
|
||||
);
|
||||
}
|
||||
|
||||
private int calculateVisualFoldingStartColumn(FoldRegion region) {
|
||||
DocumentImpl document = (DocumentImpl)myEditor.getDocument();
|
||||
int foldingStartOffset = region.getStartOffset();
|
||||
int logicalLine = document.getLineNumber(foldingStartOffset);
|
||||
int logicalLineStartOffset = document.getLineStartOffset(logicalLine);
|
||||
|
||||
int softWrapIndex = getSoftWrapIndex(logicalLineStartOffset);
|
||||
if (softWrapIndex < 0) {
|
||||
softWrapIndex = -softWrapIndex - 1;
|
||||
}
|
||||
|
||||
int startOffsetOfVisualLineWithFoldingStart = logicalLineStartOffset;
|
||||
for (; softWrapIndex < myWraps.size(); softWrapIndex++) {
|
||||
TextChange softWrap = myWraps.get(softWrapIndex);
|
||||
if (softWrap.getStart() >= foldingStartOffset) {
|
||||
break;
|
||||
}
|
||||
|
||||
startOffsetOfVisualLineWithFoldingStart = softWrap.getStart();
|
||||
}
|
||||
|
||||
assert startOffsetOfVisualLineWithFoldingStart <= foldingStartOffset;
|
||||
return toVisualColumnSymbolsNumber(document.getCharsNoThreadCheck(), startOffsetOfVisualLineWithFoldingStart, foldingStartOffset);
|
||||
}
|
||||
|
||||
@NotNull
|
||||
public LogicalPosition offsetToLogicalPosition(int offset) {
|
||||
if (myActive > 0 || !isSoftWrappingEnabled()) {
|
||||
return myEditor.offsetToLogicalPosition(offset, false);
|
||||
}
|
||||
|
||||
myActive++;
|
||||
try {
|
||||
return doOffsetToLogicalPosition(offset);
|
||||
} finally {
|
||||
myActive--;
|
||||
}
|
||||
}
|
||||
|
||||
@SuppressWarnings({"AssignmentToForLoopParameter"})
|
||||
private LogicalPosition doOffsetToLogicalPosition(int offset) {
|
||||
DocumentImpl document = (DocumentImpl)myEditor.getDocument();
|
||||
CharSequence chars = document.getCharsNoThreadCheck();
|
||||
|
||||
int targetLine = document.getLineNumber(offset);
|
||||
int targetLineStartOffset = document.getLineStartOffset(targetLine);
|
||||
|
||||
updateVisibleAreaIfNecessary();
|
||||
|
||||
int softWrapIntroducedLines = 0;
|
||||
int linesFromCurrentSoftWrap = 0;
|
||||
int symbolsOnCurrentLogicalLine = 0;
|
||||
int symbolsOnCurrentVisibleLine = 0;
|
||||
|
||||
// Retrieve information about logical position that is soft-wraps unaware.
|
||||
LogicalPosition rawLineStartLogicalPosition = myEditor.offsetToLogicalPosition(targetLineStartOffset, false);
|
||||
|
||||
// Calculate number of soft wrap-introduced lines before the line that holds target offset.
|
||||
int index = getSoftWrapIndex(targetLineStartOffset);
|
||||
if (index < 0) {
|
||||
index = -index - 1;
|
||||
}
|
||||
int max = Math.min(index, myWraps.size());
|
||||
for (int j = 0; j < max; j++) {
|
||||
softWrapIntroducedLines += StringUtil.countNewLines(myWraps.get(j).getText());
|
||||
}
|
||||
|
||||
FoldingModel foldingModel = myEditor.getFoldingModel();
|
||||
|
||||
// Return eagerly if there are no soft wraps before the target offset on a line that contains it.
|
||||
if (max >= myWraps.size() || myWraps.get(max).getStart() > offset) {
|
||||
LogicalPosition foldingUnawarePosition = new LogicalPosition(
|
||||
rawLineStartLogicalPosition.line, offset - targetLineStartOffset, softWrapIntroducedLines, 0, 0, 0, 0
|
||||
);
|
||||
return adjustFoldingData(foldingModel, foldingUnawarePosition);
|
||||
}
|
||||
|
||||
// Calculate number of lines and columns introduced by soft wrap located at the line that holds target offset if any.
|
||||
|
||||
|
||||
// We add '1' here in order to correctly process situation when there is soft wrap at target offset (it impacts resulting logical
|
||||
// position but document symbol at that offset should not be count).
|
||||
max = Math.min(chars.length(), offset + 1);
|
||||
|
||||
for (int i = targetLineStartOffset; i < max; i++) {
|
||||
FoldRegion region = foldingModel.getCollapsedRegionAtOffset(i);
|
||||
if (region != null) {
|
||||
// Assuming that folded region placeholder doesn't contain line feed symbols.
|
||||
i = region.getEndOffset();
|
||||
symbolsOnCurrentVisibleLine += region.getPlaceholderText().length();
|
||||
continue;
|
||||
}
|
||||
|
||||
TextChange softWrap = getSoftWrap(i);
|
||||
if (softWrap != null) {
|
||||
CharSequence softWrapText = softWrap.getText();
|
||||
for (int j = 0; j < softWrapText.length(); j++) {
|
||||
if (softWrapText.charAt(j) == '\n') {
|
||||
softWrapIntroducedLines++;
|
||||
linesFromCurrentSoftWrap++;
|
||||
symbolsOnCurrentVisibleLine = 0;
|
||||
}
|
||||
else {
|
||||
symbolsOnCurrentVisibleLine++;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// We don't want to count symbol at target offset.
|
||||
if (i == offset) {
|
||||
break;
|
||||
}
|
||||
|
||||
// Assuming that no line feed is contained before target offset on a line that holds it.
|
||||
symbolsOnCurrentLogicalLine++;
|
||||
symbolsOnCurrentVisibleLine++;
|
||||
}
|
||||
|
||||
LogicalPosition foldingUnawarePosition = new LogicalPosition(
|
||||
rawLineStartLogicalPosition.line, symbolsOnCurrentLogicalLine, softWrapIntroducedLines, linesFromCurrentSoftWrap,
|
||||
symbolsOnCurrentVisibleLine - symbolsOnCurrentLogicalLine, 0, 0
|
||||
);
|
||||
return adjustFoldingData(foldingModel, foldingUnawarePosition);
|
||||
}
|
||||
|
||||
@NotNull
|
||||
public VisualPosition adjustVisualPosition(@NotNull LogicalPosition logical, @NotNull VisualPosition defaultVisual) {
|
||||
if (myActive > 0 || !isSoftWrappingEnabled() || myWraps.isEmpty()) {
|
||||
return defaultVisual;
|
||||
}
|
||||
|
||||
if (logical.visualPositionAware) {
|
||||
// We don't need to recalculate logical position adjustments because given object already has them.
|
||||
return logical.toVisualPosition();
|
||||
}
|
||||
|
||||
updateVisibleAreaIfNecessary();
|
||||
myActive++;
|
||||
try {
|
||||
return doAdjustVisualPosition(logical, defaultVisual);
|
||||
}
|
||||
finally {
|
||||
myActive--;
|
||||
}
|
||||
}
|
||||
|
||||
@NotNull
|
||||
private VisualPosition doAdjustVisualPosition(LogicalPosition logical, VisualPosition visual) {
|
||||
// Check if there are registered soft wraps before the target logical position.
|
||||
int maxOffset = myEditor.logicalPositionToOffset(logical);
|
||||
int endIndex = getSoftWrapIndex(maxOffset);
|
||||
if (endIndex < 0) {
|
||||
endIndex = -endIndex - 2; // We subtract '2' instead of '1' here in order to point to offset of the first soft wrap the
|
||||
// is located before the given logical position.
|
||||
}
|
||||
|
||||
// Return eagerly if no soft wraps are registered before the target offset.
|
||||
if (endIndex < 0 || endIndex >= myWraps.size()) {
|
||||
return visual;
|
||||
}
|
||||
|
||||
int lineDiff = 0;
|
||||
int column = -1;
|
||||
|
||||
FoldingModel foldingModel = myEditor.getFoldingModel();
|
||||
int targetLogicalLineStartOffset = myEditor.logicalPositionToOffset(new LogicalPosition(logical.line, 0));
|
||||
for (int i = endIndex; i >= 0; i--) {
|
||||
TextChange softWrap = myWraps.get(i);
|
||||
if (softWrap == null) {
|
||||
assert false;
|
||||
continue;
|
||||
}
|
||||
|
||||
if (foldingModel.isOffsetCollapsed(softWrap.getStart())) {
|
||||
continue;
|
||||
}
|
||||
|
||||
CharSequence softWrapText = softWrap.getText();
|
||||
int softWrapLines = StringUtil.countNewLines(softWrapText);
|
||||
|
||||
// Count lines introduced by the current soft wrap. We assume that the soft wrap is located before target offset,
|
||||
// hence, we're free to count all of its line feeds.
|
||||
lineDiff += softWrapLines;
|
||||
|
||||
// Count soft wrap column offset only if it's located at the same line as the target offset.
|
||||
if (softWrapLines > 0 && softWrap.getStart() >= targetLogicalLineStartOffset) {
|
||||
for (int j = softWrapText.length() - 1; j >= 0; j--) {
|
||||
if (softWrapText.charAt(j) == '\n') {
|
||||
column = maxOffset - softWrap.getStart() - j + 1;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
int columnToUse = column >= 0 ? column : visual.column;
|
||||
return new VisualPosition(visual.line + lineDiff, columnToUse);
|
||||
}
|
||||
|
||||
public void beforeDocumentChange(@NotNull VisualPosition visualPosition) {
|
||||
LogicalPosition logicalPosition = myEditor.visualToLogicalPosition(visualPosition);
|
||||
int offset = myEditor.logicalPositionToOffset(logicalPosition);
|
||||
int i = getSoftWrapIndex(offset);
|
||||
if (i < 0 || i >= myWraps.size()) {
|
||||
return;
|
||||
}
|
||||
|
||||
TextChange softWrap = myWraps.get(i);
|
||||
|
||||
VisualPosition visualCaretPosition = myEditor.getCaretModel().getVisualPosition();
|
||||
|
||||
// Consider given visual position to belong to soft wrap-introduced virtual space if visual position for the target offset
|
||||
// differs from the given.
|
||||
if (!visualPosition.equals(myEditor.offsetToVisualPosition(offset))) {
|
||||
myEditor.getDocument().replaceString(softWrap.getStart(), softWrap.getEnd(), softWrap.getText());
|
||||
}
|
||||
|
||||
// Restore caret position.
|
||||
myEditor.getCaretModel().moveToVisualPosition(visualCaretPosition);
|
||||
myWraps.remove(i);
|
||||
}
|
||||
|
||||
/**
|
||||
* //TODO den add doc
|
||||
*
|
||||
* @param change change introduced to the document
|
||||
*/
|
||||
private void updateRegisteredSoftWraps(TextChange change) {
|
||||
int softWrapIndex = getSoftWrapIndex(change.getStart());
|
||||
if (softWrapIndex < 0) {
|
||||
softWrapIndex = -softWrapIndex - 1;
|
||||
}
|
||||
|
||||
if (softWrapIndex >= myWraps.size()) {
|
||||
return;
|
||||
}
|
||||
|
||||
Document document = myEditor.getDocument();
|
||||
int firstChangedLine = document.getLineNumber(change.getStart());
|
||||
int lastChangedLine = Math.max(document.getLineNumber(change.getEnd()), firstChangedLine + StringUtil.countNewLines(change.getText()));
|
||||
for (int i = firstChangedLine; i <= lastChangedLine; i++) {
|
||||
myDirtyLines.add(i);
|
||||
}
|
||||
|
||||
// Collect soft wraps which offsets should be modified.
|
||||
List<TextChange> modified = myWraps.subList(softWrapIndex, myWraps.size());
|
||||
List<TextChange> toModify = new ArrayList<TextChange>(modified);
|
||||
modified.clear();
|
||||
|
||||
// Add modified soft wraps.
|
||||
for (TextChange softWrap : toModify) {
|
||||
myWraps.add(softWrap.advance(change.getDiff()));
|
||||
}
|
||||
}
|
||||
|
||||
private void initIfNecessary() {
|
||||
if (myInitialized) {
|
||||
return;
|
||||
}
|
||||
myInitialized = true;
|
||||
|
||||
// Subscribe for visible area changes notifications.
|
||||
ScrollingModel scrollingModel = myEditor.getScrollingModel();
|
||||
scrollingModel.addVisibleAreaListener(new VisibleAreaListener() {
|
||||
public void visibleAreaChanged(VisibleAreaEvent e) {
|
||||
updateVisibleAreaIfNecessary(e.getNewRectangle());
|
||||
}
|
||||
});
|
||||
updateVisibleAreaIfNecessary(scrollingModel.getVisibleArea());
|
||||
|
||||
// Subscribe for document change events.
|
||||
myEditor.getDocument().addDocumentListener(new DocumentListener() {
|
||||
public void beforeDocumentChange(DocumentEvent event) {
|
||||
}
|
||||
|
||||
public void documentChanged(DocumentEvent event) {
|
||||
myActive++;
|
||||
try {
|
||||
updateRegisteredSoftWraps(new TextChange(event.getNewFragment(), event.getOffset(), event.getOffset() + event.getOldLength()));
|
||||
}
|
||||
finally {
|
||||
myActive--;
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
private void updateVisibleAreaIfNecessary() {
|
||||
updateVisibleAreaIfNecessary(myEditor.getScrollingModel().getVisibleArea());
|
||||
}
|
||||
|
||||
private void updateVisibleAreaIfNecessary(@Nullable Rectangle visibleArea) {
|
||||
if (visibleArea == null || myActive > 0 || !isSoftWrappingEnabled()) {
|
||||
return;
|
||||
}
|
||||
myActive++;
|
||||
try {
|
||||
doUpdateVisibleAreaChange(visibleArea);
|
||||
}
|
||||
finally {
|
||||
myActive--;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Notifies current model that visible rectangle is changed in order for it to update the state accordingly.
|
||||
* <p/>
|
||||
* This method is introduced mostly for business-logic vs try/finally separation.
|
||||
*
|
||||
* @param visibleArea current visible area of the editor managed by the current model
|
||||
*/
|
||||
private void doUpdateVisibleAreaChange(@NotNull Rectangle visibleArea) {
|
||||
// Update right edge.
|
||||
int currentRightEdgeLocation = visibleArea.x + visibleArea.width;
|
||||
if (myRightEdgeLocation != currentRightEdgeLocation) {
|
||||
myDataIsDirty = true;
|
||||
myRightEdgeLocation = currentRightEdgeLocation;
|
||||
}
|
||||
}
|
||||
|
||||
private int toVisualColumnSymbolsNumber(char c) {
|
||||
myCharBuffer.clear();
|
||||
myCharBuffer.put(c);
|
||||
myCharBuffer.flip();
|
||||
return toVisualColumnSymbolsNumber(myCharBuffer, 0, 1);
|
||||
}
|
||||
|
||||
private int toVisualColumnSymbolsNumber(CharSequence text) {
|
||||
return toVisualColumnSymbolsNumber(text, 0, text.length());
|
||||
}
|
||||
|
||||
private int toVisualColumnSymbolsNumber(CharSequence text, int start, int end) {
|
||||
return EditorUtil.calcColumnNumber(myEditor, text, start, end, EditorUtil.getTabSize(myEditor));
|
||||
}
|
||||
|
||||
private class Context {
|
||||
|
||||
public final FoldingModel foldingModel;
|
||||
public final LogicalPosition softWrapUnawareLogicalPosition;
|
||||
public final VisualPosition targetVisualPosition;
|
||||
public final int softWrapIntroducedLines;
|
||||
public final int visualLineBeforeSoftWrapAppliance;
|
||||
public int lineFeedsFromCurrentSoftWrap;
|
||||
public int symbolsOnCurrentLogicalLine;
|
||||
public int symbolsOnCurrentVisualLine;
|
||||
|
||||
Context(LogicalPosition softWrapUnawareLogicalPosition, VisualPosition targetVisualPosition, int softWrapIntroducedLines,
|
||||
int visualLineBeforeSoftWrapAppliance, FoldingModel foldingModel)
|
||||
{
|
||||
this.softWrapUnawareLogicalPosition = softWrapUnawareLogicalPosition;
|
||||
this.targetVisualPosition = targetVisualPosition;
|
||||
this.softWrapIntroducedLines = softWrapIntroducedLines;
|
||||
this.visualLineBeforeSoftWrapAppliance = visualLineBeforeSoftWrapAppliance;
|
||||
this.foldingModel = foldingModel;
|
||||
}
|
||||
|
||||
/**
|
||||
* Updates current context within the soft wrap symbol.
|
||||
*
|
||||
* @param c soft wrap symbol to process
|
||||
* @return logical position that matches target visual position if given symbol processing makes it possible to calculate it;
|
||||
* <code>null</code> otherwise
|
||||
*/
|
||||
@Nullable
|
||||
public LogicalPosition onSoftWrapSymbol(char c) {
|
||||
// Process line feed inside soft wrap.
|
||||
if (c == '\n') {
|
||||
if (targetVisualPosition.line == visualLineBeforeSoftWrapAppliance + lineFeedsFromCurrentSoftWrap) {
|
||||
return build(targetVisualPosition.column - symbolsOnCurrentLogicalLine);
|
||||
}
|
||||
else {
|
||||
lineFeedsFromCurrentSoftWrap++;
|
||||
symbolsOnCurrentVisualLine = 0;
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
// Just update information about tracked symbols number if current visual line is too low.
|
||||
if (targetVisualPosition.line > visualLineBeforeSoftWrapAppliance + lineFeedsFromCurrentSoftWrap) {
|
||||
symbolsOnCurrentVisualLine += toVisualColumnSymbolsNumber(c);
|
||||
}
|
||||
|
||||
// There is a possible case that, for example, target visual column is zero and it points to the soft-wrapped line,
|
||||
// i.e. soft wrap are. We shouldn't count symbols then. Hence, we perform this preliminary examination with eager
|
||||
// return if necessary.
|
||||
if (targetVisualPosition.column <= symbolsOnCurrentVisualLine) {
|
||||
return build();
|
||||
}
|
||||
|
||||
// Process non-line feed inside soft wrap.
|
||||
symbolsOnCurrentVisualLine += toVisualColumnSymbolsNumber(c);
|
||||
if (targetVisualPosition.column <= symbolsOnCurrentVisualLine) {
|
||||
return build();
|
||||
}
|
||||
else {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Updates current context within the non-soft wrap symbol.
|
||||
*
|
||||
* @param c soft wrap symbol to process
|
||||
* @return logical position that matches target visual position if given symbol processing makes it possible to calculate it;
|
||||
* <code>null</code> otherwise
|
||||
*/
|
||||
@Nullable
|
||||
public LogicalPosition onNonSoftWrapSymbol(char c) {
|
||||
// Don't expect line feed symbol to be delivered to this method in assumption that we process only one logical line here.
|
||||
if (c == '\n') {
|
||||
assert false;
|
||||
return null;
|
||||
}
|
||||
|
||||
// Just update information about tracked symbols number if current visual line is too low.
|
||||
if (targetVisualPosition.line > visualLineBeforeSoftWrapAppliance + lineFeedsFromCurrentSoftWrap) {
|
||||
symbolsOnCurrentVisualLine += toVisualColumnSymbolsNumber(c);
|
||||
symbolsOnCurrentLogicalLine++;
|
||||
return null;
|
||||
}
|
||||
|
||||
// There is a possible case that, for example, target visual column is zero. We shouldn't count symbols then.
|
||||
// Hence, we perform this preliminary examination with eager return if necessary.
|
||||
if (targetVisualPosition.column <= symbolsOnCurrentVisualLine) {
|
||||
return build();
|
||||
}
|
||||
|
||||
symbolsOnCurrentVisualLine += toVisualColumnSymbolsNumber(c);
|
||||
symbolsOnCurrentLogicalLine++;
|
||||
|
||||
|
||||
if (targetVisualPosition.column <= symbolsOnCurrentVisualLine) {
|
||||
return build();
|
||||
}
|
||||
else {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
private LogicalPosition build() {
|
||||
return build(symbolsOnCurrentVisualLine - symbolsOnCurrentLogicalLine);
|
||||
}
|
||||
|
||||
private LogicalPosition build(int softWrapColumnDiff) {
|
||||
int logicalLine = softWrapUnawareLogicalPosition.line - softWrapIntroducedLines - lineFeedsFromCurrentSoftWrap;
|
||||
LogicalPosition foldingUnawareResult = new LogicalPosition(
|
||||
logicalLine, symbolsOnCurrentLogicalLine, softWrapIntroducedLines + lineFeedsFromCurrentSoftWrap, lineFeedsFromCurrentSoftWrap,
|
||||
softWrapColumnDiff, 0, 0
|
||||
);
|
||||
return adjustFoldingData(foldingModel, foldingUnawareResult);
|
||||
}
|
||||
}
|
||||
}
|
||||
+7
@@ -43,6 +43,7 @@ public class TextComponentEditor extends UserDataHolderBase implements Editor {
|
||||
private final TextComponentCaretModel myCaretModel;
|
||||
private final TextComponentSelectionModel mySelectionModel;
|
||||
private final TextComponentScrollingModel myScrollingModel;
|
||||
private final TextComponentSoftWrapModel mySoftWrapModel;
|
||||
private EditorSettings mySettings;
|
||||
|
||||
public TextComponentEditor(final Project project, final JTextComponent textComponent) {
|
||||
@@ -57,6 +58,7 @@ public class TextComponentEditor extends UserDataHolderBase implements Editor {
|
||||
myCaretModel = new TextComponentCaretModel(textComponent, this);
|
||||
mySelectionModel = new TextComponentSelectionModel(textComponent, this);
|
||||
myScrollingModel = new TextComponentScrollingModel(textComponent);
|
||||
mySoftWrapModel = new TextComponentSoftWrapModel();
|
||||
}
|
||||
|
||||
@NotNull
|
||||
@@ -103,6 +105,11 @@ public class TextComponentEditor extends UserDataHolderBase implements Editor {
|
||||
return myCaretModel;
|
||||
}
|
||||
|
||||
@NotNull
|
||||
public SoftWrapModel getSoftWrapModel() {
|
||||
return mySoftWrapModel;
|
||||
}
|
||||
|
||||
@NotNull
|
||||
public EditorSettings getSettings() {
|
||||
if (mySettings == null) {
|
||||
|
||||
+15
-17
@@ -13,29 +13,27 @@
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package com.intellij.openapi.editor.colors.impl;
|
||||
package com.intellij.openapi.editor.textarea;
|
||||
|
||||
import com.intellij.openapi.util.SystemInfo;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
import com.intellij.openapi.editor.SoftWrapModel;
|
||||
import com.intellij.openapi.editor.TextChange;
|
||||
import com.intellij.openapi.editor.VisualPosition;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
|
||||
/**
|
||||
* @author Roman.Chernyatchik
|
||||
* @author Denis Zhdanov
|
||||
* @since Jun 22, 2010 5:56:23 PM
|
||||
*/
|
||||
public class MacOSDefaultColorSchemeExtension implements BundledColorSchemesProvider{
|
||||
@Override
|
||||
@Nullable
|
||||
public String[] getBundledSchemesRelativePaths() {
|
||||
public class TextComponentSoftWrapModel implements SoftWrapModel {
|
||||
|
||||
public boolean isSoftWrappingEnabled() {
|
||||
return false;
|
||||
}
|
||||
|
||||
public TextChange getSoftWrap(int offset) {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
@Nullable
|
||||
public String getDefaultSchemaExtensionPath() {
|
||||
|
||||
// Extends default color scheme settings on MacOS
|
||||
if (!SystemInfo.isMac) {
|
||||
return null;
|
||||
}
|
||||
return "colorSchemes/MacOSDefaultExt";
|
||||
public void beforeDocumentChange(@NotNull VisualPosition position) {
|
||||
}
|
||||
}
|
||||
@@ -27,10 +27,10 @@ import com.intellij.openapi.editor.event.CaretEvent;
|
||||
import com.intellij.openapi.editor.event.CaretListener;
|
||||
import com.intellij.openapi.fileEditor.FileEditorManager;
|
||||
import com.intellij.openapi.fileEditor.FileEditorManagerAdapter;
|
||||
import com.intellij.openapi.fileEditor.FileEditorManagerEvent;
|
||||
import com.intellij.openapi.fileEditor.FileEditorManagerListener;
|
||||
import com.intellij.openapi.fileEditor.ex.IdeDocumentHistory;
|
||||
import com.intellij.openapi.project.Project;
|
||||
import com.intellij.openapi.vfs.VirtualFile;
|
||||
import com.intellij.openapi.wm.StatusBar;
|
||||
import com.intellij.openapi.wm.StatusBarWidget;
|
||||
import com.intellij.ui.UIBundle;
|
||||
@@ -49,8 +49,8 @@ public class PositionPanel implements StatusBarWidget, StatusBarWidget.TextPrese
|
||||
public PositionPanel(@NotNull final Project project) {
|
||||
project.getMessageBus().connect().subscribe(FileEditorManagerListener.FILE_EDITOR_MANAGER, new FileEditorManagerAdapter() {
|
||||
@Override
|
||||
public void fileOpened(@NotNull final FileEditorManager source, @NotNull final VirtualFile file) {
|
||||
final Editor editor = source.getSelectedTextEditor();
|
||||
public void selectionChanged(final FileEditorManagerEvent event) {
|
||||
final Editor editor = getEditor();
|
||||
if (editor != null) updatePosition(editor);
|
||||
}
|
||||
});
|
||||
@@ -140,7 +140,11 @@ public class PositionPanel implements StatusBarWidget, StatusBarWidget.TextPrese
|
||||
}
|
||||
|
||||
public void caretPositionChanged(final CaretEvent e) {
|
||||
updatePosition(e.getEditor());
|
||||
final Editor editor = e.getEditor();
|
||||
Project project = editor.getProject();
|
||||
if (project != null && FileEditorManager.getInstance(project).getSelectedTextEditor() == e.getEditor()) {
|
||||
updatePosition(editor);
|
||||
}
|
||||
}
|
||||
|
||||
private void updatePosition(final Editor editor) {
|
||||
|
||||
@@ -298,6 +298,7 @@ radio.close.less.frequently.used.files=Close less frequently used files
|
||||
radio.close.non.modified.files.first=Close non-modified files first
|
||||
label.when.number.of.opened.editors.exceeds.tab.limit=<html>When number of opened editors exceeds tab limit:</html>
|
||||
group.virtual.space=Virtual Space
|
||||
checkbox.use.soft.wraps=Use soft wraps
|
||||
checkbox.allow.placement.of.caret.after.end.of.line=Allow placement of caret after end of line
|
||||
checkbox.allow.placement.of.caret.inside.tabs=Allow placement of caret inside tabs
|
||||
checkbox.show.virtual.space.at.file.bottom=Show virtual space at file bottom
|
||||
|
||||
@@ -121,8 +121,6 @@
|
||||
<extensionPoint name="fileType.fileViewProviderFactory" beanClass="com.intellij.openapi.fileTypes.FileTypeExtensionPoint"/>
|
||||
<extensionPoint name="multiLangCommenter"
|
||||
interface="com.intellij.psi.templateLanguages.MultipleLangCommentProvider"/>
|
||||
<extensionPoint name="selfManagingCommenter"
|
||||
interface="com.intellij.codeInsight.generation.SelfManagingCommenter"/>
|
||||
|
||||
<extensionPoint name="cacheBuilder"
|
||||
beanClass="com.intellij.lang.cacheBuilder.CacheBuilderEP"/>
|
||||
|
||||
@@ -197,6 +197,5 @@
|
||||
<projectConfigurable implementation="com.intellij.javaee.ExternalResourceConfigurable"/>
|
||||
|
||||
<bundledKeymapProvider implementation="com.intellij.openapi.keymap.impl.DefaultBundledKeymaps"/>
|
||||
<bundledColorSchemesProvider implementation="com.intellij.openapi.editor.colors.impl.MacOSDefaultColorSchemeExtension"/>
|
||||
<!-- <checkinHandlerFactory implementation="com.intellij.openapi.vcs.CheckRemoteStatusCheckinHandlerFactory"/> -->
|
||||
</extensions>
|
||||
|
||||
@@ -1,15 +0,0 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<scheme name="Default" version="1">
|
||||
<attributes>
|
||||
<option name="TYPO">
|
||||
<value>
|
||||
<option name="FOREGROUND" />
|
||||
<option name="BACKGROUND" />
|
||||
<option name="FONT_TYPE" value="0" />
|
||||
<option name="EFFECT_COLOR" value="ff4d4d" />
|
||||
<option name="EFFECT_TYPE" value="5" />
|
||||
<option name="ERROR_STRIPE_COLOR" />
|
||||
</value>
|
||||
</option>
|
||||
</attributes>
|
||||
</scheme>
|
||||
@@ -16,6 +16,7 @@
|
||||
package com.intellij.util.text;
|
||||
|
||||
import com.intellij.openapi.util.TextRange;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
import java.io.ByteArrayOutputStream;
|
||||
import java.io.IOException;
|
||||
@@ -27,6 +28,9 @@ import java.util.List;
|
||||
public class CharArrayUtil {
|
||||
private static final int GET_CHARS_THRESHOLD = 10;
|
||||
|
||||
private CharArrayUtil() {
|
||||
}
|
||||
|
||||
public static void getChars(CharSequence src, char[] dst, int dstOffset) {
|
||||
getChars(src, dst, dstOffset, src.length());
|
||||
}
|
||||
@@ -66,11 +70,12 @@ public class CharArrayUtil {
|
||||
public static char[] fromSequenceStrict(CharSequence seq) {
|
||||
char[] chars = fromSequence(seq);
|
||||
if (seq.length() == chars.length) return chars;
|
||||
char[] strictchars = new char[seq.length()];
|
||||
System.arraycopy(chars, 0, strictchars, 0, seq.length());
|
||||
return strictchars;
|
||||
char[] strictChars = new char[seq.length()];
|
||||
System.arraycopy(chars, 0, strictChars, 0, seq.length());
|
||||
return strictChars;
|
||||
}
|
||||
|
||||
@Nullable
|
||||
public static char[] fromSequenceWithoutCopying(CharSequence seq) {
|
||||
if (seq instanceof CharSequenceBackedByArray) {
|
||||
return ((CharSequenceBackedByArray)seq).getChars();
|
||||
@@ -94,10 +99,11 @@ public class CharArrayUtil {
|
||||
if (seq instanceof CharBuffer) {
|
||||
final CharBuffer buffer = (CharBuffer)seq;
|
||||
if (buffer.hasArray() && !buffer.isReadOnly() && buffer.arrayOffset() == 0) {
|
||||
final char[] bufArray = buffer.array();
|
||||
/* return larger array. Clients may use seq.length() to calculate correct processing range.
|
||||
if (bufArray.length == seq.length())
|
||||
*/ return bufArray;
|
||||
return buffer.array();
|
||||
// final char[] bufArray = buffer.array();
|
||||
// return larger array. Clients may use seq.length() to calculate correct processing range.
|
||||
// if (bufArray.length == seq.length())
|
||||
// return bufArray;
|
||||
}
|
||||
|
||||
char[] chars = new char[seq.length()];
|
||||
@@ -180,9 +186,11 @@ public class CharArrayUtil {
|
||||
return shiftBackward(new CharArrayCharSequence(buffer), offset, chars);
|
||||
}
|
||||
|
||||
public static int shiftForwardUntil(char[] buffer, int offset, String chars) {
|
||||
return shiftForwardUntil(new CharArrayCharSequence(buffer), offset, chars);
|
||||
}
|
||||
//Commented in order to apply to green code policy as the method is unused.
|
||||
//
|
||||
//public static int shiftForwardUntil(char[] buffer, int offset, String chars) {
|
||||
// return shiftForwardUntil(new CharArrayCharSequence(buffer), offset, chars);
|
||||
//}
|
||||
|
||||
public static int shiftForwardUntil(CharSequence buffer, int offset, String chars) {
|
||||
while (true) {
|
||||
@@ -198,10 +206,37 @@ public class CharArrayUtil {
|
||||
return offset;
|
||||
}
|
||||
|
||||
public static int shiftBackwardUntil(char[] buffer, int offset, String chars) {
|
||||
return shiftBackwardUntil(new CharArrayCharSequence(buffer), offset, chars);
|
||||
}
|
||||
//Commented in order to apply to the green code policy as the method is unused.
|
||||
//
|
||||
//public static int shiftBackwardUntil(char[] buffer, int offset, String chars) {
|
||||
// return shiftBackwardUntil(new CharArrayCharSequence(buffer), offset, chars);
|
||||
//}
|
||||
|
||||
/**
|
||||
* Calculates offset that points to the given buffer and has the following characteristics:
|
||||
* <p/>
|
||||
* <ul>
|
||||
* <li>is less than or equal to the given offset;</li>
|
||||
* <li>
|
||||
* it's guaranteed that all symbols of the given buffer that are located at <code>(returned offset; given offset]</code>
|
||||
* interval differ from the given symbols;
|
||||
* </li>
|
||||
* </ul>
|
||||
* <p/>
|
||||
* Example: suppose that this method is called with buffer that holds <code>'test data'</code> symbols, offset that points
|
||||
* to the last symbols and <code>'sf'</code> as a chars to exclude. Offset that points to <code>'s'</code> symbol
|
||||
* is returned then, i.e. all symbols of the given buffer that are located after it and not after given offset
|
||||
* (<code>'t data'</code>) are guaranteed to not contain given chars (<code>'sf'</code>).
|
||||
*
|
||||
* @param buffer symbols buffer to check
|
||||
* @param offset initial symbols buffer offset to use
|
||||
* @param chars chars to exclude
|
||||
* @return offset of the given buffer that guarantees that all symbols at <code>(returned offset; given offset]</code>
|
||||
* interval of the given buffer differ from symbols of given <code>'chars'</code> arguments;
|
||||
* given offset is returned if it is outside of given buffer bounds;
|
||||
* <code>'-1'</code> is returned if all document symbols that precede given offset differ from symbols
|
||||
* of the given <code>'chars to exclude'</code>
|
||||
*/
|
||||
public static int shiftBackwardUntil(CharSequence buffer, int offset, String chars) {
|
||||
if (offset >= buffer.length()) return offset;
|
||||
while (true) {
|
||||
@@ -341,8 +376,12 @@ public class CharArrayUtil {
|
||||
public static byte[] toByteArray(char[] chars, int size) throws IOException {
|
||||
ByteArrayOutputStream out = new ByteArrayOutputStream();
|
||||
OutputStreamWriter writer = new OutputStreamWriter(out);
|
||||
writer.write(chars, 0, size);
|
||||
writer.close();
|
||||
try {
|
||||
writer.write(chars, 0, size);
|
||||
}
|
||||
finally {
|
||||
writer.close();
|
||||
}
|
||||
return out.toByteArray();
|
||||
}
|
||||
|
||||
@@ -356,15 +395,17 @@ public class CharArrayUtil {
|
||||
return true;
|
||||
}
|
||||
|
||||
public static boolean subArraysEqual(char[] ca1, int startOffset1, int endOffset1,char[] ca2, int startOffset2, int endOffset2) {
|
||||
if (endOffset1 - startOffset1 != endOffset2 - startOffset2) return false;
|
||||
for (int i = startOffset1; i < endOffset1; i++) {
|
||||
char c1 = ca1[i];
|
||||
char c2 = ca2[i - startOffset1 + startOffset2];
|
||||
if (c1 != c2) return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
//Commented in order to apply to green code policy as the method is unused.
|
||||
//
|
||||
//public static boolean subArraysEqual(char[] ca1, int startOffset1, int endOffset1,char[] ca2, int startOffset2, int endOffset2) {
|
||||
// if (endOffset1 - startOffset1 != endOffset2 - startOffset2) return false;
|
||||
// for (int i = startOffset1; i < endOffset1; i++) {
|
||||
// char c1 = ca1[i];
|
||||
// char c2 = ca2[i - startOffset1 + startOffset2];
|
||||
// if (c1 != c2) return false;
|
||||
// }
|
||||
// return true;
|
||||
//}
|
||||
|
||||
public static TextRange[] getIndents(CharSequence charsSequence, int shift) {
|
||||
List<TextRange> result = new ArrayList<TextRange>();
|
||||
|
||||
@@ -98,6 +98,9 @@
|
||||
<projectService
|
||||
serviceInterface="git4idea.config.GitVcsSettings"
|
||||
serviceImplementation="git4idea.config.GitVcsSettings"/>
|
||||
<projectService
|
||||
serviceInterface="git4idea.history.GitUsersComponent"
|
||||
serviceImplementation="git4idea.history.GitUsersComponent"/>
|
||||
<applicationService
|
||||
serviceInterface="git4idea.config.GitVcsApplicationSettings"
|
||||
serviceImplementation="git4idea.config.GitVcsApplicationSettings"/>
|
||||
@@ -115,8 +118,5 @@
|
||||
<component>
|
||||
<implementation-class>git4idea.history.browser.GitProjectLogManager</implementation-class>
|
||||
</component>
|
||||
<component>
|
||||
<implementation-class>git4idea.history.GitUsersComponent</implementation-class>
|
||||
</component>
|
||||
</project-components>
|
||||
</idea-plugin>
|
||||
|
||||
@@ -638,7 +638,9 @@ public class GitCheckinEnvironment implements CheckinEnvironment {
|
||||
final GitUsersComponent component = GitUsersComponent.getInstance(project);
|
||||
final Set<String> result = new HashSet<String>();
|
||||
for (VirtualFile root : roots) {
|
||||
result.addAll(component.getUsersList(root));
|
||||
final List<String> list = component.getUsersList(root);
|
||||
if (list == null) continue;
|
||||
result.addAll(list);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
@@ -16,7 +16,6 @@
|
||||
package com.theoryinpractice.testng.inspection;
|
||||
|
||||
import com.intellij.codeInsight.CodeInsightUtilBase;
|
||||
import com.intellij.codeInsight.actions.OptimizeImportsProcessor;
|
||||
import com.intellij.codeInspection.*;
|
||||
import com.intellij.openapi.diagnostic.Logger;
|
||||
import com.intellij.openapi.project.Project;
|
||||
@@ -196,7 +195,7 @@ public class JUnitConvertTool extends BaseJavaLocalInspectionTool {
|
||||
element.delete();
|
||||
}
|
||||
}
|
||||
new OptimizeImportsProcessor(project, javaFile).run(); //delete unused imports
|
||||
JavaCodeStyleManager.getInstance(project).optimizeImports(javaFile);//delete unused imports
|
||||
}
|
||||
catch (IncorrectOperationException e) {
|
||||
LOG.error("Error converting testcase", e);
|
||||
|
||||
Reference in New Issue
Block a user