Merge branch 'master' of git.labs.intellij.net:idea/community

This commit is contained in:
Kirill Likhodedov
2010-06-24 17:07:22 +04:00
49 changed files with 2420 additions and 316 deletions
@@ -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);
}
@@ -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();
@@ -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 { | &lt;- 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 { | &lt;- 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();
}
}
}
@@ -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();
@@ -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;
}
}
}
@@ -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);
}
}
}
@@ -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) {
@@ -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>();
+3 -3
View File
@@ -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);