Merge branch 'master' of git.labs.intellij.net:idea/community
authorKirill Likhodedov <kirill.likhodedov@jetbrains.com>
Tue, 14 Sep 2010 08:28:53 +0000 (12:28 +0400)
committerKirill Likhodedov <kirill.likhodedov@jetbrains.com>
Tue, 14 Sep 2010 08:28:53 +0000 (12:28 +0400)
131 files changed:
java/debugger/impl/src/com/intellij/debugger/actions/JumpToObjectAction.java
java/debugger/impl/src/com/intellij/debugger/engine/DebugProcessEvents.java
java/debugger/impl/src/com/intellij/debugger/impl/DebuggerSession.java
java/debugger/impl/src/com/intellij/debugger/ui/FramesPanel.java
java/debugger/impl/src/com/intellij/debugger/ui/PositionHighlighter.java
java/debugger/impl/src/com/intellij/debugger/ui/impl/DebuggerTreePanel.java
java/debugger/impl/src/com/intellij/debugger/ui/impl/InspectPanel.java
java/debugger/impl/src/com/intellij/debugger/ui/impl/UpdatableDebuggerView.java
java/debugger/impl/src/com/intellij/debugger/ui/impl/VariablesPanel.java
java/debugger/impl/src/com/intellij/debugger/ui/impl/WatchPanel.java
java/java-impl/src/com/intellij/ide/JavaLanguageCodeStyleSettingsProvider.java
java/java-impl/src/com/intellij/refactoring/changeSignature/JavaChangeSignatureDetector.java
platform/lang-api/src/com/intellij/psi/codeStyle/LanguageCodeStyleSettingsProvider.java
platform/lang-api/src/com/intellij/psi/util/PsiTreeUtil.java
platform/lang-impl/src/com/intellij/application/options/CodeStyleAbstractPanel.java
platform/lang-impl/src/com/intellij/application/options/GeneralCodeStylePanel.java
platform/lang-impl/src/com/intellij/application/options/codeStyle/CodeStyleBlankLinesPanel.java
platform/lang-impl/src/com/intellij/application/options/codeStyle/MultilanguageCodeStyleAbstractPanel.java
platform/lang-impl/src/com/intellij/application/options/codeStyle/OptionTreeWithPreviewPanel.java
platform/lang-impl/src/com/intellij/application/options/codeStyle/WrappingAndBracesPanel.java
platform/lang-impl/src/com/intellij/application/options/colors/FontEditorPreview.java
platform/lang-impl/src/com/intellij/codeInsight/daemon/impl/TrafficLightRenderer.java
platform/lang-impl/src/com/intellij/codeInsight/daemon/impl/TrafficTooltipRendererImpl.java
platform/lang-impl/src/com/intellij/ide/actions/CreateDirectoryOrPackageAction.java
platform/lang-impl/src/com/intellij/ide/actions/CreateDirectoryOrPackageHandler.java [new file with mode: 0644]
platform/lang-impl/src/com/intellij/ide/util/DirectoryChooserUtil.java
platform/lang-impl/src/com/intellij/injected/editor/FoldingModelWindow.java
platform/lang-impl/src/com/intellij/refactoring/changeSignature/ChangeSignatureGestureDetector.java
platform/lang-impl/src/com/intellij/refactoring/changeSignature/LanguageChangeSignatureDetector.java
platform/lang-impl/src/com/intellij/refactoring/changeSignature/MoveParameterAction.java [new file with mode: 0644]
platform/lang-impl/src/com/intellij/refactoring/changeSignature/MoveParameterLeftAction.java [new file with mode: 0644]
platform/lang-impl/src/com/intellij/refactoring/changeSignature/MoveParameterRightAction.java [new file with mode: 0644]
platform/platform-api/src/com/intellij/codeHighlighting/HighlightDisplayLevel.java
platform/platform-api/src/com/intellij/ide/util/treeView/AbstractTreeUi.java
platform/platform-api/src/com/intellij/openapi/editor/DefaultLineWrapPositionStrategy.java
platform/platform-api/src/com/intellij/openapi/editor/EditorModificationUtil.java
platform/platform-api/src/com/intellij/openapi/editor/SoftWrap.java [new file with mode: 0644]
platform/platform-api/src/com/intellij/openapi/editor/SoftWrapModel.java
platform/platform-api/src/com/intellij/patterns/StringPattern.java
platform/platform-impl/src/com/intellij/codeInsight/hint/CustomHrView.java [new file with mode: 0644]
platform/platform-impl/src/com/intellij/codeInsight/hint/EditorFragmentComponent.java
platform/platform-impl/src/com/intellij/codeInsight/hint/LineTooltipRenderer.java
platform/platform-impl/src/com/intellij/ide/IdeTooltipManager.java
platform/platform-impl/src/com/intellij/openapi/actionSystem/impl/ActionToolbarImpl.java
platform/platform-impl/src/com/intellij/openapi/editor/actions/EditorActionUtil.java
platform/platform-impl/src/com/intellij/openapi/editor/actions/ToggleUseSoftWrapsToolbarAction.java
platform/platform-impl/src/com/intellij/openapi/editor/ex/FoldingListener.java [new file with mode: 0644]
platform/platform-impl/src/com/intellij/openapi/editor/ex/FoldingModelEx.java
platform/platform-impl/src/com/intellij/openapi/editor/ex/SoftWrapChangeListener.java
platform/platform-impl/src/com/intellij/openapi/editor/ex/SoftWrapModelEx.java
platform/platform-impl/src/com/intellij/openapi/editor/ex/util/EditorUtil.java
platform/platform-impl/src/com/intellij/openapi/editor/ex/util/LexerEditorHighlighter.java
platform/platform-impl/src/com/intellij/openapi/editor/impl/CaretModelImpl.java
platform/platform-impl/src/com/intellij/openapi/editor/impl/EditorDocumentPriorities.java [new file with mode: 0644]
platform/platform-impl/src/com/intellij/openapi/editor/impl/EditorGutterComponentImpl.java
platform/platform-impl/src/com/intellij/openapi/editor/impl/EditorImpl.java
platform/platform-impl/src/com/intellij/openapi/editor/impl/FoldingModelImpl.java
platform/platform-impl/src/com/intellij/openapi/editor/impl/RangeMarkerTree.java
platform/platform-impl/src/com/intellij/openapi/editor/impl/ScrollingModelImpl.java
platform/platform-impl/src/com/intellij/openapi/editor/impl/SelectionModelImpl.java
platform/platform-impl/src/com/intellij/openapi/editor/impl/SoftWrapModelImpl.java
platform/platform-impl/src/com/intellij/openapi/editor/impl/softwrap/DefaultSoftWrapApplianceManager.java [deleted file]
platform/platform-impl/src/com/intellij/openapi/editor/impl/softwrap/SoftWrapApplianceManager.java [deleted file]
platform/platform-impl/src/com/intellij/openapi/editor/impl/softwrap/SoftWrapConstants.java [deleted file]
platform/platform-impl/src/com/intellij/openapi/editor/impl/softwrap/SoftWrapDataMapper.java
platform/platform-impl/src/com/intellij/openapi/editor/impl/softwrap/SoftWrapDocumentChangeManager.java [deleted file]
platform/platform-impl/src/com/intellij/openapi/editor/impl/softwrap/SoftWrapHelper.java
platform/platform-impl/src/com/intellij/openapi/editor/impl/softwrap/SoftWrapImpl.java [new file with mode: 0644]
platform/platform-impl/src/com/intellij/openapi/editor/impl/softwrap/SoftWrapsStorage.java
platform/platform-impl/src/com/intellij/openapi/editor/impl/softwrap/TextBasedSoftWrapPainter.java
platform/platform-impl/src/com/intellij/openapi/editor/impl/softwrap/mapping/AbstractDataProvider.java [new file with mode: 0644]
platform/platform-impl/src/com/intellij/openapi/editor/impl/softwrap/mapping/AbstractListBasedDataProvider.java [new file with mode: 0644]
platform/platform-impl/src/com/intellij/openapi/editor/impl/softwrap/mapping/AbstractMappingStrategy.java [new file with mode: 0644]
platform/platform-impl/src/com/intellij/openapi/editor/impl/softwrap/mapping/CacheEntry.java [new file with mode: 0644]
platform/platform-impl/src/com/intellij/openapi/editor/impl/softwrap/mapping/CachingSoftWrapDataMapper.java [new file with mode: 0644]
platform/platform-impl/src/com/intellij/openapi/editor/impl/softwrap/mapping/CompositeDataProvider.java [new file with mode: 0644]
platform/platform-impl/src/com/intellij/openapi/editor/impl/softwrap/mapping/DataProvider.java [new file with mode: 0644]
platform/platform-impl/src/com/intellij/openapi/editor/impl/softwrap/mapping/FoldingData.java [new file with mode: 0644]
platform/platform-impl/src/com/intellij/openapi/editor/impl/softwrap/mapping/FoldingDataProvider.java [new file with mode: 0644]
platform/platform-impl/src/com/intellij/openapi/editor/impl/softwrap/mapping/LogicalToVisualMappingStrategy.java [new file with mode: 0644]
platform/platform-impl/src/com/intellij/openapi/editor/impl/softwrap/mapping/MappingStrategy.java [new file with mode: 0644]
platform/platform-impl/src/com/intellij/openapi/editor/impl/softwrap/mapping/MappingUtil.java [new file with mode: 0644]
platform/platform-impl/src/com/intellij/openapi/editor/impl/softwrap/mapping/OffsetToLogicalCalculationStrategy.java [new file with mode: 0644]
platform/platform-impl/src/com/intellij/openapi/editor/impl/softwrap/mapping/ProcessingContext.java [new file with mode: 0644]
platform/platform-impl/src/com/intellij/openapi/editor/impl/softwrap/mapping/SoftWrapApplianceManager.java [new file with mode: 0644]
platform/platform-impl/src/com/intellij/openapi/editor/impl/softwrap/mapping/SoftWrapAwareDocumentParsingListener.java [new file with mode: 0644]
platform/platform-impl/src/com/intellij/openapi/editor/impl/softwrap/mapping/SoftWrapDataProviderKeys.java [new file with mode: 0644]
platform/platform-impl/src/com/intellij/openapi/editor/impl/softwrap/mapping/SoftWrapsDataProvider.java [new file with mode: 0644]
platform/platform-impl/src/com/intellij/openapi/editor/impl/softwrap/mapping/TabData.java [new file with mode: 0644]
platform/platform-impl/src/com/intellij/openapi/editor/impl/softwrap/mapping/TabulationDataProvider.java [new file with mode: 0644]
platform/platform-impl/src/com/intellij/openapi/editor/impl/softwrap/mapping/VisualToLogicalCalculationStrategy.java [new file with mode: 0644]
platform/platform-impl/src/com/intellij/openapi/editor/textarea/TextComponentSoftWrapModel.java
platform/platform-impl/src/com/intellij/ui/EditorTextField.java
platform/platform-impl/src/com/intellij/ui/HintHint.java
platform/platform-impl/testSrc/com/intellij/openapi/editor/impl/softwrap/MockEditorTextRepresentationHelper.java
platform/platform-impl/testSrc/com/intellij/openapi/editor/impl/softwrap/mapping/CachingSoftWrapDataMapperTest.java [moved from platform/platform-impl/testSrc/com/intellij/openapi/editor/impl/softwrap/SoftWrapDataMapperTest.java with 83% similarity]
platform/platform-impl/testSrc/com/intellij/openapi/editor/impl/softwrap/mapping/CompositeDataProviderTest.java [new file with mode: 0644]
platform/platform-resources-en/src/messages/ActionsBundle.properties
platform/platform-resources/src/META-INF/PlatformPlugin.xml
platform/platform-resources/src/idea/PlatformApplicationInfo.xml
platform/util/src/com/intellij/util/ui/UIUtil.java
plugins/IntelliLang/IntelliLang-js.iml [deleted file]
plugins/IntelliLang/js-support/com/intellij/plugins/intelliLang/inject/JSLanguageInjectionSupport.java [deleted file]
plugins/IntelliLang/js-support/resources/jsInjections.xml [deleted file]
plugins/groovy/resources/standardDsls/enumMethods.gdsl
plugins/groovy/src/org/jetbrains/plugins/groovy/GroovyBundle.properties
plugins/groovy/src/org/jetbrains/plugins/groovy/annotator/GroovyAnnotator.java
plugins/groovy/src/org/jetbrains/plugins/groovy/intentions/conversions/ConvertClosureToMethodIntention.java
plugins/groovy/src/org/jetbrains/plugins/groovy/lang/lexer/_GroovyLexer.java
plugins/groovy/src/org/jetbrains/plugins/groovy/lang/lexer/groovy.flex
plugins/groovy/src/org/jetbrains/plugins/groovy/lang/psi/impl/GroovyFileImpl.java
plugins/groovy/src/org/jetbrains/plugins/groovy/refactoring/move/MoveGroovyClassHandler.java
plugins/groovy/src/org/jetbrains/plugins/groovy/refactoring/move/MoveGroovyClassUtil.java [new file with mode: 0644]
plugins/groovy/src/org/jetbrains/plugins/groovy/refactoring/move/MoveGroovyScriptHandler.java
plugins/groovy/src/org/jetbrains/plugins/groovy/refactoring/move/MoveGroovyScriptProcessor.java
plugins/groovy/test/org/jetbrains/plugins/groovy/intentions/ConvertMethodToClosureTest.java
plugins/groovy/test/org/jetbrains/plugins/groovy/refactoring/move/GroovyMoveScriptTest.java
plugins/groovy/testdata/intentions/convertMethodToClosure/ClosureWithImplicitParameterToMethod_after.groovy
plugins/groovy/testdata/intentions/convertMethodToClosure/ClosureWithoutModifiersToMethod.groovy [new file with mode: 0644]
plugins/groovy/testdata/intentions/convertMethodToClosure/ClosureWithoutModifiersToMethod_after.groovy [new file with mode: 0644]
plugins/groovy/testdata/refactoring/move/moveScript/scriptWithClasses/after/a/Y.groovy [new file with mode: 0644]
plugins/groovy/testdata/refactoring/move/moveScript/scriptWithClasses/after/b/Foo.groovy [new file with mode: 0644]
plugins/groovy/testdata/refactoring/move/moveScript/scriptWithClasses/after/b/X.groovy [new file with mode: 0644]
plugins/groovy/testdata/refactoring/move/moveScript/scriptWithClasses/before/a/Foo.groovy [new file with mode: 0644]
plugins/groovy/testdata/refactoring/move/moveScript/scriptWithClasses/before/a/Y.groovy [new file with mode: 0644]
plugins/groovy/testdata/refactoring/move/moveScript/scriptWithClasses/before/b/X.groovy [new file with mode: 0644]
plugins/junit/src/com/intellij/execution/junit/JUnitConfiguration.java
plugins/testng/src/com/theoryinpractice/testng/model/TestData.java
resources/src/idea/IdeaActions.xml
xml/dom-impl/src/com/intellij/util/xml/highlighting/DomElementsErrorPanel.java
xml/impl/src/com/intellij/psi/impl/source/xml/SchemaPrefixReference.java

index b58eaa7ff08a4268395e1f5506333e3003c625a7..269d3eafa5bb792db013aab0b0a11a798b470cc2 100644 (file)
@@ -98,18 +98,21 @@ public class JumpToObjectAction extends DebuggerAction{
         type = ((ArrayType)type).componentType();
       }
       if(type instanceof ClassType) {
-        final List<Location> locations = ((ClassType)type).allLineLocations();
+        final ClassType clsType = (ClassType)type;
+        final List<Location> locations = clsType.allLineLocations();
         if(locations.size() > 0) {
           final Location location = locations.get(0);
           return ApplicationManager.getApplication().runReadAction(new Computable<SourcePosition>() {
             public SourcePosition compute() {
               SourcePosition position = debugProcess.getPositionManager().getSourcePosition(location);
-              // adjust position
-              final PsiClass classAt = JVMNameUtil.getClassAt(position);
-              if (classAt != null) {
-                final SourcePosition classPosition = SourcePosition.createFromElement(classAt);
-                if (classPosition != null) {
-                  position = classPosition;
+              // adjust position for non-anonymous classes
+              if (clsType.name().indexOf("$") < 0) {
+                final PsiClass classAt = JVMNameUtil.getClassAt(position);
+                if (classAt != null) {
+                  final SourcePosition classPosition = SourcePosition.createFromElement(classAt);
+                  if (classPosition != null) {
+                    position = classPosition;
+                  }
                 }
               }
               return position;
index 55c31d7c4dbefc18f37103c44841d1099f90954a..5d12ced336519eb541babd03abea6a9a117f9c83 100644 (file)
@@ -154,6 +154,7 @@ public class DebugProcessEvents extends DebugProcessImpl {
                 final ThreadReference thread = ((ThreadStartEvent)event).thread();
                 getManagerThread().schedule(new DebuggerCommandImpl() {
                   protected void action() throws Exception {
+                    getVirtualMachineProxy().threadStarted(thread);
                     myDebugProcessDispatcher.getMulticaster().threadStarted(DebugProcessEvents.this, thread);
                   }
                 });
@@ -163,6 +164,7 @@ public class DebugProcessEvents extends DebugProcessImpl {
                 final ThreadReference thread = ((ThreadDeathEvent)event).thread();
                 getManagerThread().schedule(new DebuggerCommandImpl() {
                   protected void action() throws Exception {
+                    getVirtualMachineProxy().threadStopped(thread);
                     myDebugProcessDispatcher.getMulticaster().threadStopped(DebugProcessEvents.this, thread);
                   }
                 });
index e993c5ad6479fd9bb0a424a0e9315af242cdc0de..b72c1f0809b17fec1432f9187e902eb2d88e7214 100644 (file)
@@ -26,7 +26,6 @@ import com.intellij.debugger.engine.events.SuspendContextCommandImpl;
 import com.intellij.debugger.engine.requests.RequestManagerImpl;
 import com.intellij.debugger.jdi.StackFrameProxyImpl;
 import com.intellij.debugger.jdi.ThreadReferenceProxyImpl;
-import com.intellij.debugger.jdi.VirtualMachineProxyImpl;
 import com.intellij.debugger.ui.breakpoints.Breakpoint;
 import com.intellij.debugger.ui.breakpoints.BreakpointWithHighlighter;
 import com.intellij.debugger.ui.breakpoints.LineBreakpoint;
@@ -41,18 +40,21 @@ import com.intellij.execution.process.ProcessOutputTypes;
 import com.intellij.execution.runners.ProgramRunner;
 import com.intellij.idea.ActionsBundle;
 import com.intellij.openapi.application.ApplicationManager;
+import com.intellij.openapi.application.ModalityState;
 import com.intellij.openapi.diagnostic.Logger;
 import com.intellij.openapi.editor.Document;
 import com.intellij.openapi.module.Module;
 import com.intellij.openapi.project.Project;
 import com.intellij.openapi.ui.Messages;
 import com.intellij.openapi.util.Computable;
+import com.intellij.openapi.util.Disposer;
 import com.intellij.openapi.util.Pair;
 import com.intellij.psi.PsiCompiledElement;
 import com.intellij.psi.PsiDocumentManager;
 import com.intellij.psi.PsiFile;
 import com.intellij.psi.search.GlobalSearchScope;
 import com.intellij.unscramble.ThreadState;
+import com.intellij.util.Alarm;
 import com.intellij.xdebugger.AbstractDebuggerSession;
 import com.sun.jdi.ObjectCollectedException;
 import com.sun.jdi.ThreadReference;
@@ -88,6 +90,7 @@ public class DebuggerSession implements AbstractDebuggerSession {
   public static final int EVENT_START_WAIT_ATTACH = 9;
   public static final int EVENT_DISPOSE = 10;
   public static final int EVENT_REFRESH_VIEWS_ONLY = 11;
+  public static final int EVENT_THREADS_REFRESH = 12;
 
   private volatile boolean myIsEvaluating;
   private volatile int myIgnoreFiltersFrameCountThreshold = 0;
@@ -101,6 +104,7 @@ public class DebuggerSession implements AbstractDebuggerSession {
   private final DebuggerContextImpl SESSION_EMPTY_CONTEXT;
   //Thread, user is currently stepping through
   private final Set<ThreadReferenceProxyImpl> mySteppingThroughThreads = new HashSet<ThreadReferenceProxyImpl>();
+  protected final Alarm myUpdateAlarm = new Alarm(Alarm.ThreadToUse.SWING_THREAD);
 
   public boolean isSteppingThrough(ThreadReferenceProxyImpl threadProxy) {
     return mySteppingThroughThreads.contains(threadProxy);
@@ -307,6 +311,7 @@ public class DebuggerSession implements AbstractDebuggerSession {
     ApplicationManager.getApplication().assertIsDispatchThread();
     getProcess().dispose();
     getContextManager().setState(SESSION_EMPTY_CONTEXT, STATE_DISPOSED, EVENT_DISPOSE, null);
+    Disposer.dispose(myUpdateAlarm);
   }
 
   // ManagerCommands
@@ -567,23 +572,21 @@ public class DebuggerSession implements AbstractDebuggerSession {
     }
 
     public void threadStarted(DebugProcess proc, ThreadReference thread) {
-      ((VirtualMachineProxyImpl)proc.getVirtualMachineProxy()).threadStarted(thread);
-      DebuggerInvocationUtil.invokeLater(getProject(), new Runnable() {
-        public void run() {
-          final DebuggerStateManager contextManager = getContextManager();
-          contextManager.fireStateChanged(contextManager.getContext(), EVENT_REFRESH_VIEWS_ONLY);
-        }
-      });
+      notifyThreadsRefresh();
     }
 
     public void threadStopped(DebugProcess proc, ThreadReference thread) {
-      ((VirtualMachineProxyImpl)proc.getVirtualMachineProxy()).threadStopped(thread);
-      DebuggerInvocationUtil.invokeLater(getProject(), new Runnable() {
+      notifyThreadsRefresh();
+    }
+    
+    private void notifyThreadsRefresh() {
+      myUpdateAlarm.cancelAllRequests();
+      myUpdateAlarm.addRequest(new Runnable() {
         public void run() {
           final DebuggerStateManager contextManager = getContextManager();
-          contextManager.fireStateChanged(contextManager.getContext(), EVENT_REFRESH_VIEWS_ONLY);
+          contextManager.fireStateChanged(contextManager.getContext(), EVENT_THREADS_REFRESH);
         }
-      });
+      }, 100, ModalityState.NON_MODAL);
     }
   }
 
index 2f7164de9d10690994c909dff21f1f4fdc38f87a..b05540efb1d1f348aa3b98bc53e7a56b390c78d8 100644 (file)
@@ -29,6 +29,7 @@ import com.intellij.debugger.engine.events.SuspendContextCommandImpl;
 import com.intellij.debugger.engine.jdi.StackFrameProxy;
 import com.intellij.debugger.impl.DebuggerContextImpl;
 import com.intellij.debugger.impl.DebuggerContextUtil;
+import com.intellij.debugger.impl.DebuggerSession;
 import com.intellij.debugger.impl.DebuggerStateManager;
 import com.intellij.debugger.jdi.StackFrameProxyImpl;
 import com.intellij.debugger.jdi.ThreadReferenceProxyImpl;
@@ -161,11 +162,13 @@ public class FramesPanel extends UpdatableDebuggerView {
   }
 
   /*invoked in swing thread*/
-  protected void rebuild(final boolean updateOnly) {
+  protected void rebuild(int event) {
     final DebuggerContextImpl context = getContext();
     final boolean paused = context.getDebuggerSession().isPaused();
-    
-    if (!paused || !updateOnly) {
+    final boolean isRefresh = event == DebuggerSession.EVENT_REFRESH ||
+                              event == DebuggerSession.EVENT_REFRESH_VIEWS_ONLY ||
+                              event == DebuggerSession.EVENT_THREADS_REFRESH;
+    if (!paused || !isRefresh) {
       myThreadsCombo.removeAllItems();
       synchronized (myFramesList) {
         myFramesList.clear();
@@ -175,7 +178,7 @@ public class FramesPanel extends UpdatableDebuggerView {
     if (paused) {
       final DebugProcessImpl process = context.getDebugProcess();
       if (process != null) {
-        process.getManagerThread().schedule(new RefreshFramePanelCommand(updateOnly && myThreadsCombo.getItemCount() != 0));
+        process.getManagerThread().schedule(new RefreshFramePanelCommand(isRefresh && myThreadsCombo.getItemCount() != 0));
       }
     }
   }
index 9e56b2774962867e8ce79c509120a1845ae9115d..2f0203acb8b7f2b27dbda4789c56d72cdb369b1a 100644 (file)
@@ -77,7 +77,7 @@ public class PositionHighlighter {
     stateManager.addListener(new DebuggerContextListener() {
       public void changeEvent(DebuggerContextImpl newContext, int event) {
         myContext = newContext;
-        if (event != DebuggerSession.EVENT_REFRESH_VIEWS_ONLY) {
+        if (event != DebuggerSession.EVENT_REFRESH_VIEWS_ONLY && event != DebuggerSession.EVENT_THREADS_REFRESH) {
           refresh();
         }
       }
index 2e140372077cd5669dda0dec2a299d2ae7285376..fbbcad3b909c37fd01d3500fc21e1126a865b0a4 100644 (file)
@@ -74,7 +74,7 @@ public abstract class DebuggerTreePanel extends UpdatableDebuggerView implements
   protected abstract DebuggerTree createTreeView();
 
 
-  protected void rebuild(final boolean updateOnly) {
+  protected void rebuild(int event) {
     DebuggerSession debuggerSession = getContext().getDebuggerSession();
     if(debuggerSession == null) {
       return;
index 4fc5a4d924533320f5aa280ae706137b49258b9a..c5fe2e0a6c3f8b6a735ff06442359f9ca60524c8 100644 (file)
@@ -22,10 +22,11 @@ package com.intellij.debugger.ui.impl;
 
 import com.intellij.debugger.actions.DebuggerAction;
 import com.intellij.debugger.actions.DebuggerActions;
+import com.intellij.debugger.impl.DebuggerContextImpl;
+import com.intellij.debugger.impl.DebuggerSession;
 import com.intellij.debugger.impl.DebuggerStateManager;
 import com.intellij.debugger.ui.impl.watch.DebuggerTree;
 import com.intellij.debugger.ui.impl.watch.NodeDescriptorImpl;
-import com.intellij.openapi.Disposable;
 import com.intellij.openapi.actionSystem.ActionPopupMenu;
 import com.intellij.openapi.actionSystem.CommonShortcuts;
 import com.intellij.openapi.project.Project;
@@ -47,6 +48,11 @@ public class InspectPanel extends DebuggerTreePanel {
     setUpdateEnabled(true);
   }
 
+  protected void changeEvent(DebuggerContextImpl newContext, int event) {
+    if (event != DebuggerSession.EVENT_THREADS_REFRESH) {
+      super.changeEvent(newContext, event);
+    }
+  }
 
   protected DebuggerTree createTreeView() {
     return new InspectDebuggerTree(getProject());
index f2abc68402f2af2967d456c11616a901f8ae8d8a..9863785dea78b102db169c982144bd101b987083 100644 (file)
@@ -17,7 +17,6 @@ package com.intellij.debugger.ui.impl;
 
 import com.intellij.debugger.impl.DebuggerContextImpl;
 import com.intellij.debugger.impl.DebuggerContextListener;
-import com.intellij.debugger.impl.DebuggerSession;
 import com.intellij.debugger.impl.DebuggerStateManager;
 import com.intellij.debugger.ui.DebuggerView;
 import com.intellij.openapi.Disposable;
@@ -86,7 +85,7 @@ public abstract class UpdatableDebuggerView extends JPanel implements DebuggerVi
       myRebuildAlarm.addRequest(new Runnable() {
         public void run() {
           try {
-            rebuild(event == DebuggerSession.EVENT_REFRESH || event == DebuggerSession.EVENT_REFRESH_VIEWS_ONLY);
+            rebuild(event);
           }
           catch (VMDisconnectedException e) {
             // ignored
@@ -99,7 +98,7 @@ public abstract class UpdatableDebuggerView extends JPanel implements DebuggerVi
     }
   }
 
-  protected abstract void rebuild(boolean updateOnly);
+  protected abstract void rebuild(int event);
 
   protected final void registerDisposable(Disposable disposable) {
     myDisposables.add(disposable);
index 7dd4cd58eda6fbee69daad5ba39f0a6922d7e983..0a4910f3a231397ac9d582769a1d4300d3358cd3 100644 (file)
@@ -17,6 +17,8 @@ package com.intellij.debugger.ui.impl;
 
 import com.intellij.debugger.actions.DebuggerAction;
 import com.intellij.debugger.actions.DebuggerActions;
+import com.intellij.debugger.impl.DebuggerContextImpl;
+import com.intellij.debugger.impl.DebuggerSession;
 import com.intellij.debugger.impl.DebuggerStateManager;
 import com.intellij.debugger.ui.impl.watch.DebuggerTree;
 import com.intellij.openapi.Disposable;
@@ -53,6 +55,11 @@ public class VariablesPanel extends DebuggerTreePanel implements DataProvider{
     return new FrameDebuggerTree(getProject());
   }
 
+  protected void changeEvent(DebuggerContextImpl newContext, int event) {
+    if (event != DebuggerSession.EVENT_THREADS_REFRESH) {
+      super.changeEvent(newContext, event);
+    }
+  }
 
   protected ActionPopupMenu createPopupMenu() {
     ActionGroup group = (ActionGroup)ActionManager.getInstance().getAction(DebuggerActions.FRAME_PANEL_POPUP);
index 2889e65f37df39eb237a8dff6eab0b31b931a578..3823e5d312fb2ba3b35351598b23495738f03af3 100644 (file)
@@ -28,7 +28,6 @@ import com.intellij.debugger.impl.DebuggerStateManager;
 import com.intellij.debugger.ui.impl.watch.DebuggerTree;
 import com.intellij.debugger.ui.impl.watch.DebuggerTreeNodeImpl;
 import com.intellij.debugger.ui.impl.watch.WatchItemDescriptor;
-import com.intellij.openapi.Disposable;
 import com.intellij.openapi.actionSystem.ActionPopupMenu;
 import com.intellij.openapi.actionSystem.CommonShortcuts;
 import com.intellij.openapi.actionSystem.PlatformDataKeys;
@@ -54,6 +53,9 @@ public abstract class WatchPanel extends DebuggerTreePanel {
   }
 
   protected void changeEvent(DebuggerContextImpl newContext, int event) {
+    if (event == DebuggerSession.EVENT_THREADS_REFRESH) {
+      return;
+    }
     if(event == DebuggerSession.EVENT_ATTACHED) {
       DebuggerTreeNodeImpl root = (DebuggerTreeNodeImpl) getWatchTree().getModel().getRoot();
       if(root != null) {
index 20d5adb3aa7cce29990045e1aeafc3e6614bcdaa..87afe0d3a3901f41036352eb2e55f47fe5e8932c 100644 (file)
  */
 package com.intellij.ide;
 
-import com.intellij.psi.codeStyle.LanguageCodeStyleSettingsProvider;
 import com.intellij.lang.Language;
 import com.intellij.lang.StdLanguages;
 import com.intellij.psi.codeStyle.CodeStyleSettingsCustomizable;
+import com.intellij.psi.codeStyle.LanguageCodeStyleSettingsProvider;
 import org.jetbrains.annotations.NotNull;
 
 /**
@@ -40,6 +40,12 @@ public class JavaLanguageCodeStyleSettingsProvider extends LanguageCodeStyleSett
     return GENERAL_CODE_SAMPLE;
   }
 
+  @Override
+  public int getRightMargin(@NotNull SettingsType settingsType) {
+    if (settingsType == SettingsType.WRAPPING_AND_BRACES_SETTINGS) return 37;
+    return super.getRightMargin(settingsType);
+  }
+
   @Override
   public void customizeSettings(@NotNull CodeStyleSettingsCustomizable consumer, @NotNull SettingsType settingsType) {
     consumer.showAllStandardOptions();
index c71f7674f9b4aec737f8d4292ec04179b1f4c799..c9102b3fb4c4d78fa75f184610e0dc82cc30912f 100644 (file)
 package com.intellij.refactoring.changeSignature;
 
 import com.intellij.openapi.application.ApplicationManager;
+import com.intellij.openapi.application.Result;
 import com.intellij.openapi.command.CommandProcessor;
+import com.intellij.openapi.command.WriteCommandAction;
 import com.intellij.openapi.diagnostic.Logger;
 import com.intellij.openapi.editor.Document;
+import com.intellij.openapi.editor.Editor;
 import com.intellij.openapi.util.Comparing;
 import com.intellij.openapi.util.TextRange;
 import com.intellij.psi.*;
@@ -254,6 +257,52 @@ public class JavaChangeSignatureDetector implements LanguageChangeSignatureDetec
     return method != null && isInsideMethodSignature(element, method) && Comparing.equal(method, bannedInfo.getMethod());
   }
 
+  @Override
+  public boolean isMoveParameterAvailable(PsiElement element, boolean left) {
+    if (element instanceof PsiParameter) {
+      final PsiParameter parameter = (PsiParameter)element;
+      final PsiElement declarationScope = parameter.getDeclarationScope();
+      if (declarationScope instanceof PsiMethod) {
+        final PsiMethod method = (PsiMethod)declarationScope;
+        final int parameterIndex = method.getParameterList().getParameterIndex(parameter);
+        if (left) {
+          return parameterIndex > 0;
+        } else {
+          return parameterIndex < method.getParameterList().getParametersCount() - 1;
+        }
+      }
+    }
+    return false;
+  }
+
+  @Override
+  public void moveParameter(final PsiElement element, final Editor editor, final boolean left) {
+    final PsiParameter parameter = (PsiParameter)element;
+    final PsiMethod method = (PsiMethod)parameter.getDeclarationScope();
+    final int parameterIndex = method.getParameterList().getParameterIndex(parameter);
+    new WriteCommandAction(element.getProject(), MOVE_PARAMETER){
+      @Override
+      protected void run(Result result) throws Throwable {
+        final PsiParameterList parameterList = method.getParameterList();
+        final PsiParameter[] parameters = parameterList.getParameters();
+        final int deltaOffset = editor.getCaretModel().getOffset() - parameter.getTextRange().getStartOffset();
+        final PsiParameter frst = left ? parameters[parameterIndex - 1] : parameter;
+        final PsiParameter scnd = left ? parameter : parameters[parameterIndex + 1];
+        final int startOffset = frst.getTextRange().getStartOffset();
+        final int endOffset = scnd.getTextRange().getEndOffset();
+
+        final PsiFile file = method.getContainingFile();
+        final Document document = PsiDocumentManager.getInstance(getProject()).getDocument(file);
+        if (document != null) {
+          final String comma_whitespace_between =
+            document.getText().substring(frst.getTextRange().getEndOffset(), scnd.getTextRange().getStartOffset());
+          document.replaceString(startOffset, endOffset, scnd.getText() + comma_whitespace_between + frst.getText());
+          editor.getCaretModel().moveToOffset(document.getText().indexOf(parameter.getText(), startOffset) + deltaOffset);
+        }
+      }
+    }.execute();
+  }
+
   private static boolean isInsideMethodSignature(PsiElement element, @NotNull PsiMethod method) {
     final PsiCodeBlock body = method.getBody();
     if (body != null) {
index e3da448ac825b5f68ba3a2107e662be04db8171e..d01859164a6bea06e208d35867c8cd2481575f82 100644 (file)
@@ -39,6 +39,10 @@ public abstract class LanguageCodeStyleSettingsProvider {
 
   public abstract String getCodeSample(@NotNull SettingsType settingsType);
 
+  public int getRightMargin(@NotNull SettingsType settingsType) {
+    return settingsType == SettingsType.WRAPPING_AND_BRACES_SETTINGS ? 30 : -1;
+  }
+
   public void customizeSettings(@NotNull CodeStyleSettingsCustomizable consumer, @NotNull SettingsType settingsType) {
   }
 
@@ -74,6 +78,15 @@ public abstract class LanguageCodeStyleSettingsProvider {
     return null;
   }
 
+  public static int getRightMargin(Language lang, @NotNull SettingsType settingsType) {
+    for (LanguageCodeStyleSettingsProvider provider : Extensions.getExtensions(EP_NAME)) {
+      if (provider.getLanguage().equals(lang)) {
+        return provider.getRightMargin(settingsType);
+      }
+    }
+    return -1;
+  }
+
   @Nullable
   public static Language getLanguage(String langName) {
     for (LanguageCodeStyleSettingsProvider provider : Extensions.getExtensions(EP_NAME)) {
index 35fa915d585ed727bcd3ace83692adb342aee577..01e6a35be7ebdb61093e47d5b3b2d90ecb983c5d 100644 (file)
@@ -24,6 +24,7 @@ import com.intellij.psi.*;
 import com.intellij.psi.scope.PsiScopeProcessor;
 import com.intellij.psi.search.PsiElementProcessor;
 import com.intellij.psi.stubs.StubBase;
+import com.intellij.psi.stubs.StubElement;
 import com.intellij.psi.templateLanguages.OuterLanguageElement;
 import com.intellij.util.ArrayUtil;
 import com.intellij.util.SmartList;
@@ -352,7 +353,16 @@ public class PsiTreeUtil {
 
   @Nullable
   public static PsiElement getStubOrPsiParent(@Nullable PsiElement element) {
-    return getStubOrPsiParentOfType(element, PsiElement.class);
+    if (element instanceof StubBasedPsiElement) {
+      StubBase stub = (StubBase)((StubBasedPsiElement)element).getStub();
+      if (stub != null) {
+        //noinspection unchecked
+        final StubElement parentStub = stub.getParentStub();
+        return parentStub != null ? parentStub.getPsi() : null;
+      }
+
+    }
+    return element != null ? element.getParent() : null;
   }
 
   @Nullable
index 4f308de9261d301cb9376db0e8253fa262f4380c..87c17486aa3eaa3f2f9a57c5704e369a8f0a2b09 100644 (file)
@@ -16,6 +16,7 @@
 package com.intellij.application.options;
 
 import com.intellij.application.options.codeStyle.CodeStyleSchemesModel;
+import com.intellij.codeStyle.CodeStyleFacade;
 import com.intellij.ide.DataManager;
 import com.intellij.openapi.Disposable;
 import com.intellij.openapi.actionSystem.PlatformDataKeys;
@@ -89,6 +90,8 @@ public abstract class CodeStyleAbstractPanel implements Disposable {
         somethingChanged();
       }
     });
+
+    updatePreview();
   }
 
   public void setModel(final CodeStyleSchemesModel model) {
@@ -107,35 +110,13 @@ public abstract class CodeStyleAbstractPanel implements Disposable {
 
   private Editor createEditor() {
     EditorFactory editorFactory = EditorFactory.getInstance();
-    myTextToReformat = getPreviewText();
-    Document editorDocument = editorFactory.createDocument(myTextToReformat);
+    Document editorDocument = editorFactory.createDocument("");
     EditorEx editor = (EditorEx)editorFactory.createViewer(editorDocument);
-
+    fillEditorSettings(editor.getSettings());
     myLastDocumentModificationStamp = editor.getDocument().getModificationStamp();
-
-    EditorSettings editorSettings = editor.getSettings();
-    fillEditorSettings(editorSettings);
-
-    updatePreviewHighlighter(editor);
-
     return editor;
   }
 
-  private void updatePreviewHighlighter(final EditorEx editor) {
-    EditorColorsScheme scheme = editor.getColorsScheme();
-    scheme.setColor(EditorColors.CARET_ROW_COLOR, null);
-
-    editor.setHighlighter(createHighlighter(scheme));
-  }
-
-  protected void updatePreviewEditor() {
-    myTextToReformat = getPreviewText();
-    updatePreview();
-    updatePreviewHighlighter((EditorEx)myEditor);
-  }
-
-  protected abstract EditorHighlighter createHighlighter(final EditorColorsScheme scheme);
-
   private void fillEditorSettings(final EditorSettings editorSettings) {
     editorSettings.setWhitespacesShown(true);
     editorSettings.setLineMarkerAreaShown(false);
@@ -144,21 +125,22 @@ public abstract class CodeStyleAbstractPanel implements Disposable {
     editorSettings.setFoldingOutlineShown(false);
     editorSettings.setAdditionalColumnsCount(0);
     editorSettings.setAdditionalLinesCount(1);
-    final int rightMargin = getRightMargin();
-    if (rightMargin > 0) {
-      editorSettings.setRightMargin(rightMargin);
-    }
   }
 
-  protected abstract int getRightMargin();
+  protected void updatePreview() {
+    updateEditor();
+    updatePreviewHighlighter((EditorEx)myEditor);
+  }
 
-  public final void updatePreview() {
+  private void updateEditor() {
     if (!myShouldUpdatePreview || !myEditor.getComponent().isShowing()) {
       return;
     }
 
     if (myLastDocumentModificationStamp != myEditor.getDocument().getModificationStamp()) {
       myTextToReformat = myEditor.getDocument().getText();
+    } else {
+      myTextToReformat = getPreviewText();
     }
 
     int currOffs = myEditor.getScrollingModel().getVerticalScrollOffset();
@@ -169,11 +151,19 @@ public abstract class CodeStyleAbstractPanel implements Disposable {
         replaceText(finalProject);
       }
     }, null, null);
-    myEditor.getSettings().setRightMargin(getRightMargin());
+
+    myEditor.getSettings().setRightMargin(getAdjustedRightMargin());
     myLastDocumentModificationStamp = myEditor.getDocument().getModificationStamp();
     myEditor.getScrollingModel().scrollVertically(currOffs);
   }
 
+  private int getAdjustedRightMargin() {
+    int result = getRightMargin();
+    return result > 0 ? result : CodeStyleFacade.getInstance(getCurrentProject()).getRightMargin();
+  }
+
+  protected abstract int getRightMargin();
+
   private void replaceText(final Project project) {
     ApplicationManager.getApplication().runWriteAction(new Runnable() {
       public void run() {
@@ -184,9 +174,7 @@ public abstract class CodeStyleAbstractPanel implements Disposable {
           prepareForReformat(psiFile);
           apply(mySettings);
           CodeStyleSettings clone = mySettings.clone();
-          if (getRightMargin() > 0) {
-            clone.RIGHT_MARGIN = getRightMargin();
-          }
+          clone.RIGHT_MARGIN = getAdjustedRightMargin();
 
 
           CodeStyleSettingsManager.getInstance(project).setTemporarySettings(clone);
@@ -226,6 +214,14 @@ public abstract class CodeStyleAbstractPanel implements Disposable {
     return project;
   }
 
+  private void updatePreviewHighlighter(final EditorEx editor) {
+    EditorColorsScheme scheme = editor.getColorsScheme();
+    scheme.setColor(EditorColors.CARET_ROW_COLOR, null);
+    editor.setHighlighter(createHighlighter(scheme));
+  }
+
+  protected abstract EditorHighlighter createHighlighter(final EditorColorsScheme scheme);
+
   @NotNull
   protected abstract FileType getFileType();
 
@@ -322,7 +318,7 @@ public abstract class CodeStyleAbstractPanel implements Disposable {
         try {
           myUpdateAlarm.cancelAllRequests();
           if (isSomethingChanged()) {
-            updatePreview();
+            updateEditor();
           }
         }
         finally {
index 90ef442f2faf39caea7a23cfbc40c1fcc2bb2f53..f227858fbecea7b4341a7a5da7dfa7287d247f47 100644 (file)
@@ -148,7 +148,7 @@ public class GeneralCodeStylePanel extends CodeStyleAbstractPanel {
         final int selIndex = myIndentOptionsTabs.getSelectedIndex();
         if (selIndex != myLastSelectedTab) {
           myLastSelectedTab = selIndex;
-          updatePreviewEditor();
+          updatePreview();
           somethingChanged();
         }
       }
index 5a08c679c8175ba37753ba6bbbd4a36f2ba254b5..2fe4ea1dafa6ef31693388f53bcc7b898bd39057 100644 (file)
@@ -155,10 +155,6 @@ public class CodeStyleBlankLinesPanel extends MultilanguageCodeStyleAbstractPane
 
   }
 
-  protected int getRightMargin() {
-    return -1;
-  }
-
   public JComponent getPanel() {
     return myPanel;
   }
index a83091633f28583bd602b6b7859f4deacba4b417..eedd6f5f01ecb560313852e06e422139ee73f530 100644 (file)
@@ -86,7 +86,7 @@ public abstract class MultilanguageCodeStyleAbstractPanel extends CodeStyleAbstr
       }
     }
     onLanguageChange(language);
-    updatePreviewEditor();
+    updatePreview();
   }
 
   public Language getSelectedLanguage() {
@@ -106,6 +106,12 @@ public abstract class MultilanguageCodeStyleAbstractPanel extends CodeStyleAbstr
     return sample;
   }
 
+  @Override
+  protected int getRightMargin() {
+    if (myLanguage == null) return -1;
+    return LanguageCodeStyleSettingsProvider.getRightMargin(myLanguage, getSettingsType());
+  }
+
   @NotNull
   @Override
   protected FileType getFileType() {
@@ -188,7 +194,7 @@ public abstract class MultilanguageCodeStyleAbstractPanel extends CodeStyleAbstr
         setPanelLanguage(langs[0]);
       }
       else {
-        updatePreviewEditor();
+        updatePreview();
       }
       tabbedPane.addChangeListener(new ChangeListener() {
         public void stateChanged(ChangeEvent e) {
@@ -213,7 +219,7 @@ public abstract class MultilanguageCodeStyleAbstractPanel extends CodeStyleAbstr
     else {
       // If settings are language-specific
       previewPanel.add(getEditor().getComponent(), BorderLayout.CENTER);
-      updatePreviewEditor();
+      updatePreview();
     }
   }
 
index 5b94deafdfd0918f84c05b5d9a61965a4bfa720e..ac5805f483cf5d21833477692e1aac867bc083e5 100644 (file)
@@ -223,10 +223,6 @@ public abstract class OptionTreeWithPreviewPanel extends MultilanguageCodeStyleA
 
   protected abstract void initTables();
 
-  protected int getRightMargin() {
-    return -1;
-  }
-
   protected void resetImpl(final CodeStyleSettings settings) {
     TreeModel treeModel = myOptionsTree.getModel();
     TreeNode root = (TreeNode)treeModel.getRoot();
index d715db1a4cf64335559c20886b3bf29ec484c581..6b7349a00d7f673db73d25d2d793188edccb25a7 100644 (file)
@@ -134,8 +134,4 @@ public class WrappingAndBracesPanel extends OptionTableWithPreviewPanel {
     addOption("VARIABLE_ANNOTATION_WRAP", ApplicationBundle.message("wrapping.local.variables.annotation"), WRAP_OPTIONS, WRAP_VALUES);
     addOption("ENUM_CONSTANTS_WRAP", ApplicationBundle.message("wrapping.enum.constants"), WRAP_OPTIONS, WRAP_VALUES);
   }
-
-  protected int getRightMargin() {
-    return 37;
-  }
 }
\ No newline at end of file
index 33add956d0d499077196c973929787dca11ab908..1b303ff6e681041b331bdcf9481c5907bf9b9098 100644 (file)
@@ -16,6 +16,7 @@
 
 package com.intellij.application.options.colors;
 
+import com.intellij.codeInsight.daemon.impl.SeverityRegistrar;
 import com.intellij.codeInsight.daemon.impl.TrafficLightRenderer;
 import com.intellij.openapi.application.ApplicationNamesInfo;
 import com.intellij.openapi.editor.*;
@@ -56,7 +57,7 @@ public class FontEditorPreview implements PreviewPanel{
 
   static void installTrafficLights(EditorEx editor) {
     ErrorStripeRenderer renderer = new TrafficLightRenderer(null,null,null,null){
-      protected DaemonCodeAnalyzerStatus getDaemonCodeAnalyzerStatus(boolean fillErrorsCount) {
+      protected DaemonCodeAnalyzerStatus getDaemonCodeAnalyzerStatus(boolean fillErrorsCount, SeverityRegistrar severityRegistrar) {
         DaemonCodeAnalyzerStatus status = new DaemonCodeAnalyzerStatus();
         status.errorAnalyzingFinished = true;
         status.errorCount = new int[]{1, 2};
index a743397e0ebfb61edb45e6300bcf7e6a10e0654e..f312f8105d0c5e243b6411e08fc986a013e9c585 100644 (file)
@@ -33,6 +33,7 @@ import com.intellij.util.ArrayUtil;
 import com.intellij.util.Processor;
 import com.intellij.util.ui.EmptyIcon;
 import com.intellij.util.ui.UIUtil;
+import gnu.trove.TIntArrayList;
 import org.jetbrains.annotations.NonNls;
 import org.jetbrains.annotations.Nullable;
 
@@ -40,7 +41,6 @@ import javax.swing.*;
 import java.awt.*;
 import java.net.URL;
 import java.util.ArrayList;
-import java.util.Arrays;
 import java.util.List;
 
 public class TrafficLightRenderer implements ErrorStripeRenderer {
@@ -58,12 +58,14 @@ public class TrafficLightRenderer implements ErrorStripeRenderer {
   @NonNls private static final String HTML_FOOTER = "</body></html>";
   @NonNls private static final String BR = "<br>";
   @NonNls private static final String NO_PASS_FOR_MESSAGE_KEY_SUFFIX = ".for";
+  private final SeverityRegistrar mySeverityRegistrar;
 
   public TrafficLightRenderer(Project project, DaemonCodeAnalyzerImpl highlighter, Document document, PsiFile file) {
     myProject = project;
     myDaemonCodeAnalyzer = highlighter;
     myDocument = document;
     myFile = file;
+    mySeverityRegistrar = SeverityRegistrar.getInstance(myProject);
   }
 
   public static class DaemonCodeAnalyzerStatus {
@@ -80,12 +82,13 @@ public class TrafficLightRenderer implements ErrorStripeRenderer {
       for (ProgressableTextEditorHighlightingPass passStatus : passStati) {
         s += String.format("(%s %2.0f%% %b)", passStatus.getPresentableName(), passStatus.getProgress() *100, passStatus.isFinished());
       }
+      s += "; error count: "+errorCount.length + ": "+new TIntArrayList(errorCount);
       return s;
     }
   }
 
   @Nullable
-  protected DaemonCodeAnalyzerStatus getDaemonCodeAnalyzerStatus(boolean fillErrorsCount) {
+  protected DaemonCodeAnalyzerStatus getDaemonCodeAnalyzerStatus(boolean fillErrorsCount, SeverityRegistrar severityRegistrar) {
     if (myFile == null || myProject.isDisposed() || !myDaemonCodeAnalyzer.isHighlightingAvailable(myFile)) return null;
 
     List<String> noInspectionRoots = new ArrayList<String>();
@@ -103,7 +106,6 @@ public class TrafficLightRenderer implements ErrorStripeRenderer {
     status.noInspectionRoots = noInspectionRoots.isEmpty() ? null : ArrayUtil.toStringArray(noInspectionRoots);
     status.noHighlightingRoots = noHighlightingRoots.isEmpty() ? null : ArrayUtil.toStringArray(noHighlightingRoots);
 
-    final SeverityRegistrar severityRegistrar = SeverityRegistrar.getInstance(myProject);
     status.errorCount = new int[severityRegistrar.getSeveritiesCount()];
     status.rootsNumber = roots.length;
     fillDaemonCodeAnalyzerErrorsStatus(status, fillErrorsCount, severityRegistrar);
@@ -123,7 +125,6 @@ public class TrafficLightRenderer implements ErrorStripeRenderer {
   protected void fillDaemonCodeAnalyzerErrorsStatus(final DaemonCodeAnalyzerStatus status,
                                                     final boolean fillErrorsCount,
                                                     final SeverityRegistrar severityRegistrar) {
-    if (fillErrorsCount) Arrays.fill(status.errorCount, 0);
     final int count = severityRegistrar.getSeveritiesCount() - 1;
     final HighlightSeverity maxPossibleSeverity = severityRegistrar.getSeverityByIndex(count);
     final HighlightSeverity[] maxFoundSeverity = {null};
@@ -138,13 +139,12 @@ public class TrafficLightRenderer implements ErrorStripeRenderer {
           }
         }
         else {
+          if (maxFoundSeverity[0] == null || severityRegistrar.compare(maxFoundSeverity[0], infoSeverity) < 0) {
+            maxFoundSeverity[0] = infoSeverity;
+          }
           if (infoSeverity == maxPossibleSeverity) {
-            status.errorCount[count] = 1;
             return false;
           }
-          if (maxFoundSeverity[0] == null || severityRegistrar.compare(maxFoundSeverity[0], infoSeverity) <= 0) {
-            maxFoundSeverity[0] = infoSeverity;
-          }
         }
         return true;
       }
@@ -162,7 +162,7 @@ public class TrafficLightRenderer implements ErrorStripeRenderer {
   }
 
   public String getTooltipMessage() {
-    DaemonCodeAnalyzerStatus status = getDaemonCodeAnalyzerStatus(true);
+    DaemonCodeAnalyzerStatus status = getDaemonCodeAnalyzerStatus(true, mySeverityRegistrar);
 
     if (status == null) return null;
     @NonNls String text = HTML_HEADER;
@@ -193,7 +193,7 @@ public class TrafficLightRenderer implements ErrorStripeRenderer {
     int currentSeverityErrors = 0;
     for (int i = status.errorCount.length - 1; i >= 0; i--) {
       if (status.errorCount[i] > 0) {
-        final HighlightSeverity severity = SeverityRegistrar.getInstance(myProject).getSeverityByIndex(i);
+        final HighlightSeverity severity = mySeverityRegistrar.getSeverityByIndex(i);
         text += BR;
         String name = status.errorCount[i] > 1 ? StringUtil.pluralize(severity.toString().toLowerCase()) : severity.toString().toLowerCase();
         text += status.errorAnalyzingFinished
@@ -255,7 +255,7 @@ public class TrafficLightRenderer implements ErrorStripeRenderer {
   }
 
   private Icon getIcon() {
-    DaemonCodeAnalyzerStatus status = getDaemonCodeAnalyzerStatus(false);
+    DaemonCodeAnalyzerStatus status = getDaemonCodeAnalyzerStatus(false, mySeverityRegistrar);
 
     if (status == null) {
       return NO_ICON;
@@ -264,23 +264,15 @@ public class TrafficLightRenderer implements ErrorStripeRenderer {
       return NO_ANALYSIS_ICON;
     }
 
-    boolean atLeastOnePassFinished = status.errorAnalyzingFinished;
-    for (ProgressableTextEditorHighlightingPass passStatus : status.passStati) {
-      atLeastOnePassFinished |= passStatus.isFinished();
-    }
     Icon icon = HighlightDisplayLevel.DO_NOT_SHOW.getIcon();
-    if (atLeastOnePassFinished) {
-      SeverityRegistrar severityRegistrar = SeverityRegistrar.getInstance(myProject);
-      for (int i = status.errorCount.length - 1; i >= 0; i--) {
-        if (status.errorCount[i] != 0) {
-          icon = severityRegistrar.getRendererIconByIndex(i);
-          break;
-        }
+    for (int i = status.errorCount.length - 1; i >= 0; i--) {
+      if (status.errorCount[i] != 0) {
+        icon = mySeverityRegistrar.getRendererIconByIndex(i);
+        break;
       }
     }
 
     if (status.errorAnalyzingFinished) return icon;
-      //icon = HighlightDisplayLevel.createIconByMask(Color.blue);
     double progress = getOverallProgress(status);
     TruncatingIcon trunc = new TruncatingIcon(icon, (int)(icon.getIconWidth() * progress), (int)(icon.getIconHeight() * progress));
     return LayeredIcon.create(trunc, STARING_EYE_ICON);
index dd79df6486962223202dd1cdfb3a2f82e2a332ec..255fec49dda06a6a7e893f48e802edac1a9c63f1 100644 (file)
@@ -42,7 +42,9 @@ public class TrafficTooltipRendererImpl implements TrafficTooltipRenderer {
   @Override
   public void repaintTooltipWindow() {
     if (myPanel != null) {
-      myPanel.updatePanel(myTrafficLightRenderer.getDaemonCodeAnalyzerStatus(true));
+      myPanel.updatePanel(myTrafficLightRenderer.getDaemonCodeAnalyzerStatus(true,
+                                                                             SeverityRegistrar.getInstance(
+                                                                               myTrafficLightRenderer.getProject())));
     }
   }
 
index 733c610cded82aa7607b9a2abf6767fe559a0ec2..a66f6de4a6c1ba699ba957d6be8e14b13fc4dc01 100644 (file)
 
 package com.intellij.ide.actions;
 
-import com.intellij.CommonBundle;
-import com.intellij.history.LocalHistory;
-import com.intellij.history.LocalHistoryAction;
 import com.intellij.ide.IdeBundle;
 import com.intellij.ide.IdeView;
 import com.intellij.ide.util.DirectoryChooserUtil;
-import com.intellij.ide.util.DirectoryUtil;
 import com.intellij.openapi.actionSystem.*;
-import com.intellij.openapi.application.ApplicationManager;
-import com.intellij.openapi.command.CommandProcessor;
-import com.intellij.openapi.fileTypes.FileTypeManager;
 import com.intellij.openapi.project.DumbAware;
 import com.intellij.openapi.project.Project;
-import com.intellij.openapi.ui.InputValidatorEx;
 import com.intellij.openapi.ui.Messages;
 import com.intellij.psi.PsiDirectory;
 import com.intellij.psi.PsiElement;
 import com.intellij.psi.impl.file.PsiDirectoryFactory;
 import com.intellij.util.Icons;
-import com.intellij.util.IncorrectOperationException;
-
-import java.io.File;
 
 public class CreateDirectoryOrPackageAction extends AnAction implements DumbAware {
   public CreateDirectoryOrPackageAction() {
@@ -45,40 +34,39 @@ public class CreateDirectoryOrPackageAction extends AnAction implements DumbAwar
   }
 
   public void actionPerformed(AnActionEvent e) {
-    DataContext dataContext = e.getDataContext();
-
-    IdeView view = LangDataKeys.IDE_VIEW.getData(dataContext);
-    Project project = PlatformDataKeys.PROJECT.getData(dataContext);
+    IdeView view = e.getData(LangDataKeys.IDE_VIEW);
+    Project project = e.getData(PlatformDataKeys.PROJECT);
 
     PsiDirectory directory = DirectoryChooserUtil.getOrChooseDirectory(view);
 
     if (directory == null) return;
     boolean isDirectory = !PsiDirectoryFactory.getInstance(project).isPackage(directory);
 
-    MyInputValidator validator = new MyInputValidator(project, directory, isDirectory);
+    CreateDirectoryOrPackageHandler validator = new CreateDirectoryOrPackageHandler(project, directory, isDirectory,
+                                                                                   isDirectory ? "\\/" : ".");
     Messages.showInputDialog(project, isDirectory
                                       ? IdeBundle.message("prompt.enter.new.directory.name")
                                       : IdeBundle.message("prompt.enter.new.package.name"),
                                       isDirectory ? IdeBundle.message("title.new.directory") : IdeBundle.message("title.new.package"),
                                       Messages.getQuestionIcon(), "", validator);
 
-    if (validator.myCreatedElement == null) return;
-
-    view.selectElement(validator.myCreatedElement);
+    final PsiElement result = validator.getCreatedElement();
+    if (result != null && view != null) {
+      view.selectElement(result);
+    }
   }
 
   public void update(AnActionEvent event) {
     Presentation presentation = event.getPresentation();
-    DataContext dataContext = event.getDataContext();
 
-    Project project = PlatformDataKeys.PROJECT.getData(dataContext);
+    Project project = event.getData(PlatformDataKeys.PROJECT);
     if (project == null) {
       presentation.setVisible(false);
       presentation.setEnabled(false);
       return;
     }
 
-    IdeView view = LangDataKeys.IDE_VIEW.getData(dataContext);
+    IdeView view = event.getData(LangDataKeys.IDE_VIEW);
     if (view == null) {
       presentation.setVisible(false);
       presentation.setEnabled(false);
@@ -114,100 +102,4 @@ public class CreateDirectoryOrPackageAction extends AnAction implements DumbAwar
     }
   }
 
-  protected class MyInputValidator implements InputValidatorEx {
-    private final Project myProject;
-    private final PsiDirectory myDirectory;
-    private final boolean myIsDirectory;
-    private PsiElement myCreatedElement = null;
-
-    public MyInputValidator(Project project, PsiDirectory directory, boolean isDirectory) {
-      myProject = project;
-      myDirectory = directory;
-      myIsDirectory = isDirectory;
-    }
-
-    public boolean checkInput(String inputString) {
-      return true;
-    }
-
-    public String getErrorText(String inputString) {
-      if (FileTypeManager.getInstance().isFileIgnored(inputString)) {
-        return "Trying to create a " + (myIsDirectory ? "directory" : "package") + " with ignored name, result will not be visible";
-      }
-      if (!myIsDirectory && inputString.length() > 0 && !PsiDirectoryFactory.getInstance(myProject).isValidPackageName(inputString)) {
-        return "Not a valid package name";
-      }
-      return null;
-    }
-
-    public boolean canClose(String inputString) {
-      final String subDirName = inputString;
-
-      if (subDirName.length() == 0) {
-        Messages.showMessageDialog(myProject, IdeBundle.message("error.name.should.be.specified"), CommonBundle.getErrorTitle(),
-                                   Messages.getErrorIcon());
-        return false;
-      }
-
-      final boolean multiCreation = myIsDirectory
-                                    ? subDirName.indexOf('/') != -1 || subDirName.indexOf('\\') != -1
-                                    : subDirName.indexOf('.') != -1;
-
-      if (!multiCreation) {
-        try {
-          myDirectory.checkCreateSubdirectory(subDirName);
-        }
-        catch (IncorrectOperationException ex) {
-          Messages.showMessageDialog(myProject, CreateElementActionBase.filterMessage(ex.getMessage()), CommonBundle.getErrorTitle(),
-                                     Messages.getErrorIcon());
-          return false;
-        }
-      }
-
-      Runnable command = new Runnable() {
-        public void run() {
-          final Runnable run = new Runnable() {
-            public void run() {
-              LocalHistoryAction action = LocalHistoryAction.NULL;
-              try {
-                String actionName;
-                String dirPath = myDirectory.getVirtualFile().getPresentableUrl();
-                actionName = IdeBundle.message("progress.creating.directory", dirPath, File.separator, subDirName);
-                action = LocalHistory.getInstance().startAction(actionName);
-
-                final PsiDirectory createdDir;
-                if (myIsDirectory) {
-                  createdDir = DirectoryUtil.createSubdirectories(subDirName, myDirectory, "\\/");
-                }
-                else {
-                  createdDir = DirectoryUtil.createSubdirectories(subDirName, myDirectory, ".");
-                }
-
-
-                myCreatedElement = createdDir;
-
-              }
-              catch (final IncorrectOperationException ex) {
-                ApplicationManager.getApplication().invokeLater(new Runnable() {
-                  public void run() {
-                    Messages.showMessageDialog(myProject, CreateElementActionBase.filterMessage(ex.getMessage()),
-                                               CommonBundle.getErrorTitle(), Messages.getErrorIcon());
-                  }
-                });
-              }
-              finally {
-                action.finish();
-              }
-            }
-          };
-          ApplicationManager.getApplication().runWriteAction(run);
-        }
-      };
-      CommandProcessor.getInstance().executeCommand(myProject, command, myIsDirectory
-                                                                        ? IdeBundle.message("command.create.directory")
-                                                                        : IdeBundle.message("command.create.package"), null);
-
-      return myCreatedElement != null;
-    }
-  }
 }
diff --git a/platform/lang-impl/src/com/intellij/ide/actions/CreateDirectoryOrPackageHandler.java b/platform/lang-impl/src/com/intellij/ide/actions/CreateDirectoryOrPackageHandler.java
new file mode 100644 (file)
index 0000000..f820d62
--- /dev/null
@@ -0,0 +1,125 @@
+/*
+ * 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.ide.actions;
+
+import com.intellij.CommonBundle;
+import com.intellij.history.LocalHistory;
+import com.intellij.history.LocalHistoryAction;
+import com.intellij.ide.IdeBundle;
+import com.intellij.ide.util.DirectoryUtil;
+import com.intellij.openapi.application.ApplicationManager;
+import com.intellij.openapi.command.CommandProcessor;
+import com.intellij.openapi.fileTypes.FileTypeManager;
+import com.intellij.openapi.project.Project;
+import com.intellij.openapi.ui.InputValidatorEx;
+import com.intellij.openapi.ui.Messages;
+import com.intellij.openapi.util.text.StringUtil;
+import com.intellij.psi.PsiDirectory;
+import com.intellij.psi.impl.file.PsiDirectoryFactory;
+import com.intellij.util.IncorrectOperationException;
+
+import java.io.File;
+
+public class CreateDirectoryOrPackageHandler implements InputValidatorEx {
+  private final Project myProject;
+  private final PsiDirectory myDirectory;
+  private final boolean myIsDirectory;
+  private PsiDirectory myCreatedElement = null;
+  private String myDelimiters;
+
+  public CreateDirectoryOrPackageHandler(Project project, PsiDirectory directory, boolean isDirectory, final String delimiters) {
+    myProject = project;
+    myDirectory = directory;
+    myIsDirectory = isDirectory;
+    myDelimiters = delimiters;
+  }
+
+  public boolean checkInput(String inputString) {
+    return true;
+  }
+
+  public String getErrorText(String inputString) {
+    if (FileTypeManager.getInstance().isFileIgnored(inputString)) {
+      return "Trying to create a " + (myIsDirectory ? "directory" : "package") + " with ignored name, result will not be visible";
+    }
+    if (!myIsDirectory && inputString.length() > 0 && !PsiDirectoryFactory.getInstance(myProject).isValidPackageName(inputString)) {
+      return "Not a valid package name";
+    }
+    return null;
+  }
+
+  public boolean canClose(String inputString) {
+    final String subDirName = inputString;
+
+    if (subDirName.length() == 0) {
+      Messages.showMessageDialog(myProject, IdeBundle.message("error.name.should.be.specified"), CommonBundle.getErrorTitle(),
+                                 Messages.getErrorIcon());
+      return false;
+    }
+
+    final boolean multiCreation = StringUtil.containsAnyChar(subDirName, myDelimiters);
+    if (!multiCreation) {
+      try {
+        myDirectory.checkCreateSubdirectory(subDirName);
+      }
+      catch (IncorrectOperationException ex) {
+        Messages.showMessageDialog(myProject, CreateElementActionBase.filterMessage(ex.getMessage()), CommonBundle.getErrorTitle(),
+                                   Messages.getErrorIcon());
+        return false;
+      }
+    }
+
+    Runnable command = new Runnable() {
+      public void run() {
+        final Runnable run = new Runnable() {
+          public void run() {
+            LocalHistoryAction action = LocalHistoryAction.NULL;
+            try {
+              String actionName;
+              String dirPath = myDirectory.getVirtualFile().getPresentableUrl();
+              actionName = IdeBundle.message("progress.creating.directory", dirPath, File.separator, subDirName);
+              action = LocalHistory.getInstance().startAction(actionName);
+
+              myCreatedElement = DirectoryUtil.createSubdirectories(subDirName, myDirectory, myDelimiters);
+
+            }
+            catch (final IncorrectOperationException ex) {
+              ApplicationManager.getApplication().invokeLater(new Runnable() {
+                public void run() {
+                  Messages.showMessageDialog(myProject, CreateElementActionBase.filterMessage(ex.getMessage()),
+                                             CommonBundle.getErrorTitle(), Messages.getErrorIcon());
+                }
+              });
+            }
+            finally {
+              action.finish();
+            }
+          }
+        };
+        ApplicationManager.getApplication().runWriteAction(run);
+      }
+    };
+    CommandProcessor.getInstance().executeCommand(myProject, command, myIsDirectory
+                                                                      ? IdeBundle.message("command.create.directory")
+                                                                      : IdeBundle.message("command.create.package"), null);
+
+    return myCreatedElement != null;
+  }
+
+  public PsiDirectory getCreatedElement() {
+    return myCreatedElement;
+  }
+}
index 296679097d83a2e3bf2c340cc9c0b1b310c8aab1..cf8d9c23e6817efd0712a42ec0015bd9f61c7a49 100644 (file)
@@ -33,6 +33,7 @@ public class DirectoryChooserUtil {
   private DirectoryChooserUtil() {
   }
 
+  @Nullable
   public static PsiDirectory getOrChooseDirectory(IdeView view) {
     PsiDirectory[] dirs = view.getDirectories();
     if (dirs.length == 0) return null;
@@ -45,6 +46,7 @@ public class DirectoryChooserUtil {
     }
   }
 
+  @Nullable
   public static PsiDirectory selectDirectory(Project project,
                                              PsiDirectory[] packageDirectories,
                                              PsiDirectory defaultDirectory,
index 3f845c451a157f9c07d776d7ea42d0cfe817ae4d..809c59bd8659f91f931b94c4d11bf8ffecac7b4f 100644 (file)
@@ -18,6 +18,7 @@ package com.intellij.injected.editor;
 
 import com.intellij.openapi.editor.FoldRegion;
 import com.intellij.openapi.editor.FoldingGroup;
+import com.intellij.openapi.editor.ex.FoldingListener;
 import com.intellij.openapi.editor.ex.FoldingModelEx;
 import com.intellij.openapi.editor.impl.FoldRegionImpl;
 import com.intellij.openapi.editor.markup.TextAttributes;
@@ -130,4 +131,14 @@ public class FoldingModelWindow implements FoldingModelEx{
     hostRegion.putUserData(FOLD_REGION_WINDOW, window);
     return window;
   }
+
+  @Override
+  public boolean addListener(@NotNull FoldingListener listener) {
+    return myDelegate.addListener(listener);
+  }
+
+  @Override
+  public boolean removeListener(@NotNull FoldingListener listener) {
+    return myDelegate.removeListener(listener);
+  }
 }
index a9bce25662fc7e504607e67d99d37cacbc3400e2..a350fd739b122352278f9ecdf54f008c2190a429 100644 (file)
@@ -216,6 +216,7 @@ public class ChangeSignatureGestureDetector extends PsiTreeChangeAdapter impleme
           final String currentCommandName = processor.getCurrentCommandName();
           if (!Comparing.strEqual(EditorBundle.message("typing.in.editor.command.name"), currentCommandName) &&
               !Comparing.strEqual(EditorBundle.message("paste.command.name"), currentCommandName) &&
+              !Comparing.strEqual(LanguageChangeSignatureDetector.MOVE_PARAMETER, currentCommandName) &&
               !Comparing.equal(EditorActionUtil.DELETE_COMMAND_GROUP, processor.getCurrentCommandGroupId())) {
             return;
           }
index 58f8cd5f4829be882401ed99897c2051b5022f72..3ad1dadf9d138e2ae510b6484afeb7fc169f63a8 100644 (file)
@@ -15,6 +15,7 @@
  */
 package com.intellij.refactoring.changeSignature;
 
+import com.intellij.openapi.editor.Editor;
 import com.intellij.openapi.util.TextRange;
 import com.intellij.psi.PsiElement;
 import org.jetbrains.annotations.NotNull;
@@ -25,7 +26,7 @@ import org.jetbrains.annotations.Nullable;
  * Date: Sep 6, 2010
  */
 public interface LanguageChangeSignatureDetector {
-
+  String MOVE_PARAMETER = "Parameter Move";
 
   @Nullable
   ChangeInfo createCurrentChangeSignature(final @NotNull PsiElement element,
@@ -39,4 +40,8 @@ public interface LanguageChangeSignatureDetector {
   TextRange getHighlightingRange(PsiElement element);
 
   boolean wasBanned(PsiElement element, @NotNull ChangeInfo bannedInfo);
+
+  boolean isMoveParameterAvailable(PsiElement parameter, boolean left);
+
+  void moveParameter(PsiElement parameter, Editor editor, boolean left);
 }
diff --git a/platform/lang-impl/src/com/intellij/refactoring/changeSignature/MoveParameterAction.java b/platform/lang-impl/src/com/intellij/refactoring/changeSignature/MoveParameterAction.java
new file mode 100644 (file)
index 0000000..b43f4bf
--- /dev/null
@@ -0,0 +1,47 @@
+package com.intellij.refactoring.changeSignature;
+
+import com.intellij.openapi.actionSystem.*;
+import com.intellij.openapi.diagnostic.Logger;
+import com.intellij.openapi.editor.Editor;
+import com.intellij.psi.PsiElement;
+
+/**
+ * User: anna
+ * Date: Sep 10, 2010
+ */
+public abstract class MoveParameterAction extends AnAction{
+  private final boolean myLeft;
+  private static final Logger LOG = Logger.getInstance("#" + MoveParameterAction.class.getName());
+
+  public MoveParameterAction(boolean left) {
+    super();
+    myLeft = left;
+  }
+
+  @Override
+  public void actionPerformed(AnActionEvent e) {
+    final DataContext dataContext = e.getDataContext();
+    final PsiElement psiElement = LangDataKeys.PSI_ELEMENT.getData(dataContext);
+    LOG.assertTrue(psiElement != null);
+    final Editor editor = PlatformDataKeys.EDITOR.getData(dataContext);
+    LanguageChangeSignatureDetectors.INSTANCE.forLanguage(psiElement.getLanguage()).moveParameter(psiElement, editor, myLeft);
+  }
+
+
+  @Override
+  public void update(AnActionEvent e) {
+    final Presentation presentation = e.getPresentation();
+    presentation.setEnabled(false);
+    final DataContext dataContext = e.getDataContext();
+    final Editor editor = PlatformDataKeys.EDITOR.getData(dataContext);
+    if (editor != null) {
+      final PsiElement psiElement = LangDataKeys.PSI_ELEMENT.getData(dataContext);
+      if (psiElement != null) {
+        final LanguageChangeSignatureDetector detector = LanguageChangeSignatureDetectors.INSTANCE.forLanguage(psiElement.getLanguage());
+        if (detector != null) {
+          presentation.setEnabled(detector.isMoveParameterAvailable(psiElement, myLeft));
+        }
+      }
+    }
+  }
+}
diff --git a/platform/lang-impl/src/com/intellij/refactoring/changeSignature/MoveParameterLeftAction.java b/platform/lang-impl/src/com/intellij/refactoring/changeSignature/MoveParameterLeftAction.java
new file mode 100644 (file)
index 0000000..c380d37
--- /dev/null
@@ -0,0 +1,26 @@
+/*
+ * 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.refactoring.changeSignature;
+
+/**
+ * User: anna
+ * Date: Sep 10, 2010
+ */
+public class MoveParameterLeftAction extends MoveParameterAction {
+  public MoveParameterLeftAction() {
+    super(true);
+  }
+}
diff --git a/platform/lang-impl/src/com/intellij/refactoring/changeSignature/MoveParameterRightAction.java b/platform/lang-impl/src/com/intellij/refactoring/changeSignature/MoveParameterRightAction.java
new file mode 100644 (file)
index 0000000..8f2eb78
--- /dev/null
@@ -0,0 +1,26 @@
+/*
+ * 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.refactoring.changeSignature;
+
+/**
+ * User: anna
+ * Date: Sep 10, 2010
+ */
+public class MoveParameterRightAction extends MoveParameterAction {
+  public MoveParameterRightAction() {
+    super(false);
+  }
+}
index 3dfca1588ecb72b005aca3a53c057eda7494e126..8ca4d2f6d9cb0e59f47aeb46c285ab0ceadc7257 100644 (file)
@@ -20,14 +20,12 @@ import com.intellij.openapi.editor.colors.CodeInsightColors;
 import com.intellij.openapi.util.Comparing;
 import com.intellij.util.ImageLoader;
 import com.intellij.util.containers.HashMap;
-import com.intellij.util.ui.EmptyIcon;
 
 import javax.swing.*;
 import java.awt.*;
 import java.util.Map;
 
 public class HighlightDisplayLevel {
-  private static final Icon EMPTY = new EmptyIcon(12, 12);
   private static final Map<HighlightSeverity, HighlightDisplayLevel> ourMap = new HashMap<HighlightSeverity, HighlightDisplayLevel>();
 
   public static final HighlightDisplayLevel GENERIC_SERVER_ERROR_OR_WARNING = new HighlightDisplayLevel(HighlightSeverity.GENERIC_SERVER_ERROR_OR_WARNING,
@@ -88,6 +86,7 @@ public class HighlightDisplayLevel {
     private static final Image ourErrorMaskImage = ImageLoader.loadFromResource("/general/errorMask.png");
   }
 
+  private static final int EMPTY_ICON_DIM = 12;
   public static Icon createIconByMask(final Color renderColor) {
     return new Icon() {
       public void paintIcon(Component c, Graphics g, int x, int y) {
@@ -97,12 +96,12 @@ public class HighlightDisplayLevel {
 
 
       public int getIconWidth() {
-        return EMPTY.getIconWidth();
+        return EMPTY_ICON_DIM;
       }
 
 
       public int getIconHeight() {
-        return EMPTY.getIconHeight();
+        return EMPTY_ICON_DIM;
       }
     };
   }
index 56c893eb981848d554f425fd1d8e190e4a3325a6..7dd4a094ce6bbfd7c8501bd992f21204c0e45283 100644 (file)
@@ -3919,7 +3919,12 @@ public class AbstractTreeUi {
       });
     }
     catch (ProcessCanceledException e) {
-      runDone(onDone);
+      try {
+        runDone(onDone);
+      }
+      catch (ProcessCanceledException ignored) {
+        //todo[kirillk] added by Nik to fix IDEA-58475. I'm not sure that it is correct solution
+      }
     }
   }
 
index f91c0113550fd6d1193241248e04e76c0ea19b2c..65d2ece59df7894e66430c04ebc733671673c0a8 100644 (file)
@@ -66,10 +66,16 @@ public class DefaultLineWrapPositionStrategy implements LineWrapPositionStrategy
       return endOffset;
     }
 
+    // Normalization.
     int maxPreferredOffsetToUse = maxPreferredOffset >= endOffset ? endOffset - 1 : maxPreferredOffset;
+    maxPreferredOffsetToUse = maxPreferredOffsetToUse < startOffset ? startOffset : maxPreferredOffsetToUse;
+
     // Try to find target offset that is not greater than preferred position.
     for (int i = maxPreferredOffsetToUse; i > startOffset; i--) {
       char c = text.charAt(i);
+      if (c == '\n') {
+        return i + 1;
+      }
 
       if (WHITE_SPACES.contains(c)) {
         return i < maxPreferredOffsetToUse ? i + 1 : i;
@@ -100,6 +106,10 @@ public class DefaultLineWrapPositionStrategy implements LineWrapPositionStrategy
     // Try to find target offset that is greater than preferred position.
     for (int i = maxPreferredOffsetToUse + 1; i < endOffset; i++) {
       char c = text.charAt(i);
+      if (c == '\n') {
+        return i + 1;
+      }
+
       if (WHITE_SPACES.contains(c)) {
         return i;
       }
@@ -120,7 +130,8 @@ public class DefaultLineWrapPositionStrategy implements LineWrapPositionStrategy
         return i;
       }
     }
-    return maxPreferredOffset;
+
+    return allowToBeyondMaxPreferredOffset ? endOffset : maxPreferredOffset;
   }
 
   private static boolean isIdSymbol(char c) {
index 529bca30f2559297cf48e911cc6245661481d657..3c654ac42bc976575034d79ffbeae0a5e44aae24 100644 (file)
@@ -249,12 +249,14 @@ public class EditorModificationUtil {
 
     int caretOffset = caretModel.getOffset();
     int anchorLineEndOffset = document.getLineEndOffset(lineNumber);
-    List<? extends TextChange> softWraps = editor.getSoftWrapModel().getSoftWrapsForLine(logicalPosition.line);
-    for (TextChange softWrap : softWraps) {
+    List<? extends SoftWrap> softWraps = editor.getSoftWrapModel().getSoftWrapsForLine(logicalPosition.line);
+    for (SoftWrap softWrap : softWraps) {
       if (!editor.getSoftWrapModel().isVisible(softWrap)) {
         continue;
       }
-      if (softWrap.getStart() == caretOffset) {
+
+      int softWrapOffset = softWrap.getStart();
+      if (softWrapOffset == caretOffset) {
         // There are two possible situations:
         //     *) caret is located on a visual line before soft wrap-introduced line feed;
         //     *) caret is located on a visual line after soft wrap-introduced line feed;
@@ -264,17 +266,17 @@ public class EditorModificationUtil {
           return visualCaret.column - position.column - 1;
         }
       }
-      if (softWrap.getStart() > caretOffset) {
-        anchorLineEndOffset = softWrap.getStart();
+      if (softWrapOffset > caretOffset) {
+        anchorLineEndOffset = softWrapOffset;
         break;
       }
 
       // Same offset corresponds to all soft wrap-introduced symbols, however, current method should behave differently in
       // situations when the caret is located just before the soft wrap and at the next visual line.
-      if (softWrap.getStart() == caretOffset) {
+      if (softWrapOffset == caretOffset) {
         boolean visuallyBeforeSoftWrap = caretModel.getVisualPosition().line < editor.offsetToVisualPosition(caretOffset).line;
         if (visuallyBeforeSoftWrap) {
-          anchorLineEndOffset = softWrap.getStart();
+          anchorLineEndOffset = softWrapOffset;
           break;
         }
       }
diff --git a/platform/platform-api/src/com/intellij/openapi/editor/SoftWrap.java b/platform/platform-api/src/com/intellij/openapi/editor/SoftWrap.java
new file mode 100644 (file)
index 0000000..6a0a924
--- /dev/null
@@ -0,0 +1,36 @@
+/*
+ * 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;
+
+/**
+ * Defines common interface for <code>'soft wrap'</code>, i.e. for virtual line break that doesn't present at actual file on a disk
+ * but is used exclusively during document representation.
+ *
+ * @author Denis Zhdanov
+ * @since Aug 30, 2010 6:07:00 PM
+ */
+public interface SoftWrap extends TextChange {
+
+  /**
+   * @return    number of columns between current soft wrap end and first column on a visual line
+   */
+  int getIndentInColumns();
+
+  /**
+   * @return    number of pixels between current soft wrap end and first column on a visual line
+   */
+  int getIndentInPixels();
+}
index cc03a7605ba085eaf02ea2daba57c0a8f64480aa..05f7b6e53745241a48382c50e006c2458cc42fbe 100644 (file)
@@ -85,7 +85,7 @@ public interface SoftWrapModel {
    * @return            soft wrap registered for the given offset within the current model if any; <code>null</code> otherwise
    */
   @Nullable
-  TextChange getSoftWrap(int offset);
+  SoftWrap getSoftWrap(int offset);
 
   /**
    * Allows to ask current model about all soft wraps registered for the given document offsets range.
@@ -95,7 +95,7 @@ public interface SoftWrapModel {
    * @return        all soft wraps registered for the target document offsets range
    */
   @NotNull
-  List<? extends TextChange> getSoftWrapsForRange(int start, int end);
+  List<? extends SoftWrap> getSoftWrapsForRange(int start, int end);
 
   /**
    * Allows to ask current model about all soft wraps registered for the given document line.
@@ -104,7 +104,7 @@ public interface SoftWrapModel {
    * @return                all soft wraps registered for the given document line
    */
   @NotNull
-  List<? extends TextChange> getSoftWrapsForLine(int documentLine);
+  List<? extends SoftWrap> getSoftWrapsForLine(int documentLine);
 
   /**
    * Allows to answer if given soft wrap is shown.
@@ -114,7 +114,7 @@ public interface SoftWrapModel {
    * @param softWrap    soft wrap to check
    * @return            <code>true</code> if given soft wrap is visible; <code>false</code> otherwise
    */
-  boolean isVisible(TextChange softWrap);
+  boolean isVisible(SoftWrap softWrap);
 
   /**
    * Notifies current model that target document is about to be changed at current caret location.
@@ -143,25 +143,7 @@ public interface SoftWrapModel {
   boolean isInsideOrBeforeSoftWrap(@NotNull VisualPosition visual);
 
   /**
-   * Asks current model to calculate indent width of the given soft wrap in pixels.
-   * <p/>
-   * <code>'Soft wrap indent width'</code> here means visual width of soft wrap-introduced editor space on a line
-   * that contains document text just after soft wrap. Basically, resulting value is a resulting width sum of soft
-   * wrap symbols that follow last soft wrap line feed plus <code>'after soft wrap'</code> drawing width.
-   *
-   * @param softWrap    soft wrap which indent width should be calculated
-   * @return            indent width in pixels for the given soft wrap
-   */
-  int getSoftWrapIndentWidthInPixels(@NotNull TextChange softWrap);
-
-  /**
-   * Asks current model to calculate indent width of the given soft wrap in {@link VisualPosition#column columns}.
-   * <p/>
-   * <code>'Soft wrap indent width'</code> here means number of soft wrap symbols that follow last soft wrap line
-   * feed plus one symbol for <code>'after soft wrap'</code> drawing.
-   *
-   * @param softWrap    soft wrap which indent width should be calculated
-   * @return            indent width in columns for the given soft wrap
+   * Callback method to ask soft wrap model to release all resources.
    */
-  int getSoftWrapIndentWidthInColumns(@NotNull TextChange softWrap);
+  void release();
 }
index fb00b2afd3a830fa90d5c9639d3c54b6c386f95a..43459f96a7f6b0b50866f67d03ac59fb141b7259 100644 (file)
@@ -122,6 +122,7 @@ public class StringPattern extends ObjectPattern<String, StringPattern> {
         sb.append("<whitespace>");
       }
       else
+      //This is really stupid and inconvinient builder - it breaks any normal pattern with uppercase
       if(Character.isUpperCase(c)) {
         sb.append('[').append(Character.toUpperCase(c)).append(Character.toLowerCase(c)).append(']');
       }
diff --git a/platform/platform-impl/src/com/intellij/codeInsight/hint/CustomHrView.java b/platform/platform-impl/src/com/intellij/codeInsight/hint/CustomHrView.java
new file mode 100644 (file)
index 0000000..2be2747
--- /dev/null
@@ -0,0 +1,238 @@
+/*
+ * @(#)HRuleView.java  1.33 05/11/17
+ *
+ * Copyright 2006 Sun Microsystems, Inc. All rights reserved.
+ * SUN PROPRIETARY/CONFIDENTIAL. Use is subject to license terms.
+ */
+package com.intellij.codeInsight.hint;
+
+import java.awt.*;
+import javax.swing.event.DocumentEvent;
+import javax.swing.text.*;
+import javax.swing.text.html.CSS;
+import javax.swing.text.html.HTML;
+import javax.swing.text.html.HTMLDocument;
+import javax.swing.text.html.StyleSheet;
+import java.util.Enumeration;
+import java.lang.Integer;
+
+/**
+ * A view implementation to display an html horizontal
+ * rule.
+ *
+ * @author Timothy Prinzing
+ * @author Sara Swanson
+ * @version 1.33 11/17/05
+ */
+class CustomHrView extends View {
+
+  private Color myColor;
+
+  /**
+   * Creates a new view that represents an &lt;hr&gt; element.
+   *
+   * @param elem the element to create a view for
+   */
+  public CustomHrView(Element elem, Color color) {
+    super(elem);
+    myColor = color;
+  }
+
+
+  public void paint(Graphics g, Shape a) {
+    Rectangle alloc = (a instanceof Rectangle) ? (Rectangle)a : a.getBounds();
+    int x = 0;
+    int y = alloc.y + SPACE_ABOVE + (int)topMargin;
+    int width = alloc.width - (int)(leftMargin + rightMargin);
+    int height = 1;
+    if (size > 0) height = size;
+
+    // Align the rule horizontally.
+    switch (alignment) {
+      case StyleConstants.ALIGN_CENTER:
+        x = alloc.x + (alloc.width / 2) - (width / 2);
+        break;
+      case StyleConstants.ALIGN_RIGHT:
+        x = alloc.x + alloc.width - width - (int)rightMargin;
+        break;
+      case StyleConstants.ALIGN_LEFT:
+      default:
+        x = alloc.x + (int)leftMargin;
+        break;
+    }
+
+    // Paint either a shaded rule or a solid line.
+    if (noshade != null) {
+      g.setColor(myColor);
+      g.fillRect(x, y, width, height);
+    }
+    else {
+      Color bg = getContainer().getBackground();
+      Color bottom, top;
+      if (bg == null || bg.equals(Color.white)) {
+        top = Color.darkGray;
+        bottom = Color.lightGray;
+      }
+      else {
+        top = Color.darkGray;
+        bottom = Color.white;
+      }
+      g.setColor(bottom);
+      g.drawLine(x + width - 1, y, x + width - 1, y + height - 1);
+      g.drawLine(x, y + height - 1, x + width - 1, y + height - 1);
+      g.setColor(top);
+      g.drawLine(x, y, x + width - 1, y);
+      g.drawLine(x, y, x, y + height - 1);
+    }
+
+  }
+
+
+  /**
+   * Calculates the desired shape of the rule... this is
+   * basically the preferred size of the border.
+   *
+   * @param axis may be either X_AXIS or Y_AXIS
+   * @return the desired span
+   * @see View#getPreferredSpan
+   */
+  public float getPreferredSpan(int axis) {
+    switch (axis) {
+      case View.X_AXIS:
+        return 1;
+      case View.Y_AXIS:
+        if (size > 0) {
+          return size + SPACE_ABOVE + SPACE_BELOW + topMargin + bottomMargin;
+        }
+        else {
+          if (noshade != null) {
+            return 2 + SPACE_ABOVE + SPACE_BELOW + topMargin + bottomMargin;
+          }
+          else {
+            return SPACE_ABOVE + SPACE_BELOW + topMargin + bottomMargin;
+          }
+        }
+      default:
+        throw new IllegalArgumentException("Invalid axis: " + axis);
+    }
+  }
+
+  /**
+   * Gets the resize weight for the axis.
+   * The rule is: rigid vertically and flexible horizontally.
+   *
+   * @param axis may be either X_AXIS or Y_AXIS
+   * @return the weight
+   */
+  public int getResizeWeight(int axis) {
+    if (axis == View.X_AXIS) {
+      return 1;
+    }
+    else if (axis == View.Y_AXIS) {
+      return 0;
+    }
+    else {
+      return 0;
+    }
+  }
+
+  /**
+   * Determines how attractive a break opportunity in
+   * this view is.  This is implemented to request a forced break.
+   *
+   * @param axis may be either View.X_AXIS or View.Y_AXIS
+   * @param pos  the potential location of the start of the
+   *             broken view (greater than or equal to zero).
+   *             This may be useful for calculating tab
+   *             positions.
+   * @param len  specifies the relative length from <em>pos</em>
+   *             where a potential break is desired. The value must be greater
+   *             than or equal to zero.
+   * @return the weight, which should be a value between
+   *         ForcedBreakWeight and BadBreakWeight.
+   */
+  public int getBreakWeight(int axis, float pos, float len) {
+    if (axis == X_AXIS) {
+      return ForcedBreakWeight;
+    }
+    return BadBreakWeight;
+  }
+
+  public View breakView(int axis, int offset, float pos, float len) {
+    return null;
+  }
+
+  /**
+   * Provides a mapping from the document model coordinate space
+   * to the coordinate space of the view mapped to it.
+   *
+   * @param pos the position to convert
+   * @param a   the allocated region to render into
+   * @return the bounding box of the given position
+   * @throws BadLocationException if the given position does not
+   *                              represent a valid location in the associated document
+   * @see View#modelToView
+   */
+  public Shape modelToView(int pos, Shape a, Position.Bias b) throws BadLocationException {
+    int p0 = getStartOffset();
+    int p1 = getEndOffset();
+    if ((pos >= p0) && (pos <= p1)) {
+      Rectangle r = a.getBounds();
+      if (pos == p1) {
+        r.x += r.width;
+      }
+      r.width = 0;
+      return r;
+    }
+    return null;
+  }
+
+  /**
+   * Provides a mapping from the view coordinate space to the logical
+   * coordinate space of the model.
+   *
+   * @param x the X coordinate
+   * @param y the Y coordinate
+   * @param a the allocated region to render into
+   * @return the location within the model that best represents the
+   *         given point of view
+   * @see View#viewToModel
+   */
+  public int viewToModel(float x, float y, Shape a, Position.Bias[] bias) {
+    Rectangle alloc = (Rectangle)a;
+    if (x < alloc.x + (alloc.width / 2)) {
+      bias[0] = Position.Bias.Forward;
+      return getStartOffset();
+    }
+    bias[0] = Position.Bias.Backward;
+    return getEndOffset();
+  }
+
+  /**
+   * Fetches the attributes to use when rendering.  This is
+   * implemented to multiplex the attributes specified in the
+   * model with a StyleSheet.
+   */
+  public AttributeSet getAttributes() {
+    return attr;
+  }
+
+  // --- variables ------------------------------------------------
+
+  private float topMargin;
+  private float bottomMargin;
+  private float leftMargin;
+  private float rightMargin;
+  private int alignment = StyleConstants.ALIGN_CENTER;
+  private String noshade = "true";
+  private int size = 0;
+
+  private static final int SPACE_ABOVE = 3;
+  private static final int SPACE_BELOW = 3;
+
+  /**
+   * View Attributes.
+   */
+  private AttributeSet attr;
+}
+
index c6dd95e38d8129a415833431dc00ef685e29005f..9745fe537891a3449d31c4ea7c05030f0ef648e3 100644 (file)
@@ -51,8 +51,8 @@ public class EditorFragmentComponent extends JPanel {
     int y1 = p1.y;
     int y2 = p2.y;
 
-    int savedScrollOfset = editor.getScrollingModel().getHorizontalScrollOffset();
-    if (savedScrollOfset > 0) {
+    int savedScrollOffset = editor.getScrollingModel().getHorizontalScrollOffset();
+    if (savedScrollOffset > 0) {
       editor.stopOptimizedScrolling();
       editor.getScrollingModel().scrollHorizontally(0);
     }
@@ -90,9 +90,9 @@ public class EditorFragmentComponent extends JPanel {
       foldingModel.setFoldingEnabled(isFoldingEnabled);
     }
 
-    if (savedScrollOfset > 0) {
+    if (savedScrollOffset > 0) {
       editor.stopOptimizedScrolling();
-      editor.getScrollingModel().scrollHorizontally(savedScrollOfset);
+      editor.getScrollingModel().scrollHorizontally(savedScrollOffset);
     }
 
     JComponent component = new JComponent() {
@@ -139,7 +139,8 @@ public class EditorFragmentComponent extends JPanel {
     int startLine = editor.offsetToLogicalPosition(range.getStartOffset()).line;
     int endLine = Math.min(editor.offsetToLogicalPosition(range.getEndOffset()).line + 1, editor.getDocument().getLineCount() - 1);
 
-    if (editor.logicalPositionToXY(new LogicalPosition(startLine, 0)).y >= editor.logicalPositionToXY(new LogicalPosition(endLine, 0)).y) return null;
+    //if (editor.logicalPositionToXY(new LogicalPosition(startLine, 0)).y >= editor.logicalPositionToXY(new LogicalPosition(endLine, 0)).y) return null;
+    if (startLine >= endLine) return null;
 
     EditorFragmentComponent fragmentComponent = createEditorFragmentComponent(editor, startLine, endLine, showFolding, true);
 
index 0f5f233cba571f5f1f6bb49397952d6e20bef0cd..a7826c77d08747f588e7888beba74bd3251c0abc 100644 (file)
@@ -35,6 +35,9 @@ import org.jetbrains.annotations.NonNls;
 import javax.swing.*;
 import javax.swing.event.HyperlinkEvent;
 import javax.swing.event.HyperlinkListener;
+import javax.swing.text.*;
+import javax.swing.text.html.HTML;
+import javax.swing.text.html.HTMLEditorKit;
 import java.awt.*;
 import java.awt.event.MouseAdapter;
 import java.awt.event.MouseEvent;
@@ -285,20 +288,46 @@ public class LineTooltipRenderer implements TooltipRenderer {
   protected void stripDescription() {
   }
 
-  static JEditorPane initPane(@NonNls String text, HintHint hintHint, JLayeredPane layeredPane) {
+  static JEditorPane initPane(@NonNls String text, final HintHint hintHint, JLayeredPane layeredPane) {
     final Ref<Dimension> prefSize = new Ref<Dimension>(null);
     text = "<html><head>" +
-           UIUtil.getCssFontDeclaration(hintHint.getTextFont(), hintHint.getTextForeground()) +
+           UIUtil.getCssFontDeclaration(hintHint.getTextFont(), hintHint.getTextForeground(), hintHint.getLinkForeground()) +
            "</head><body>" +
            getHtmlBody(text) +
            "</body></html>";
-    final JEditorPane pane = new JEditorPane(UIUtil.HTML_MIME, text) {
+
+    final JEditorPane pane = new JEditorPane() {
       @Override
       public Dimension getPreferredSize() {
         return prefSize.get() != null ? prefSize.get() : super.getPreferredSize();
       }
     };
 
+    final HTMLEditorKit.HTMLFactory factory = new HTMLEditorKit.HTMLFactory() {
+      @Override
+      public View create(Element elem) {
+        AttributeSet attrs = elem.getAttributes();
+        Object elementName = attrs.getAttribute(AbstractDocument.ElementNameAttribute);
+        Object o = (elementName != null) ? null : attrs.getAttribute(StyleConstants.NameAttribute);
+        if (o instanceof HTML.Tag) {
+          HTML.Tag kind = (HTML.Tag)o;
+          if (kind == HTML.Tag.HR) {
+            return new CustomHrView(elem, hintHint.getTextForeground());
+          }
+        }
+        return super.create(elem);
+      }
+    };
+
+    HTMLEditorKit kit = new HTMLEditorKit() {
+      @Override
+      public ViewFactory getViewFactory() {
+        return factory;
+      }
+    };
+    pane.setEditorKit(kit);
+    pane.setText(text);
+
     pane.setCaretPosition(0);
     pane.setEditable(false);
 
@@ -330,6 +359,7 @@ public class LineTooltipRenderer implements TooltipRenderer {
     return pane;
   }
 
+
   public static void setColors(JComponent pane) {
     pane.setForeground(Color.black);
     pane.setBackground(HintUtil.INFORMATION_COLOR);
@@ -355,19 +385,26 @@ public class LineTooltipRenderer implements TooltipRenderer {
   }
 
   protected static String getHtmlBody(@NonNls String text) {
+    String result = text;
     if (!text.startsWith("<html>")) {
-      return text.replaceAll("\n", "<br>");
+      result = text.replaceAll("\n", "<br>");
     }
-    final int bodyIdx = text.indexOf("<body>");
-    final int closedBodyIdx = text.indexOf("</body>");
-    if (bodyIdx != -1 && closedBodyIdx != -1) {
-      return text.substring(bodyIdx + "<body>".length(), closedBodyIdx);
+    else {
+      final int bodyIdx = text.indexOf("<body>");
+      final int closedBodyIdx = text.indexOf("</body>");
+      if (bodyIdx != -1 && closedBodyIdx != -1) {
+        result = text.substring(bodyIdx + "<body>".length(), closedBodyIdx);
+      }
+      else {
+        text = StringUtil.trimStart(text, "<html>").trim();
+        text = StringUtil.trimEnd(text, "</html>").trim();
+        text = StringUtil.trimStart(text, "<body>").trim();
+        text = StringUtil.trimEnd(text, "</body>").trim();
+        result = text;
+      }
     }
-    text = StringUtil.trimStart(text, "<html>").trim();
-    text = StringUtil.trimEnd(text, "</html>").trim();
-    text = StringUtil.trimStart(text, "<body>").trim();
-    text = StringUtil.trimEnd(text, "</body>").trim();
-    return text;
+
+    return result;
   }
 
   public boolean equals(Object o) {
index 1165235e1371fc6f74e28b024c9262040f0f31b3..26fbc2396c05c57ed133b684e2b16ad0bf87f407 100644 (file)
@@ -246,6 +246,10 @@ public class IdeTooltipManager implements ApplicationComponent, AWTEventListener
     return useGraphite(awtTooltip) ? Color.white : UIManager.getColor("ToolTip.foreground");
   }
 
+  public Color getLinkForeground(boolean awtTooltip) {
+    return useGraphite(awtTooltip) ? new Color(209, 209, 255) : Color.blue;
+  }
+
   public Color getTextBackground(boolean awtTooltip) {
     return useGraphite(awtTooltip) ? new Color(100, 100, 100, 230) : UIManager.getColor("ToolTip.background");
   }
@@ -332,4 +336,5 @@ public class IdeTooltipManager implements ApplicationComponent, AWTEventListener
       hideCurrent(null);
     }
   }
+
 }
index 248e6f87adcfeb034c8d0d9f9ffe6b45b6dee94d..0d66b46bb2afdf82723568027e7975b3d87ff31f 100644 (file)
@@ -105,7 +105,7 @@ public class ActionToolbarImpl extends JPanel implements ActionToolbar {
   private ActionToolbarImpl.MyTimerListener myTimerListener;
 
   public ActionToolbarImpl(final String place,
-                           final ActionGroup actionGroup,
+                           @NotNull final ActionGroup actionGroup,
                            final boolean horizontal,
                            DataManager dataManager,
                            ActionManagerEx actionManager,
@@ -114,7 +114,7 @@ public class ActionToolbarImpl extends JPanel implements ActionToolbar {
   }
 
   public ActionToolbarImpl(final String place,
-                           final ActionGroup actionGroup,
+                           @NotNull final ActionGroup actionGroup,
                            final boolean horizontal,
                            DataManager dataManager,
                            ActionManagerEx actionManager,
index e1cd5b0fcd5a7e9e93649c98233679db967be4fd..6809794a04116b1f2347f54c87ad09a9132e882c 100644 (file)
@@ -336,9 +336,10 @@ public class EditorActionUtil {
     }
 
     int lineFeedsToSkip = visualLineNumber - visLineStart.line;
-    List<? extends TextChange> softWraps = editor.getSoftWrapModel().getSoftWrapsForLine(logLine);
-    for (TextChange softWrap : softWraps) {
-      int softWrapLineFeedsNumber = StringUtil.countNewLines(softWrap.getText());
+    List<? extends SoftWrap> softWraps = editor.getSoftWrapModel().getSoftWrapsForLine(logLine);
+    for (SoftWrap softWrap : softWraps) {
+      CharSequence softWrapText = softWrap.getText();
+      int softWrapLineFeedsNumber = StringUtil.countNewLines(softWrapText);
 
       if (softWrapLineFeedsNumber < lineFeedsToSkip) {
         lineFeedsToSkip -= softWrapLineFeedsNumber;
@@ -347,7 +348,6 @@ public class EditorActionUtil {
 
       // Point to the first non-white space symbol at the target soft wrap visual line or to the first non-white space symbol
       // of document line that follows it if possible.
-      CharSequence softWrapText = softWrap.getText();
       int softWrapTextLength = softWrapText.length();
       boolean skip = true;
       for (int j = 0; j < softWrapTextLength; j++) {
@@ -443,7 +443,7 @@ public class EditorActionUtil {
       int offset = editor.logicalPositionToOffset(logical);
       if (offset < editor.getDocument().getTextLength()) {
 
-        TextChange softWrap = softWrapModel.getSoftWrap(offset);
+        SoftWrap softWrap = softWrapModel.getSoftWrap(offset);
         if (softWrap == null) {
           // Same offset may correspond to positions on different visual lines in case of soft wraps presence
           // (all soft-wrap introduced virtual text is mapped to the same offset as the first document symbol after soft wrap).
index cf9487d6da41c2f981dfa690d68bd523cb3906e7..e80eb525e3725dd9e8502bc9a82f9889dad241e4 100644 (file)
@@ -30,13 +30,4 @@ public class ToggleUseSoftWrapsToolbarAction extends AbstractToggleUseSoftWrapsA
     super();
     copyFrom(ActionManager.getInstance().getAction(IdeActions.ACTION_EDITOR_USE_SOFT_WRAPS));
   }
-
-  //TODO den remove
-  @Override
-  public void setSelected(AnActionEvent e, boolean state) {
-    Editor editor = getEditor(e);
-    if (editor != null) {
-      editor.getSettings().setUseSoftWraps(state);
-    }
-  }
 }
diff --git a/platform/platform-impl/src/com/intellij/openapi/editor/ex/FoldingListener.java b/platform/platform-impl/src/com/intellij/openapi/editor/ex/FoldingListener.java
new file mode 100644 (file)
index 0000000..1a4ee38
--- /dev/null
@@ -0,0 +1,43 @@
+/*
+ * 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.FoldRegion;
+import org.jetbrains.annotations.NotNull;
+
+/**
+ * Defines common contract for clients interested in folding processing.
+ *
+ * @author Denis Zhdanov
+ * @since Sep 8, 2010 11:20:28 AM
+ */
+public interface FoldingListener {
+
+  /**
+   * Informs that <code>'collapsed'</code> state of given fold region is just changed.
+   * <p/>
+   * <b>Note:</b> listener should delay fold region state processing until {@link #onFoldProcessingEnd()} is called.
+   * I.e. folding model may return inconsistent data between current moment and {@link #onFoldProcessingEnd()}.
+   *
+   * @param region    fold region that is just collapsed or expanded
+   */
+  void onFoldRegionStateChange(@NotNull FoldRegion region);
+
+  /**
+   * Informs that fold processing is done.
+   */
+  void onFoldProcessingEnd();
+}
index 50fda1bc781095cc85e87f99dfb7f8451a23875a..82a35ca5b52de8c9cace838d5b3b59e3bcefbb6f 100644 (file)
@@ -45,4 +45,8 @@ public interface FoldingModelEx extends FoldingModel {
 
   @Nullable
   FoldRegion createFoldRegion(int startOffset, int endOffset, @NotNull String placeholder, FoldingGroup group);
+
+  boolean addListener(@NotNull FoldingListener listener);
+
+  boolean removeListener(@NotNull FoldingListener listener);
 }
index 32c638e6141017febfb7229fe53decab915efc59..78072b5ebc7ec5d22845aa3008fbe0b8ead27194 100644 (file)
  */
 package com.intellij.openapi.editor.ex;
 
-import com.intellij.openapi.editor.TextChange;
+import com.intellij.openapi.editor.SoftWrap;
 import org.jetbrains.annotations.NotNull;
 
-import java.util.Collection;
-
 /**
  * Defines a contract for the callbacks for soft wraps management notifications (addition/removal).
  *
@@ -33,7 +31,7 @@ public interface SoftWrapChangeListener {
    *
    * @param softWrap   newly registered soft wrap
    */
-  void softWrapAdded(@NotNull TextChange softWrap);
+  void softWrapAdded(@NotNull SoftWrap softWrap);
 
   /**
    * This method is assumed to be called every time soft wrap(s) is removed.
index ac7a6aef13594f6a0a1e7f674b3158c58bc6950c..858fb1949934cbe3eb5e4fc1ca0ee04209a204b6 100644 (file)
  */
 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 com.intellij.openapi.editor.*;
 import com.intellij.openapi.editor.impl.softwrap.SoftWrapDrawingType;
 import org.jetbrains.annotations.NotNull;
 
@@ -67,7 +64,7 @@ public interface SoftWrapModelEx extends SoftWrapModel {
   /**
    * @return    unmodifiable collection of soft wraps currently registered within the current model
    */
-  List<? extends TextChange> getRegisteredSoftWraps();
+  List<? extends SoftWrap> getRegisteredSoftWraps();
 
   /**
    * Tries to find index of the target soft wrap at {@link #getRegisteredSoftWraps() soft wraps collection}.
@@ -101,14 +98,6 @@ public interface SoftWrapModelEx extends SoftWrapModel {
    */
   int getMinDrawingWidthInPixels(@NotNull SoftWrapDrawingType drawingType);
 
-  /**
-   * Allows to ask for the minimal width in columns required for painting of the given type.
-   *
-   * @param drawingType   target drawing type
-   * @return              width in columns required for the painting of the given type
-   */
-  int getMinDrawingWidthInColumns(@NotNull SoftWrapDrawingType drawingType);
-
   /**
    * Registers given listener within the current model
    *
@@ -116,19 +105,4 @@ public interface SoftWrapModelEx extends SoftWrapModel {
    * @return            <code>true</code> if given listener was not registered before; <code>false</code> otherwise
    */
   boolean addSoftWrapChangeListener(@NotNull SoftWrapChangeListener listener);
-
-  /**
-   * Asks current model to define approximate soft wraps for the lines range defined by the given lines if necessary.
-   * <p/>
-   * The main idea is to calculate exact soft wraps positions during editor repainting because we have complete
-   * information about font types used for text representation there. However, there is a possible case that we need to
-   * perform intermediate soft wraps calculations. E.g. we may open big document and than may want to scroll to the middle
-   * of it, hence, need to define vertical offset to apply to viewport position. However, vertical offset value depends on
-   * soft wraps between current visible area and target logical line and that soft wraps are not applied yet. We may call this
-   * method in order to define approximate soft wraps number and positions then in order to make scrolling more precise.
-   *
-   * @param line1     one of the target lines boundaries (not imposed to be greater or less than the other boundary)
-   * @param line2     another boundary line (not imposed to be greater or less than the other boundary)
-   */
-  void defineApproximateSoftWraps(int line1, int line2);
 }
index d01e22b4d4611bb6591b48d38e904ee7ed8e2372..21d742c098154b6c4b49a2eea68008e2ac890a18 100644 (file)
@@ -62,9 +62,9 @@ public class EditorUtil {
     }
 
     int visualLinesToSkip = line - resVisStart.line;
-    List<? extends TextChange> softWraps = editor.getSoftWrapModel().getSoftWrapsForLine(resultLogLine);
+    List<? extends SoftWrap> softWraps = editor.getSoftWrapModel().getSoftWrapsForLine(resultLogLine);
     for (int i = 0; i < softWraps.size(); i++) {
-      TextChange softWrap = softWraps.get(i);
+      SoftWrap softWrap = softWraps.get(i);
       CharSequence text = document.getCharsSequence();
       if (visualLinesToSkip <= 0) {
         VisualPosition visual = editor.offsetToVisualPosition(softWrap.getStart() - 1);
@@ -86,7 +86,7 @@ public class EditorUtil {
           return resVisEnd.column;
         }
         // We need to find visual column for line feed of the next soft wrap.
-        TextChange nextSoftWrap = softWraps.get(i + 1);
+        SoftWrap nextSoftWrap = softWraps.get(i + 1);
         VisualPosition visual = editor.offsetToVisualPosition(nextSoftWrap.getStart() - 1);
         int result = visual.column;
         int x = editor.visualPositionToXY(visual).x;
@@ -181,22 +181,24 @@ public class EditorUtil {
   public static int calcOffset(Editor editor, CharSequence text, int start, int end, int columnNumber, int tabSize) {
     final int maxScanIndex = Math.min(start + columnNumber + 1, end);
     SoftWrapModel softWrapModel = editor.getSoftWrapModel();
-    List<? extends TextChange> softWraps = softWrapModel.getSoftWrapsForRange(start, maxScanIndex);
+    List<? extends SoftWrap> softWraps = softWrapModel.getSoftWrapsForRange(start, maxScanIndex);
     int startToUse = start;
     int x = 0;
     AtomicInteger currentColumn = new AtomicInteger();
-    for (TextChange softWrap : softWraps) {
+    for (SoftWrap softWrap : softWraps) {
       // There is a possible case that target column points inside soft wrap-introduced virtual space.
       if (currentColumn.get() >= columnNumber) {
         return startToUse;
       }
-      int result = calcSoftWrapUnawareOffset(editor, text, startToUse, softWrap.getEnd(), columnNumber, tabSize, x, currentColumn);
+      int result = calcSoftWrapUnawareOffset(
+        editor, text, startToUse, softWrap.getEnd(), columnNumber, tabSize, x, currentColumn
+      );
       if (result >= 0) {
         return result;
       }
 
       startToUse = softWrap.getStart();
-      x = softWrapModel.getSoftWrapIndentWidthInPixels(softWrap);
+      x = softWrap.getIndentInPixels();
     }
 
     // There is a possible case that target column points inside soft wrap-introduced virtual space.
@@ -339,7 +341,7 @@ public class EditorUtil {
   public static int calcColumnNumber(Editor editor, CharSequence text, int start, int offset, int tabSize) {
     boolean useOptimization = true;
     if (editor != null) {
-      TextChange softWrap = editor.getSoftWrapModel().getSoftWrap(start);
+      SoftWrap softWrap = editor.getSoftWrapModel().getSoftWrap(start);
       useOptimization = softWrap == null;
     }
     boolean hasNonTabs = false;
@@ -426,7 +428,7 @@ public class EditorUtil {
     for (int i = end - 1; i >= start; i--) {
       switch (text.charAt(i)) {
         case '\n': startToUse = i + 1; break loop;
-        case '\t': lastTabSymbolIndex = i;
+        case '\t': if (lastTabSymbolIndex < 0) lastTabSymbolIndex = i;
       }
     }
 
@@ -442,6 +444,10 @@ public class EditorUtil {
 
     // Calculate number of columns up to the latest tabulation symbol.
     for (int i = startToUse; i <= lastTabSymbolIndex; i++) {
+      SoftWrap softWrap = editor.getSoftWrapModel().getSoftWrap(i);
+      if (softWrap != null) {
+        x = softWrap.getIndentInPixels();
+      }
       char c = text.charAt(i);
       prevX = x;
       switch (c) {
index 2efae3125f19aa5a57ddf3a125022e52ac84e698..d75684585e828f037f1d8c58fbb114a6c9deec88 100644 (file)
@@ -28,6 +28,7 @@ import com.intellij.openapi.editor.ex.PrioritizedDocumentListener;
 import com.intellij.openapi.editor.highlighter.EditorHighlighter;
 import com.intellij.openapi.editor.highlighter.HighlighterClient;
 import com.intellij.openapi.editor.highlighter.HighlighterIterator;
+import com.intellij.openapi.editor.impl.EditorDocumentPriorities;
 import com.intellij.openapi.editor.markup.TextAttributes;
 import com.intellij.openapi.fileTypes.PlainSyntaxHighlighter;
 import com.intellij.openapi.fileTypes.SyntaxHighlighter;
@@ -264,7 +265,7 @@ public class LexerEditorHighlighter implements EditorHighlighter, PrioritizedDoc
   }
 
   public int getPriority() {
-    return 2;
+    return EditorDocumentPriorities.LEXER_EDITOR;
   }
 
   private static boolean segmentsEqual(SegmentArrayWithData a1, int idx1, SegmentArrayWithData a2, int idx2, final int offsetShift) {
index 207e13b12773204e4c0b689a643937f8a5dbd200..c3e592debb4cce9c4695875c913a8eff83693a28 100644 (file)
@@ -207,7 +207,14 @@ public class CaretModelImpl implements CaretModel, PrioritizedDocumentListener,
     }
 
     if (newColumnNumber < 0) newColumnNumber = 0;
-    if (newLineNumber < 0) newLineNumber = 0;
+
+    // There is a possible case that caret is located at the first line and use presses 'Shift+Up'. We want to select all text
+    // from the document start to the current caret position then. So, we have a dedicated flag for tracking that.
+    boolean selectToDocumentStart = false;
+    if (newLineNumber < 0) {
+      selectToDocumentStart = true;
+      newLineNumber = 0;
+    }
 
     VisualPosition pos = new VisualPosition(newLineNumber, newColumnNumber);
     int lastColumnNumber = newColumnNumber;
@@ -221,7 +228,7 @@ public class CaretModelImpl implements CaretModel, PrioritizedDocumentListener,
             newColumnNumber = myEditor.offsetToVisualPosition(offset).column;
           }
           else {
-            TextChange softWrap = myEditor.getSoftWrapModel().getSoftWrap(offset + 1);
+            SoftWrap softWrap = myEditor.getSoftWrapModel().getSoftWrap(offset + 1);
             // There is a possible case that tabulation symbol is the last document symbol represented on a visual line before
             // soft wrap. We can't just use column from 'offset + 1' because it would point on a next visual line.
             if (softWrap == null) {
@@ -259,7 +266,14 @@ public class CaretModelImpl implements CaretModel, PrioritizedDocumentListener,
         selectionModel.setBlockSelection(blockSelectionStart, getLogicalPosition());
       }
       else {
-        selectionModel.setSelection(selectionStart, getOffset());
+        int endOffsetToUse = getOffset();
+        if (selectToDocumentStart) {
+          endOffsetToUse = 0;
+        }
+        else if (pos.line >= myEditor.getVisibleLineCount()) {
+          endOffsetToUse = myEditor.getDocument().getTextLength();
+        }
+        selectionModel.setSelection(selectionStart, endOffsetToUse);
       }
     }
     else {
@@ -512,7 +526,7 @@ public class CaretModelImpl implements CaretModel, PrioritizedDocumentListener,
   }
 
   public int getPriority() {
-    return 3;
+    return EditorDocumentPriorities.CARET_MODEL;
   }
 
   private void setCurrentLogicalCaret(LogicalPosition position) {
@@ -533,8 +547,8 @@ public class CaretModelImpl implements CaretModel, PrioritizedDocumentListener,
     int y = myEditor.visualPositionToXY(visualPosition).y;
     int lineHeight = myEditor.getLineHeight();
     int height = lineHeight;
-    List<? extends TextChange> softWraps = myEditor.getSoftWrapModel().getSoftWrapsForRange(startOffset, endOffset);
-    for (TextChange softWrap : softWraps) {
+    List<? extends SoftWrap> softWraps = myEditor.getSoftWrapModel().getSoftWrapsForRange(startOffset, endOffset);
+    for (SoftWrap softWrap : softWraps) {
       height += StringUtil.countNewLines(softWrap.getText()) * lineHeight;
     }
 
diff --git a/platform/platform-impl/src/com/intellij/openapi/editor/impl/EditorDocumentPriorities.java b/platform/platform-impl/src/com/intellij/openapi/editor/impl/EditorDocumentPriorities.java
new file mode 100644 (file)
index 0000000..438075d
--- /dev/null
@@ -0,0 +1,44 @@
+/*
+ * 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.ex.PrioritizedDocumentListener;
+
+/**
+ * Holds values to use for common {@link PrioritizedDocumentListener prioritized document listeners} used within standard IntelliJ
+ * editor.
+ *
+ * @author Denis Zhdanov
+ * @since Sep 13, 2010 2:30:48 PM
+ */
+public class EditorDocumentPriorities {
+
+  public static final int SOFT_WRAP_MODEL = 40;
+
+  /**
+   * Assuming that range marker listeners work only with document offsets and don't perform document dimension mappings like
+   * {@code 'logical position -> visual position'}, {@code 'offset -> logical position'} etc.
+   */
+  public static final int RANGE_MARKER = 60;
+  public static final int FOLD_MODEL = 80;
+  public static final int LEXER_EDITOR = 100;
+  public static final int CARET_MODEL = 120;
+  public static final int SELECTION_MODEL = 140;
+  public static final int EDITOR_DOCUMENT_ADAPTER = 160;
+
+  private EditorDocumentPriorities() {
+  }
+}
index 35ce4231ec441c3674a7bee2eb42dbd11e1a5bae..2ca81c0f7bdb11dddf12f14d319c9053c723d987 100644 (file)
@@ -123,7 +123,7 @@ class EditorGutterComponentImpl extends EditorGutterComponentEx implements Mouse
     revalidateMarkup();
     repaint();
   }
-  
+
   public void paint(Graphics g) {
     ((ApplicationImpl)ApplicationManager.getApplication()).editorPaintStart();
 
@@ -652,8 +652,7 @@ class EditorGutterComponentImpl extends EditorGutterComponentEx implements Mouse
 
   private VisualPosition offsetToLineStartPosition(int offset) {
     offset = Math.min(myEditor.getDocument().getTextLength() - 1, offset);
-    int line = myEditor.getDocument().getLineNumber(offset);
-    return myEditor.logicalToVisualPosition(new LogicalPosition(line, 0));
+    return new VisualPosition(myEditor.offsetToVisualPosition(offset).line, 0);
   }
 
   private void paintFoldingTree(Graphics2D g) {
index 72c000b054255f0caccf9a4b19135af4c03682c9..5187ebca2b8f53859ac40a4b57b21f4c4441bead 100644 (file)
@@ -220,6 +220,13 @@ public final class EditorImpl extends UserDataHolderBase implements EditorEx, Hi
   private boolean myEmbeddedIntoDialogWrapper;
   private CachedFontContent myLastCache;
   private boolean mySpacesHaveSameWidth;
+
+  /**
+   * There is a possible case that specific font is used for particular text drawing operation (e.g. for 'before' and 'after'
+   * soft wraps drawings). Hence, even if {@link #mySpacesHaveSameWidth} is <code>true</code>, space size for that specific
+   * font may be different. So, we define additional flag that should indicate that {@link #myLastCache} should be reset.
+   */
+  private boolean myForceRefreshFont;
   private boolean mySoftWrapsChanged;
 
   private Point myLastBackgroundPosition = null;
@@ -299,6 +306,8 @@ public final class EditorImpl extends UserDataHolderBase implements EditorEx, Hi
     myDocument.addDocumentListener(myEditorDocumentAdapter);
     myDocument.addDocumentListener(mySoftWrapModel);
 
+    myFoldingModel.addListener(mySoftWrapModel);
+
     myIndentsModel = new IndentsModelImpl(this);
     myCaretModel.addCaretListener(new CaretListener() {
       private LightweightHint myCurrentHint = null;
@@ -336,7 +345,7 @@ public final class EditorImpl extends UserDataHolderBase implements EditorEx, Hi
 
     mySoftWrapModel.addSoftWrapChangeListener(new SoftWrapChangeListener() {
       @Override
-      public void softWrapAdded(@NotNull TextChange softWrap) {
+      public void softWrapAdded(@NotNull SoftWrap softWrap) {
         mySoftWrapsChanged = true;
         int softWrapLine = myDocument.getLineNumber(softWrap.getStart());
         mySizeContainer.update(softWrapLine, softWrapLine, softWrapLine);
@@ -528,6 +537,10 @@ public final class EditorImpl extends UserDataHolderBase implements EditorEx, Hi
     myDocument.removeDocumentListener(mySelectionModel);
     myDocument.removeDocumentListener(mySoftWrapModel);
 
+    myFoldingModel.removeListener(mySoftWrapModel);
+
+    mySoftWrapModel.release();
+
     MarkupModelEx markupModel = (MarkupModelEx)myDocument.getMarkupModel(myProject, false);
     if (markupModel instanceof MarkupModelImpl) {
       markupModel.removeMarkupModelListener(myMarkupModelListener);
@@ -879,7 +892,7 @@ public final class EditorImpl extends UserDataHolderBase implements EditorEx, Hi
         offset = region.getEndOffset();
       }
       else {
-        TextChange softWrap = mySoftWrapModel.getSoftWrap(offset);
+        SoftWrap 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.
@@ -1092,19 +1105,18 @@ public final class EditorImpl extends UserDataHolderBase implements EditorEx, Hi
 
     if (logical.softWrapLinesOnCurrentLogicalLine > 0) {
       int linesToSkip = logical.softWrapLinesOnCurrentLogicalLine;
-      List<? extends TextChange> softWraps = getSoftWrapModel().getSoftWrapsForLine(logLine);
-      for (TextChange softWrap : softWraps) {
+      List<? extends SoftWrap> softWraps = getSoftWrapModel().getSoftWrapsForLine(logLine);
+      for (SoftWrap softWrap : softWraps) {
         if (myFoldingModel.isOffsetCollapsed(softWrap.getStart()) && myFoldingModel.isOffsetCollapsed(softWrap.getStart() - 1)) {
           continue;
         }
-        int lineFeeds = StringUtil.countNewLines(softWrap.getText());
-        linesToSkip -= lineFeeds;
+        linesToSkip--; // Assuming here that every soft wrap has exactly one line feed
         if (linesToSkip > 0) {
           continue;
         }
         lineStartOffset = softWrap.getStart();
-        int widthInColumns = getSoftWrapModel().getSoftWrapIndentWidthInColumns(softWrap);
-        int widthInPixels = getSoftWrapModel().getSoftWrapIndentWidthInPixels(softWrap);
+        int widthInColumns = softWrap.getIndentInColumns();
+        int widthInPixels = softWrap.getIndentInPixels();
         if (widthInColumns <= column) {
           column -= widthInColumns;
           reserved = widthInPixels;
@@ -1163,7 +1175,7 @@ public final class EditorImpl extends UserDataHolderBase implements EditorEx, Hi
         fontType = state.getMergedAttributes().getFontType();
       }
       // We need to consider 'before soft wrap drawing'.
-      TextChange softWrap = getSoftWrapModel().getSoftWrap(offset);
+      SoftWrap softWrap = getSoftWrapModel().getSoftWrap(offset);
       if (softWrap != null && offset > startOffset) {
         column++;
         x += getSoftWrapModel().getMinDrawingWidthInPixels(SoftWrapDrawingType.BEFORE_SOFT_WRAP_LINE_FEED);
@@ -1646,14 +1658,15 @@ public final class EditorImpl extends UserDataHolderBase implements EditorEx, Hi
     }
 
     int start = logicalPositionToOffset(logicalPosition);
+    getSoftWrapModel().registerSoftWrapsIfNecessary(clip, start);
 
     // There is a possible case that we need to draw background from the start of soft wrap-introduced visual line. Given position
     // has valid 'y' coordinate then at it shouldn't be affected by soft wrap that corresponds to the visual line start offset.
     // Hence, we store information about soft wrap to be skipped for further processing and adjust 'x' coordinate value if necessary.
     TIntHashSet softWrapsToSkip = new TIntHashSet();
-    TextChange softWrap = getSoftWrapModel().getSoftWrap(start);
+    SoftWrap softWrap = getSoftWrapModel().getSoftWrap(start);
     if (softWrap != null) {
-      position.x = getSoftWrapModel().getSoftWrapIndentWidthInPixels(softWrap);
+      position.x = softWrap.getIndentInPixels();
       softWrapsToSkip.add(softWrap.getStart());
     }
 
@@ -1679,11 +1692,9 @@ public final class EditorImpl extends UserDataHolderBase implements EditorEx, Hi
     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();
-      getSoftWrapModel().registerSoftWrapIfNecessary(text, start, hEnd, position.x, fontType);
 
       if (hEnd >= lEnd) {
         FoldRegion collapsedFolderAt = myFoldingModel.getCollapsedRegionAtOffset(start);
@@ -1743,7 +1754,6 @@ public final class EditorImpl extends UserDataHolderBase implements EditorEx, Hi
     flushBackground(g, clip);
 
     if (lIterator.getLineNumber() >= lastLineIndex && position.y <= clip.y + clip.height) {
-      getSoftWrapModel().registerSoftWrapIfNecessary(text, start, myDocument.getTextLength(), position.x, fontType);
       paintAfterFileEndBackground(iterationState, g, position, clip, lineHeight, defaultBackground, caretRowPainted);
     }
 
@@ -1813,8 +1823,8 @@ public final class EditorImpl extends UserDataHolderBase implements EditorEx, Hi
                                           AtomicBoolean caretRowPainted)
   {
     int startToUse = start;
-    List<? extends TextChange> softWraps = getSoftWrapModel().getSoftWrapsForRange(start, end);
-    for (TextChange softWrap : softWraps) {
+    List<? extends SoftWrap> softWraps = getSoftWrapModel().getSoftWrapsForRange(start, end);
+    for (SoftWrap softWrap : softWraps) {
       int softWrapStart = softWrap.getStart();
       if (processSoftWrap.contains(softWrapStart)) {
         continue;
@@ -1832,7 +1842,7 @@ public final class EditorImpl extends UserDataHolderBase implements EditorEx, Hi
     return position.x;
   }
 
-  private void drawSoftWrap(Graphics g, TextChange softWrap, Point position, int fontType, Color defaultBackground, Rectangle clip,
+  private void drawSoftWrap(Graphics g, SoftWrap softWrap, Point position, int fontType, Color defaultBackground, Rectangle clip,
                             AtomicBoolean caretRowPainted) {
     // The main idea is to to do the following:
     //     *) update given drawing position coordinates in accordance with the current soft wrap;
@@ -2013,7 +2023,7 @@ public final class EditorImpl extends UserDataHolderBase implements EditorEx, Hi
       else {
         FoldRegion collapsedFolderAt = iterationState.getCurrentFold();
         if (collapsedFolderAt != null) {
-          TextChange softWrap = mySoftWrapModel.getSoftWrap(collapsedFolderAt.getStartOffset());
+          SoftWrap softWrap = mySoftWrapModel.getSoftWrap(collapsedFolderAt.getStartOffset());
           if (softWrap != null && logicalPosition.get() != null) {
             position.x = drawStringWithSoftWraps(
               g, chars, collapsedFolderAt.getStartOffset(), collapsedFolderAt.getStartOffset(), position, clip, effectColor, effectType,
@@ -2220,6 +2230,7 @@ public final class EditorImpl extends UserDataHolderBase implements EditorEx, Hi
                                       Color fontColor,
                                       AtomicReference<LogicalPosition> startDrawingLogicalPosition)
   {
+
     int startToUse = start;
 
     // There is a possible case that starting logical line is split by soft-wraps and it's part after the split should be drawn.
@@ -2228,11 +2239,11 @@ public final class EditorImpl extends UserDataHolderBase implements EditorEx, Hi
     if (startDrawingLogicalPosition.get() != null) {
       softWrapLinesToSkip = startDrawingLogicalPosition.get().softWrapLinesOnCurrentLogicalLine;
     }
-    TextChange lastSkippedSoftWrap = null;
+    SoftWrap lastSkippedSoftWrap = null;
     if (softWrapLinesToSkip > 0) {
-      List<? extends TextChange> softWraps = getSoftWrapModel().getSoftWrapsForLine(startDrawingLogicalPosition.get().line);
-      for (TextChange softWrap : softWraps) {
-        softWrapLinesToSkip -= StringUtil.countNewLines(softWrap.getText());
+      List<? extends SoftWrap> softWraps = getSoftWrapModel().getSoftWrapsForLine(startDrawingLogicalPosition.get().line);
+      for (SoftWrap softWrap : softWraps) {
+        softWrapLinesToSkip--; // Assuming that soft wrap has a single line feed all the time
         if (softWrapLinesToSkip <= 0) {
           lastSkippedSoftWrap = softWrap;
           startToUse = softWrap.getStart();
@@ -2249,7 +2260,7 @@ public final class EditorImpl extends UserDataHolderBase implements EditorEx, Hi
     startDrawingLogicalPosition.set(null);
 
     outer:
-    for (TextChange softWrap : getSoftWrapModel().getSoftWrapsForRange(startToUse, end)) {
+    for (SoftWrap softWrap : getSoftWrapModel().getSoftWrapsForRange(startToUse, end)) {
       char[] softWrapChars = softWrap.getChars();
 
       if (softWrap.equals(lastSkippedSoftWrap)) {
@@ -2257,11 +2268,13 @@ public final class EditorImpl extends UserDataHolderBase implements EditorEx, Hi
         // to draw soft wrap indent if any and 'after soft wrap' sign.
         int i = CharArrayUtil.lastIndexOf(softWrapChars, '\n', 0, softWrapChars.length);
         if (i < softWrapChars.length - 1) {
+          position.x = 0; // Soft wrap starts new visual line
           position.x = drawString(
             g, softWrapChars, i + 1, softWrapChars.length, position, clip, effectColor, effectType, fontType, fontColor
           );
         }
         position.x += mySoftWrapModel.paint(g, SoftWrapDrawingType.AFTER_SOFT_WRAP, position.x, position.y, getLineHeight());
+        myForceRefreshFont = true;
         continue;
       }
 
@@ -2290,6 +2303,7 @@ public final class EditorImpl extends UserDataHolderBase implements EditorEx, Hi
           );
         }
         mySoftWrapModel.paint(g, SoftWrapDrawingType.BEFORE_SOFT_WRAP_LINE_FEED, position.x, position.y, getLineHeight());
+        myForceRefreshFont = true;
 
         // Reset 'x' coordinate because of new line start.
         position.x = 0;
@@ -2310,6 +2324,7 @@ public final class EditorImpl extends UserDataHolderBase implements EditorEx, Hi
         );
       }
       position.x += mySoftWrapModel.paint(g, SoftWrapDrawingType.AFTER_SOFT_WRAP, position.x, position.y, getLineHeight());
+      myForceRefreshFont = true;
     }
     return position.x = drawString(g, text, startToUse, end, position, clip, effectColor, effectType, fontType, fontColor);
   }
@@ -2476,10 +2491,11 @@ public final class EditorImpl extends UserDataHolderBase implements EditorEx, Hi
   }
 
   private void drawCharsCached(Graphics g, char[] data, int start, int end, int x, int y, int fontType, Color color) {
-    if (mySpacesHaveSameWidth && myLastCache != null && spacesOnly(data, start, end)) {
+    if (!myForceRefreshFont && mySpacesHaveSameWidth && myLastCache != null && spacesOnly(data, start, end)) {
       myLastCache.addContent(g, data, start, end, x, y, null);
     }
     else {
+      myForceRefreshFont = false;
       FontInfo fnt = EditorUtil.fontForChar(data[start], fontType, this);
       drawCharsCached(g, data, start, end, x, y, fnt, color);
     }
@@ -4614,6 +4630,7 @@ public final class EditorImpl extends UserDataHolderBase implements EditorEx, Hi
       getGlobal().setLineSpacing(lineSpacing);
     }
 
+    @Nullable
     public Object clone() {
       return null;
     }
@@ -4792,7 +4809,7 @@ public final class EditorImpl extends UserDataHolderBase implements EditorEx, Hi
     }
 
     public int getPriority() {
-      return 5;
+      return EditorDocumentPriorities.EDITOR_DOCUMENT_ADAPTER;
     }
   }
 
@@ -4831,6 +4848,15 @@ public final class EditorImpl extends UserDataHolderBase implements EditorEx, Hi
       myOldEndLine = offsetToLogicalPosition(e.getOffset() + e.getOldLength()).line;
     }
 
+    // Commented as nobody uses this method.
+    //private int getVisualPositionLine(int offset) {
+    //  // Do round up of offset to the nearest line start (valid since we need only line)
+    //  // This is needed for preventing access to lexer editor highlighter regions [that are reset] during bulk mode operation
+    //
+    //  int line = calcLogicalLineNumber(offset);
+    //  return logicalToVisualLine(line);
+    //}
+
     public synchronized void update(int startLine, int newEndLine, int oldEndLine) {
       final int lineWidthSize = myLineWidths.size();
       if (lineWidthSize == 0 || myDocument.getTextLength() <= 0) {
@@ -4886,7 +4912,7 @@ public final class EditorImpl extends UserDataHolderBase implements EditorEx, Hi
         final int fontSize = myScheme.getEditorFontSize();
         final String fontName = myScheme.getEditorFontName();
 
-        List<? extends TextChange> softWraps = getSoftWrapModel().getRegisteredSoftWraps();
+        List<? extends SoftWrap> softWraps = getSoftWrapModel().getRegisteredSoftWraps();
         int softWrapsIndex = -1;
 
         for (int line = 0; line < lineCount; line++) {
@@ -4919,14 +4945,14 @@ public final class EditorImpl extends UserDataHolderBase implements EditorEx, Hi
             }
 
             while (softWrapsIndex < softWraps.size() && line < lineCount) {
-              TextChange softWrap = softWraps.get(softWrapsIndex);
+              SoftWrap softWrap = softWraps.get(softWrapsIndex);
               if (softWrap.getStart() > offset) {
                 break;
               }
               softWrapsIndex++;
               if (softWrap.getStart() == offset) {
                 maxPreviousSoftWrappedWidth = Math.max(maxPreviousSoftWrappedWidth, x);
-                x = getSoftWrapModel().getSoftWrapIndentWidthInPixels(softWrap);
+                x = softWrap.getIndentInPixels();
               }
             }
             if (line + 1 >= lineCount) {
@@ -5004,7 +5030,7 @@ public final class EditorImpl extends UserDataHolderBase implements EditorEx, Hi
     }
 
     private int getContentHeight() {
-      return myLineWidths.size() * getLineHeight();
+      return getVisibleLineCount() * getLineHeight();
     }
   }
 
@@ -5025,7 +5051,7 @@ public final class EditorImpl extends UserDataHolderBase implements EditorEx, Hi
         fontType = state.getMergedAttributes().getFontType();
       }
 
-      TextChange softWrap = getSoftWrapModel().getSoftWrap(i);
+      SoftWrap softWrap = getSoftWrapModel().getSoftWrap(i);
       if (softWrap != null) {
         column++; // For 'after soft wrap' drawing.
         x = getSoftWrapModel().getMinDrawingWidthInPixels(SoftWrapDrawingType.AFTER_SOFT_WRAP);
index e00ecad42f96be64d0d1b21c53f0d26406ca676f..80007783c2d392d900922f978c25cfbf06c37e57 100644 (file)
@@ -33,6 +33,7 @@ import com.intellij.openapi.editor.VisualPosition;
 import com.intellij.openapi.editor.colors.EditorColors;
 import com.intellij.openapi.editor.event.DocumentEvent;
 import com.intellij.openapi.editor.ex.DocumentEx;
+import com.intellij.openapi.editor.ex.FoldingListener;
 import com.intellij.openapi.editor.ex.FoldingModelEx;
 import com.intellij.openapi.editor.ex.PrioritizedDocumentListener;
 import com.intellij.openapi.editor.markup.TextAttributes;
@@ -42,9 +43,14 @@ import org.jetbrains.annotations.NotNull;
 import java.awt.*;
 import java.util.Arrays;
 import java.util.List;
+import java.util.Set;
+import java.util.concurrent.CopyOnWriteArraySet;
 
 public class FoldingModelImpl implements FoldingModelEx, PrioritizedDocumentListener {
   private static final Logger LOG = Logger.getInstance("#com.intellij.openapi.editor.impl.EditorFoldingModelImpl");
+
+  private final Set<FoldingListener> myListeners = new CopyOnWriteArraySet<FoldingListener>();
+
   private boolean myIsFoldingEnabled;
   private final EditorImpl myEditor;
   private final FoldRegionsTree myFoldTree;
@@ -156,6 +162,9 @@ public class FoldingModelImpl implements FoldingModelEx, PrioritizedDocumentList
       final FoldingGroup group = region.getGroup();
       if (group != null) {
         myGroups.putValue(group, region);
+        for (FoldingListener listener : myListeners) {
+          listener.onFoldRegionStateChange(region);
+        }
       }
       return true;
     }
@@ -264,6 +273,7 @@ public class FoldingModelImpl implements FoldingModelEx, PrioritizedDocumentList
 
     myFoldRegionsProcessed = true;
     ((FoldRegionImpl) region).setExpandedInternal(true);
+    notifyListenersOnFoldRegionStateChange(region);
   }
 
   public void collapseFoldRegion(FoldRegion region) {
@@ -295,9 +305,20 @@ public class FoldingModelImpl implements FoldingModelEx, PrioritizedDocumentList
 
     myFoldRegionsProcessed = true;
     ((FoldRegionImpl) region).setExpandedInternal(false);
+    notifyListenersOnFoldRegionStateChange(region);
   }
 
   private void notifyBatchFoldingProcessingDone() {
+    try {
+      doNotifyBatchFoldingProcessingDone();
+    } finally {
+      for (FoldingListener listener : myListeners) {
+        listener.onFoldProcessingEnd();
+      }
+    }
+  }
+
+  private void doNotifyBatchFoldingProcessingDone() {
     myFoldTree.rebuild();
 
     myEditor.updateCaretCursor();
@@ -314,27 +335,33 @@ public class FoldingModelImpl implements FoldingModelEx, PrioritizedDocumentList
 
     int column = -1;
     int line = -1;
+    int offsetToUse = -1;
 
     FoldRegion collapsed = myFoldTree.fetchOutermost(caretOffset);
     if (myCaretPositionSaved) {
       int savedOffset = myEditor.logicalPositionToOffset(new LogicalPosition(mySavedCaretY, mySavedCaretX));
       FoldRegion collapsedAtSaved = myFoldTree.fetchOutermost(savedOffset);
-      column = mySavedCaretX;
-      line = collapsedAtSaved != null ? collapsedAtSaved.getDocument().getLineNumber(collapsedAtSaved.getStartOffset()) : mySavedCaretY;
+      if (collapsedAtSaved == null) {
+        column = mySavedCaretX;
+        line = mySavedCaretY;
+      }
+      else {
+        offsetToUse = collapsedAtSaved.getStartOffset();
+      }
     }
 
     if (collapsed != null && column == -1) {
       line = collapsed.getDocument().getLineNumber(collapsed.getStartOffset());
-      column = myEditor.getCaretModel().getVisualPosition().column;
+      column = myEditor.offsetToLogicalPosition(collapsed.getStartOffset()).column;
     }
 
     boolean oldCaretPositionSaved = myCaretPositionSaved;
 
-    if (column != -1) {
-      LogicalPosition log = new LogicalPosition(line, 0);
-      VisualPosition vis = myEditor.logicalToVisualPosition(log);
-      VisualPosition pos = new VisualPosition(vis.line, column);
-      myEditor.getCaretModel().moveToVisualPosition(pos);
+    if (offsetToUse >= 0) {
+      myEditor.getCaretModel().moveToOffset(offsetToUse);
+    }
+    else if (column != -1) {
+      myEditor.getCaretModel().moveToLogicalPosition(new LogicalPosition(line, column));
     }
     else {
       myEditor.getCaretModel().moveToLogicalPosition(caretPosition);
@@ -410,7 +437,7 @@ public class FoldingModelImpl implements FoldingModelEx, PrioritizedDocumentList
   }
 
   public int getPriority() {
-    return 1;
+    return EditorDocumentPriorities.FOLD_MODEL;
   }
 
   public FoldRegion createFoldRegion(int startOffset, int endOffset, @NotNull String placeholder, FoldingGroup group) {
@@ -419,4 +446,20 @@ public class FoldingModelImpl implements FoldingModelEx, PrioritizedDocumentList
     LOG.assertTrue(region.isValid());
     return region;
   }
+
+  @Override
+  public boolean addListener(@NotNull FoldingListener listener) {
+    return myListeners.add(listener);
+  }
+
+  @Override
+  public boolean removeListener(@NotNull FoldingListener listener) {
+    return myListeners.remove(listener);
+  }
+
+  private void notifyListenersOnFoldRegionStateChange(@NotNull FoldRegion foldRegion) {
+    for (FoldingListener listener : myListeners) {
+      listener.onFoldRegionStateChange(foldRegion);
+    }
+  }
 }
index 8a64ac045816fb5bb8802d549586d98207f12ebd..06b9cb0958170f8b65ea60f14faff3429ed9dc1b 100644 (file)
@@ -55,7 +55,7 @@ public class RangeMarkerTree<T extends RangeMarkerEx> extends IntervalTreeImpl<T
 
     document.addDocumentListener(new PrioritizedDocumentListener() {
       public int getPriority() {
-        return 0; // Need to make sure we invalidate all the stuff before someone (like LineStatusTracker) starts to modify highlights.
+        return EditorDocumentPriorities.RANGE_MARKER; // Need to make sure we invalidate all the stuff before someone (like LineStatusTracker) starts to modify highlights.
       }
 
       public void beforeDocumentChange(DocumentEvent event) {}
index 8bb8addc586eb765c3c0c185e5df66c7b9626888..a15b3bfb1c73ac8c88d8c54dd9dc589807e82a13 100644 (file)
@@ -142,12 +142,7 @@ public class ScrollingModelImpl implements ScrollingModel {
   }
 
   private Point calcOffsetsToScroll(LogicalPosition pos, ScrollType scrollType, Rectangle viewRect) {
-    // There is a possible case that the user opens huge document with many number of soft-wrapped line.
-    // Suppose that he or she wants to move viewport to such a logical position that many document lines between current
-    // viewport position and the target one are not displayed before. That means that we can't be sure about vertical offset
-    // to be applied to the viewport. Hence, we ask soft wrap model to roughly define soft wraps on a trail.
     LogicalPosition firstVisibleLineStart = myEditor.xyToLogicalPosition(viewRect.getLocation());
-    myEditor.getSoftWrapModel().defineApproximateSoftWraps(firstVisibleLineStart.line, pos.line);
 
     Point targetLocation = myEditor.logicalPositionToXY(pos);
 
index 49d9dac78b890afcc79ac8b670001bcf69b4b971..c291207e1c02ef28f078db00080e95a33f74af3a 100644 (file)
@@ -120,7 +120,7 @@ public class SelectionModelImpl implements SelectionModel, PrioritizedDocumentLi
   }
 
   public int getPriority() {
-    return 4;
+    return EditorDocumentPriorities.SELECTION_MODEL;
   }
 
   public SelectionModelImpl(EditorImpl editor) {
index 1c9f424c826e201cc1b3d8abbce91c2f69b517db..7a00f7d48abe08b372c79262b65c8900163985dc 100644 (file)
@@ -18,14 +18,11 @@ 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.ex.EditorEx;
-import com.intellij.openapi.editor.ex.PrioritizedDocumentListener;
-import com.intellij.openapi.editor.ex.SoftWrapChangeListener;
-import com.intellij.openapi.editor.ex.SoftWrapModelEx;
+import com.intellij.openapi.editor.ex.*;
 import com.intellij.openapi.editor.ex.util.EditorUtil;
 import com.intellij.openapi.editor.impl.softwrap.*;
-import com.intellij.openapi.util.text.StringUtil;
-import com.intellij.util.text.CharArrayUtil;
+import com.intellij.openapi.editor.impl.softwrap.mapping.CachingSoftWrapDataMapper;
+import com.intellij.openapi.editor.impl.softwrap.mapping.SoftWrapApplianceManager;
 import org.jetbrains.annotations.NotNull;
 import org.jetbrains.annotations.Nullable;
 
@@ -45,19 +42,25 @@ import java.util.List;
  * @author Denis Zhdanov
  * @since Jun 8, 2010 12:47:32 PM
  */
-public class SoftWrapModelImpl implements SoftWrapModelEx, DocumentListener {
+public class SoftWrapModelImpl implements SoftWrapModelEx, PrioritizedDocumentListener, FoldingListener {
+
+  /** Upper boundary of time interval to check editor settings. */
+  private static final long EDITOR_SETTINGS_CHECK_PERIOD_MILLIS = 10000;
 
   private final List<DocumentListener> myDocumentListeners = new ArrayList<DocumentListener>();
+  private final List<FoldingListener> myFoldListeners = new ArrayList<FoldingListener>();
 
-  private final SoftWrapDataMapper            myDataMapper;
+  private final CachingSoftWrapDataMapper myDataMapper;
   private final SoftWrapsStorage              myStorage;
   private final SoftWrapPainter               myPainter;
   private final SoftWrapApplianceManager      myApplianceManager;
-  private final SoftWrapDocumentChangeManager myDocumentChangeManager;
 
   private final EditorEx myEditor;
   /** Holds number of 'active' calls, i.e. number of methods calls of the current object within the current call stack. */
-  private       int      myActive;
+  private int myActive;
+  /** Holds timestamp of the last editor settings check. */
+  private long myLastSettingsCheckTimeMillis;
+  private boolean myLastUseSoftWraps;
 
   public SoftWrapModelImpl(@NotNull EditorEx editor) {
     this(editor, new SoftWrapsStorage(), new CompositeSoftWrapPainter(editor));
@@ -70,40 +73,41 @@ public class SoftWrapModelImpl implements SoftWrapModelEx, DocumentListener {
   public SoftWrapModelImpl(@NotNull final EditorEx editor, @NotNull SoftWrapsStorage storage, @NotNull SoftWrapPainter painter,
                            EditorTextRepresentationHelper representationHelper) {
     this(
-      editor, storage, painter, new DefaultSoftWrapApplianceManager(storage, editor, painter, representationHelper),
-      new SoftWrapDataMapper(editor, storage, representationHelper), new SoftWrapDocumentChangeManager(editor, storage)
+      editor, storage, painter, new SoftWrapApplianceManager(storage, editor, painter, representationHelper),
+      new CachingSoftWrapDataMapper(editor, storage, representationHelper)
     );
+    myApplianceManager.addListener(myDataMapper);
   }
 
   public SoftWrapModelImpl(@NotNull EditorEx editor, @NotNull SoftWrapsStorage storage, @NotNull SoftWrapPainter painter,
-                           @NotNull DefaultSoftWrapApplianceManager applianceManager, @NotNull SoftWrapDataMapper dataMapper,
-                           @NotNull SoftWrapDocumentChangeManager documentChangeManager)
-  {
-    this(editor, storage, painter, (SoftWrapApplianceManager)applianceManager, dataMapper, documentChangeManager);
-    myDocumentListeners.add(applianceManager.getDocumentListener());
-  }
-
-  public SoftWrapModelImpl(@NotNull EditorEx editor, @NotNull SoftWrapsStorage storage, @NotNull SoftWrapPainter painter,
-                           @NotNull SoftWrapApplianceManager applianceManager, @NotNull SoftWrapDataMapper dataMapper,
-                           @NotNull SoftWrapDocumentChangeManager documentChangeManager)
+                           @NotNull SoftWrapApplianceManager applianceManager, @NotNull CachingSoftWrapDataMapper dataMapper)
   {
     myEditor = editor;
     myStorage = storage;
     myPainter = painter;
     myApplianceManager = applianceManager;
     myDataMapper = dataMapper;
-    myDocumentChangeManager = documentChangeManager;
 
-    myDocumentListeners.add(myDocumentChangeManager);
-    Collections.sort(myDocumentListeners, PrioritizedDocumentListener.COMPARATOR);
+    myDocumentListeners.add(myApplianceManager);
+    myFoldListeners.add(myApplianceManager);
   }
 
   public boolean isSoftWrappingEnabled() {
-    return myEditor.getSettings().isUseSoftWraps() && !myEditor.isOneLineMode();
+    if (myEditor.isOneLineMode()) {
+      return false;
+    }
+
+    // Profiling shows that editor settings lookup have impact at overall performance if called often.
+    // Hence, we cache value used last time.
+    if (System.currentTimeMillis() - myLastSettingsCheckTimeMillis <= EDITOR_SETTINGS_CHECK_PERIOD_MILLIS) {
+      return myLastUseSoftWraps;
+    }
+    myLastSettingsCheckTimeMillis = System.currentTimeMillis();
+    return myLastUseSoftWraps = myEditor.getSettings().isUseSoftWraps();
   }
 
   @Nullable
-  public TextChange getSoftWrap(int offset) {
+  public SoftWrap getSoftWrap(int offset) {
     if (!isSoftWrappingEnabled()) {
       return null;
     }
@@ -117,7 +121,7 @@ public class SoftWrapModelImpl implements SoftWrapModelEx, DocumentListener {
 
   @NotNull
   @Override
-  public List<? extends TextChange> getSoftWrapsForRange(int start, int end) {
+  public List<? extends SoftWrap> getSoftWrapsForRange(int start, int end) {
     if (!isSoftWrappingEnabled()) {
       return Collections.emptyList();
     }
@@ -126,7 +130,7 @@ public class SoftWrapModelImpl implements SoftWrapModelEx, DocumentListener {
       startIndex = -startIndex - 1;
     }
 
-    List<TextChangeImpl> softWraps = myStorage.getSoftWraps();
+    List<? extends SoftWrap> softWraps = myStorage.getSoftWraps();
     if (startIndex >= softWraps.size()) {
       return Collections.emptyList();
     }
@@ -147,7 +151,7 @@ public class SoftWrapModelImpl implements SoftWrapModelEx, DocumentListener {
 
   @Override
   @NotNull
-  public List<? extends TextChange> getSoftWrapsForLine(int documentLine) {
+  public List<? extends SoftWrap> getSoftWrapsForLine(int documentLine) {
     if (!isSoftWrappingEnabled()) {
       return Collections.emptyList();
     }
@@ -168,45 +172,23 @@ public class SoftWrapModelImpl implements SoftWrapModelEx, DocumentListener {
     }
     int result = 0;
     FoldingModel foldingModel = myEditor.getFoldingModel();
-    for (TextChange softWrap : myStorage.getSoftWraps()) {
+    for (SoftWrap softWrap : myStorage.getSoftWraps()) {
       if (!foldingModel.isOffsetCollapsed(softWrap.getStart())) {
-        result += StringUtil.countNewLines(softWrap.getText());
+        result++; // Assuming that soft wrap has single line feed all the time
       }
     }
     return result;
   }
 
-  @Override
-  public void defineApproximateSoftWraps(int line1, int line2) {
-    if (!isSoftWrappingEnabled()) {
-      return;
-    }
-    int startLine = line1;
-    int endLine = line2;
-    if (line1 > line2) {
-      startLine = line2;
-      endLine = line1;
-    }
-
-    // Normalization.
-    Document document = myEditor.getDocument();
-    startLine = Math.max(0, startLine);
-    endLine = Math.max(0, Math.min(endLine, document.getLineCount() - 1));
-
-    myApplianceManager.registerSoftWrapIfNecessary(
-      document.getCharsSequence(), document.getLineStartOffset(startLine), document.getLineEndOffset(endLine), 0, Font.PLAIN, true
-    );
-  }
-
-  public void registerSoftWrapIfNecessary(@NotNull CharSequence text, int start, int end, int x, int fontType) {
+  //TODO den add doc
+  public void registerSoftWrapsIfNecessary(@NotNull Rectangle clip, int startOffset) {
     if (!isSoftWrappingEnabled()) {
       return;
     }
-    myDocumentChangeManager.syncSoftWraps();
 
     myActive++;
     try {
-      myApplianceManager.registerSoftWrapIfNecessary(text, start, end, x, fontType, false);
+      myApplianceManager.registerSoftWrapIfNecessary(clip, startOffset);
     }
     finally {
       myActive--;
@@ -214,7 +196,7 @@ public class SoftWrapModelImpl implements SoftWrapModelEx, DocumentListener {
   }
 
   @Override
-  public List<? extends TextChange> getRegisteredSoftWraps() {
+  public List<? extends SoftWrap> getRegisteredSoftWraps() {
     if (!isSoftWrappingEnabled()) {
       return Collections.emptyList();
     }
@@ -222,7 +204,7 @@ public class SoftWrapModelImpl implements SoftWrapModelEx, DocumentListener {
   }
 
   @Override
-  public boolean isVisible(TextChange softWrap) {
+  public boolean isVisible(SoftWrap softWrap) {
     FoldingModel foldingModel = myEditor.getFoldingModel();
     int start = softWrap.getStart();
     if (!foldingModel.isOffsetCollapsed(start)) {
@@ -240,7 +222,7 @@ public class SoftWrapModelImpl implements SoftWrapModelEx, DocumentListener {
     if (!isSoftWrappingEnabled()) {
       return 0;
     }
-    return myPainter.paint(g, drawingType, x,  y, lineHeight);
+    return myPainter.paint(g, drawingType, x, y, lineHeight);
   }
 
   @Override
@@ -248,15 +230,10 @@ public class SoftWrapModelImpl implements SoftWrapModelEx, DocumentListener {
     return myPainter.getMinDrawingWidth(drawingType);
   }
 
-  @Override
-  public int getMinDrawingWidthInColumns(@NotNull SoftWrapDrawingType drawingType) {
-    return myPainter.getMinDrawingWidth(drawingType) > 0 ? 1 : 0;
-  }
-
   @NotNull
   @Override
   public LogicalPosition visualToLogicalPosition(@NotNull VisualPosition visual) {
-    if (myActive > 0 || !isSoftWrappingEnabled() || myStorage.isEmpty() || myEditor.getDocument().getTextLength() <= 0) {
+    if (!prepareToMapping()) {
       return myEditor.visualToLogicalPosition(visual, false);
     }
     myActive++;
@@ -270,7 +247,7 @@ public class SoftWrapModelImpl implements SoftWrapModelEx, DocumentListener {
   @NotNull
   @Override
   public LogicalPosition offsetToLogicalPosition(int offset) {
-    if (myActive > 0 || !isSoftWrappingEnabled() || myStorage.isEmpty() || myEditor.getDocument().getTextLength() <= 0) {
+    if (!prepareToMapping()) {
       return myEditor.offsetToLogicalPosition(offset, false);
     }
     myActive++;
@@ -283,7 +260,7 @@ public class SoftWrapModelImpl implements SoftWrapModelEx, DocumentListener {
 
   @NotNull
   public LogicalPosition adjustLogicalPosition(LogicalPosition defaultLogical, int offset) {
-    if (myActive > 0 || !isSoftWrappingEnabled()) {
+    if (!prepareToMapping()) {
       return defaultLogical;
     }
 
@@ -297,19 +274,53 @@ public class SoftWrapModelImpl implements SoftWrapModelEx, DocumentListener {
 
   @NotNull
   public VisualPosition adjustVisualPosition(@NotNull LogicalPosition logical, @NotNull VisualPosition defaultVisual) {
-    if (myActive > 0 || !isSoftWrappingEnabled() || myStorage.isEmpty()) {
+    if (!prepareToMapping()) {
       return defaultVisual;
     }
 
     myActive++;
     try {
-      return myDataMapper.adjustVisualPosition(logical, defaultVisual);
+      return myDataMapper.logicalToVisualPosition(logical, defaultVisual);
     }
     finally {
       myActive--;
     }
   }
 
+  /**
+   * Encapsulates preparations for performing document dimension mapping (e.g. visual to logical position) and answers
+   * if soft wraps-aware processing should be used (e.g. there is no need to consider soft wraps if user configured them
+   * not to be used).
+   *
+   * @return      <code>true</code> if soft wraps-aware processing should be used; <code>false</code> otherwise
+   */
+  private boolean prepareToMapping() {
+    boolean useSoftWraps = myActive <= 0 && isSoftWrappingEnabled() && !myStorage.isEmpty() && myEditor.getDocument().getTextLength() > 0;
+    if (!useSoftWraps) {
+      return useSoftWraps;
+    }
+
+    myApplianceManager.dropDataIfNecessary();
+    return true;
+    //
+    //Rectangle visibleArea = myEditor.getScrollingModel().getVisibleArea();
+    //if (visibleArea.width <= 0) {
+    //  // We don't know visible area width, hence, can't calculate soft wraps positions.
+    //  return false;
+    //}
+    //
+    //myActive++;
+    //try {
+    //  LogicalPosition logicalPosition = myEditor.xyToLogicalPosition(visibleArea.getLocation());
+    //  int offset = myEditor.logicalPositionToOffset(logicalPosition);
+    //  myApplianceManager.registerSoftWrapIfNecessary(visibleArea, offset);
+    //  return true;
+    //}
+    //finally {
+    //  myActive--;
+    //}
+  }
+
   /**
    * Allows to answer if given visual position points to soft wrap-introduced virtual space.
    *
@@ -349,7 +360,7 @@ public class SoftWrapModelImpl implements SoftWrapModelEx, DocumentListener {
       return false;
     }
 
-    TextChange softWrap = model.getSoftWrap(offset);
+    SoftWrap softWrap = model.getSoftWrap(offset);
     if (softWrap == null) {
       return false;
     }
@@ -368,7 +379,7 @@ public class SoftWrapModelImpl implements SoftWrapModelEx, DocumentListener {
       int offsetLineStart = myEditor.logicalPositionToOffset(logLineStart);
       softWrap = model.getSoftWrap(offsetLineStart);
       if (softWrap != null) {
-        x = model.getSoftWrapIndentWidthInColumns(softWrap) * EditorUtil.getSpaceWidth(Font.PLAIN, myEditor);
+        x = softWrap.getIndentInPixels();
       }
     }
     int width = EditorUtil.textWidthInColumns(myEditor, myEditor.getDocument().getCharsSequence(), offset - 1, offset, x);
@@ -379,47 +390,6 @@ public class SoftWrapModelImpl implements SoftWrapModelEx, DocumentListener {
     return countBeforeSoftWrap ? visual.column >= softWrapStartColumn : visual.column > softWrapStartColumn;
   }
 
-  @Override
-  public int getSoftWrapIndentWidthInPixels(@NotNull TextChange softWrap) {
-    if (!isSoftWrappingEnabled()) {
-      return 0;
-    }
-    char[] chars = softWrap.getChars();
-    int result = myPainter.getMinDrawingWidth(SoftWrapDrawingType.AFTER_SOFT_WRAP);
-
-    int start = 0;
-    int end = chars.length;
-
-    int i = CharArrayUtil.lastIndexOf(chars, '\n', 0, chars.length);
-    if (i >= 0) {
-      start = i + 1;
-    }
-
-    if (start < end) {
-      result += EditorUtil.textWidth(myEditor, softWrap.getText(), start, end, Font.PLAIN, 0);
-    }
-
-    return result;
-  }
-
-  @Override
-  public int getSoftWrapIndentWidthInColumns(@NotNull TextChange softWrap) {
-    if (!isSoftWrappingEnabled()) {
-      return 0;
-    }
-    char[] chars = softWrap.getChars();
-    int result = 1; // For 'after soft wrap' drawing
-
-    int start = 0;
-    int i = CharArrayUtil.lastIndexOf(chars, '\n', 0, chars.length);
-    if (i >= 0) {
-      start = i + 1;
-    }
-    result += chars.length - start;
-
-    return result;
-  }
-
   @Override
   public void beforeDocumentChangeAtCaret() {
     CaretModel caretModel = myEditor.getCaretModel();
@@ -427,10 +397,11 @@ public class SoftWrapModelImpl implements SoftWrapModelEx, DocumentListener {
     if (!isInsideSoftWrap(visualCaretPosition)) {
       return;
     }
-    if (myDocumentChangeManager.makeHardWrap(caretModel.getOffset())) {
-      // Restore caret position.
-      caretModel.moveToVisualPosition(visualCaretPosition);
-    }
+    //TODO den implement
+    //if (myDocumentChangeManager.makeHardWrap(caretModel.getOffset())) {
+    //  // Restore caret position.
+    //  caretModel.moveToVisualPosition(visualCaretPosition);
+    //}
   }
 
   @Override
@@ -438,6 +409,11 @@ public class SoftWrapModelImpl implements SoftWrapModelEx, DocumentListener {
     return myStorage.addSoftWrapChangeListener(listener);
   }
 
+  @Override
+  public int getPriority() {
+    return EditorDocumentPriorities.SOFT_WRAP_MODEL;
+  }
+
   @Override
   public void beforeDocumentChange(DocumentEvent event) {
     if (!isSoftWrappingEnabled()) {
@@ -457,4 +433,31 @@ public class SoftWrapModelImpl implements SoftWrapModelEx, DocumentListener {
       listener.documentChanged(event);
     }
   }
+
+  @Override
+  public void onFoldRegionStateChange(@NotNull FoldRegion region) {
+    if (!isSoftWrappingEnabled()) {
+      return;
+    }
+    for (FoldingListener listener : myFoldListeners) {
+      listener.onFoldRegionStateChange(region);
+    }
+  }
+
+  @Override
+  public void onFoldProcessingEnd() {
+    if (!isSoftWrappingEnabled()) {
+      return;
+    }
+    for (FoldingListener listener : myFoldListeners) {
+      listener.onFoldProcessingEnd();
+    }
+  }
+
+  @Override
+  public void release() {
+    myDataMapper.release();
+    myApplianceManager.release();
+    myStorage.removeAll();
+  }
 }
diff --git a/platform/platform-impl/src/com/intellij/openapi/editor/impl/softwrap/DefaultSoftWrapApplianceManager.java b/platform/platform-impl/src/com/intellij/openapi/editor/impl/softwrap/DefaultSoftWrapApplianceManager.java
deleted file mode 100644 (file)
index 4087bce..0000000
+++ /dev/null
@@ -1,323 +0,0 @@
-/*
- * 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.softwrap;
-
-import com.intellij.openapi.editor.Document;
-import com.intellij.openapi.editor.EditorSettings;
-import com.intellij.openapi.editor.LanguageLineWrapPositionStrategy;
-import com.intellij.openapi.editor.LineWrapPositionStrategy;
-import com.intellij.openapi.editor.event.DocumentListener;
-import com.intellij.openapi.editor.ex.EditorEx;
-import com.intellij.openapi.editor.ex.util.EditorUtil;
-import com.intellij.openapi.editor.impl.EditorTextRepresentationHelper;
-import com.intellij.openapi.project.Project;
-import com.intellij.openapi.util.text.StringUtil;
-import com.intellij.openapi.vfs.VirtualFile;
-import com.intellij.psi.codeStyle.CodeStyleSettings;
-import com.intellij.psi.codeStyle.CodeStyleSettingsManager;
-import gnu.trove.TIntArrayList;
-import gnu.trove.TIntObjectHashMap;
-import org.jetbrains.annotations.NotNull;
-
-import java.awt.*;
-import java.nio.CharBuffer;
-
-/**
- * Default {@link SoftWrapApplianceManager} implementation that is built with the following design guide lines:
- * <pre>
- * <ul>
- *   <li>
- *      perform soft wrap processing per-logical line, i.e. every time current manager is asked to process
- *      particular text range, it calculates logical lines that contain all target symbols, checks if they should
- *      be soft-wrapped and registers corresponding soft wraps if necessary;
- *   </li>
- *   <li>
- *      objects of this class remember processed logical lines and perform new processing for them only if visible
- *      area width is changed;
- *   </li>
- *   <li>
- *      {@link SoftWrapsStorage#removeAll() drops all registered soft wraps} if visible area width is changed;
- *   </li>
- * </ul>
- * </pre>
- * <p/>
- * Not thread-safe.
- *
- * @author Denis Zhdanov
- * @since Jul 5, 2010 10:01:27 AM
- */
-public class DefaultSoftWrapApplianceManager implements SoftWrapApplianceManager {
-
-  /** Enumerates possible type of soft wrap indents to use. */
-  enum IndentType {
-    /** Don't apply special indent to soft-wrapped line at all. */
-    NONE,
-
-    /**
-     * Indent soft wraps for the {@link EditorSettings#getCustomSoftWrapIndent() user-defined number of columns}
-     * to the start of the previous visual line.
-     */
-    CUSTOM,
-
-    /**
-     * Tries to indents soft-wrapped line start to the location of the first non white-space symbols of the previous visual line.
-     * <p/>
-     * Falls back to {@link #NONE} if indentation to the previous visual line start is considered to be unappropriated.
-     */
-    AUTOMATIC
-  }
-
-  private static final int DEFAULT_INDENT_SIZE = 4;
-
-  /**
-   * Holds information about logical lines for which soft wrap is calculated as a set of
-   * <code>(logical line number; temporary)</code> pairs.
-   */
-  private final TIntObjectHashMap<Boolean> myProcessedLogicalLines = new TIntObjectHashMap<Boolean>();
-
-  private final DocumentListener myDocumentListener = new LineOrientedDocumentChangeAdapter() {
-    @Override
-    public void beforeDocumentChange(int startLine, int endLine, int symbolsDifference) {
-      dropData(startLine, endLine);
-    }
-
-    @Override
-    public void afterDocumentChange(int startLine, int endLine, int symbolsDifference) {
-      dropData(startLine, endLine);
-    }
-
-    @Override
-    public int getPriority() {
-      return SoftWrapConstants.SOFT_WRAP_APPLIANCE_LISTENER_PRIORITY;
-    }
-
-    private void dropData(int startLine, int endLine) {
-      Document document = myEditor.getDocument();
-      for (int i = startLine; i <= endLine; i++) {
-        myProcessedLogicalLines.remove(i);
-
-        // Calculate approximate soft wraps positions using plain font.
-        // Note: we don't update 'myProcessedLogicalLines' collection here, i.e. soft wraps will be recalculated precisely
-        // during standard editor repainting iteration.
-        if (i < document.getLineCount()) {
-          processLogicalLine(document.getCharsSequence(), i, Font.PLAIN, getIndentToUse(), true);
-        }
-      }
-    }
-  };
-
-  private final EditorTextRepresentationHelper myTextRepresentationHelper;
-  private final SoftWrapsStorage               myStorage;
-  private final EditorEx                       myEditor;
-  private final SoftWrapPainter                myPainter;
-
-  private boolean myCustomIndentUsedLastTime;
-  private int myCustomIndentValueUsedLastTime;
-  private int myVisibleAreaWidth;
-
-  public DefaultSoftWrapApplianceManager(SoftWrapsStorage storage,
-                                         EditorEx editor,
-                                         SoftWrapPainter painter,
-                                         EditorTextRepresentationHelper textRepresentationHelper)
-  {
-    myStorage = storage;
-    myEditor = editor;
-    myPainter = painter;
-    myTextRepresentationHelper = textRepresentationHelper;
-  }
-
-  @SuppressWarnings({"AssignmentToForLoopParameter"})
-  @Override
-  public void registerSoftWrapIfNecessary(@NotNull CharSequence text, int start, int end, int x, int fontType, boolean temporary) {
-    dropDataIfNecessary();
-
-    if (myVisibleAreaWidth <= 0 || start >= end) {
-      return;
-    }
-
-    IndentType indent = getIndentToUse();
-    boolean useCustomIndent = indent == IndentType.CUSTOM;
-    int currentCustomIndent = myEditor.getSettings().getCustomSoftWrapIndent();
-    if (useCustomIndent ^ myCustomIndentUsedLastTime || (useCustomIndent && myCustomIndentValueUsedLastTime != currentCustomIndent)) {
-      myProcessedLogicalLines.clear();
-    }
-    myCustomIndentUsedLastTime = useCustomIndent;
-    myCustomIndentValueUsedLastTime = currentCustomIndent;
-
-    Document document = myEditor.getDocument();
-    int startLine = document.getLineNumber(start);
-    int endLine = document.getLineNumber(end);
-    for (int i = startLine; i <= endLine; i++) {
-      if (!myProcessedLogicalLines.contains(i) || (!temporary && myProcessedLogicalLines.get(i))) {
-        processLogicalLine(text, i, fontType, indent, temporary);
-        myProcessedLogicalLines.put(i, temporary);
-      }
-    }
-  }
-
-  private IndentType getIndentToUse() {
-    if (myEditor.getSettings().isUseCustomSoftWrapIndent()) {
-      return IndentType.CUSTOM;
-    }
-    return !myEditor.isViewer() && !myEditor.getDocument().isWritable() ? IndentType.AUTOMATIC : IndentType.NONE;
-  }
-
-  public DocumentListener getDocumentListener() {
-    return myDocumentListener;
-  }
-
-  private void dropDataIfNecessary() {
-    int currentVisibleAreaWidth = myEditor.getScrollingModel().getVisibleArea().width;
-    if (myVisibleAreaWidth == currentVisibleAreaWidth) {
-      return;
-    }
-
-    // Drop information about processed lines then.
-    myProcessedLogicalLines.clear();
-    myStorage.removeAll();
-    myVisibleAreaWidth = currentVisibleAreaWidth;
-  }
-
-  private void processLogicalLine(CharSequence text, int line, int fontType, IndentType indentType, boolean temporary) {
-    Document document = myEditor.getDocument();
-    int startOffset = document.getLineStartOffset(line);
-    int endOffset = document.getLineEndOffset(line);
-
-    // There is a possible case that this method is called for the approximate soft wraps positions calculation. E.g. the
-    // user can insert a long string to the end of the document and we don't want to perform horizontal scrolling to its end.
-    // Hence, we approximately define soft wraps for the inserted text assuming that there precise calculation will be performed
-    // on regular editor repainting iteration. However, we need to drop all those temporary soft wraps registered for
-    // the same line before.
-    myStorage.removeInRange(startOffset, endOffset + 1/* add 1 to handle possible situation when soft wrap is registered at the line end */);
-
-    if (indentType == IndentType.NONE) {
-      TIntArrayList offsets = calculateSoftWrapOffsets(text, startOffset, endOffset, fontType, 0);
-      registerSoftWraps(offsets, 0, temporary);
-      return;
-    }
-
-    // Understand if it's worth to define indent for soft wrap(s) to create and perform their actual construction and registration.
-    int prevLineIndentInColumns = 0;
-    
-    int firstNonSpaceSymbolIndex = startOffset;
-    for (; firstNonSpaceSymbolIndex < endOffset; firstNonSpaceSymbolIndex++) {
-      char c = text.charAt(firstNonSpaceSymbolIndex);
-      if (c != ' ' && c != '\t') {
-        break;
-      }
-    }
-    if (firstNonSpaceSymbolIndex > startOffset) {
-      prevLineIndentInColumns = myTextRepresentationHelper.toVisualColumnSymbolsNumber(text, startOffset, firstNonSpaceSymbolIndex, 0);
-    }
-
-    int spaceWidth = EditorUtil.getSpaceWidth(fontType, myEditor);
-    if (indentType == IndentType.CUSTOM) {
-      int indentInColumns = myEditor.getSettings().getCustomSoftWrapIndent();
-      TIntArrayList offsets = calculateSoftWrapOffsets(
-        text, startOffset, endOffset, fontType, (indentInColumns + prevLineIndentInColumns) * spaceWidth
-      );
-      registerSoftWraps(offsets, indentInColumns + prevLineIndentInColumns, temporary);
-      return;
-    }
-
-    int indentInColumns = getIndentSize();
-    int indentInColumnsToUse = 0;
-    TIntArrayList softWrapOffsetsToUse = null;
-    for (; indentInColumns >= 0; indentInColumns--) {
-      TIntArrayList offsets = calculateSoftWrapOffsets(
-        text, startOffset, endOffset, fontType, (prevLineIndentInColumns + indentInColumns) * spaceWidth
-      );
-      if (softWrapOffsetsToUse == null) {
-        softWrapOffsetsToUse = offsets;
-        indentInColumnsToUse = indentInColumns;
-        continue;
-      }
-      if (softWrapOffsetsToUse.size() > offsets.size()) {
-        softWrapOffsetsToUse = offsets;
-        indentInColumnsToUse = indentInColumns;
-      }
-    }
-
-    if (indentInColumnsToUse <= 0) {
-      processLogicalLine(text, line, fontType, IndentType.NONE, temporary);
-    }
-    else {
-      registerSoftWraps(softWrapOffsetsToUse, indentInColumnsToUse + prevLineIndentInColumns, temporary);
-    }
-  }
-
-  @SuppressWarnings({"AssignmentToForLoopParameter"})
-  private TIntArrayList calculateSoftWrapOffsets(CharSequence text, int start, int end, int fontType, int reservedWidth) {
-    TIntArrayList result = new TIntArrayList();
-
-    // Find offsets where soft wraps should be applied for the logical line in case of no indent usage.
-    int x = 0;
-    int beforeSoftWrapDrawingWidth = myPainter.getMinDrawingWidth(SoftWrapDrawingType.BEFORE_SOFT_WRAP_LINE_FEED);
-    int prevSoftWrapOffset = start;
-    CharBuffer buffer = CharBuffer.wrap(text);
-    for (int i = start; i < end; i++) {
-      int symbolWidth = myTextRepresentationHelper.textWidth(buffer, i, i + 1, fontType, x);
-      if (x + symbolWidth + beforeSoftWrapDrawingWidth >= myVisibleAreaWidth) {
-        int offset = calculateSoftWrapOffset(text, i, prevSoftWrapOffset, end);
-        if (offset >= end || offset <= prevSoftWrapOffset) {
-          // There is no way to insert soft wrap.
-          return result;
-        }
-        result.add(offset);
-        i = offset - 1; // Subtract one because of loop increment.
-        prevSoftWrapOffset = offset;
-        x = myPainter.getMinDrawingWidth(SoftWrapDrawingType.BEFORE_SOFT_WRAP_LINE_FEED)
-            + myPainter.getMinDrawingWidth(SoftWrapDrawingType.AFTER_SOFT_WRAP) + reservedWidth;
-        continue;
-      }
-      x += symbolWidth;
-    }
-    return result;
-  }
-
-  private int getIndentSize() {
-    Project project = myEditor.getProject();
-    if (project == null) return DEFAULT_INDENT_SIZE;
-    CodeStyleSettings settings = CodeStyleSettingsManager.getSettings(project);
-    if (settings == null) return DEFAULT_INDENT_SIZE;
-    VirtualFile file = myEditor.getVirtualFile();
-    if (file == null) return DEFAULT_INDENT_SIZE;
-    return settings.getIndentSize(file.getFileType());
-  }
-
-  private void registerSoftWraps(TIntArrayList offsets, int indentInColumns, boolean temporary) {
-    for (int i = 0; i < offsets.size(); i++) {
-      int offset = offsets.getQuick(i);
-      myStorage.storeOrReplace(new TextChangeImpl("\n" + StringUtil.repeatSymbol(' ', indentInColumns), offset), !temporary);
-    }
-  }
-
-  /**
-   * Calculates offset to use for soft wrap appliance from the given <code>(min; max]</code> interval.
-   *
-   * @param text        target text holder
-   * @param preferred   preferred position to use for soft wrapping. Implies that all symbols from <code>(preferred; max)</code>
-   *                    will be represented beyond the visible area, i.e. current method should try to find wrapping point
-   *                    at <code>(min; preferred]</code> interval;
-   * @param min         min offset to use (exclusive)
-   * @param max         max offset to use (inclusive)
-   * @return            wrapping offset to use (given <code>'max'</code> value should be returned if no more suitable point is found)
-   */
-  private int calculateSoftWrapOffset(CharSequence text, int preferred, int min, int max) {
-    LineWrapPositionStrategy strategy = LanguageLineWrapPositionStrategy.INSTANCE.forEditor(myEditor);
-    return strategy.calculateWrapPosition(text, min, max, preferred, true);
-  }
-}
diff --git a/platform/platform-impl/src/com/intellij/openapi/editor/impl/softwrap/SoftWrapApplianceManager.java b/platform/platform-impl/src/com/intellij/openapi/editor/impl/softwrap/SoftWrapApplianceManager.java
deleted file mode 100644 (file)
index c334cbb..0000000
+++ /dev/null
@@ -1,51 +0,0 @@
-/*
- * 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.softwrap;
-
-import org.jetbrains.annotations.NotNull;
-
-/**
- * Defines a contract for the service that receives notifications about requests to represent particular text and
- * creates and registers new soft wraps for that if necessary.
- * <p/>
- * Implementations of this interface are not obliged to be thread-safe.
- *
- * @author Denis Zhdanov
- * @since Jul 5, 2010 9:46:12 AM
- */
-public interface SoftWrapApplianceManager {
-
-  /**
-   * Defines a callback that is invoked on request to draw target text fragment and that can register new soft wraps in order
-   * to correctly represent it.
-   * <p/>
-   * Target text fragment to represent belongs to the given char sequence and lays at <code>[start; end)</code> interval.
-   * <p/>
-   * Please note that it's possible for soft wrap to occur inside <code>[start; end)</code> region - e.g. there is a possible
-   * case that particular single token is too long and we want to split it.
-   * <p/>
-   * <b>Note:</b> it's assumed that this method is called only on editor repainting.
-   *
-   * @param text      target text holder
-   * @param start     start offset of the token to process within the given char array (inclusive)
-   * @param end       end offset of the token to process within the given char array (exclusive)
-   * @param x         <code>'x'</code> coordinate within the given graphics buffer that will be used to start drawing the text
-   * @param fontType  font type used for the target text fragment representation
-   * @param temporary defines type of the current call. <code>'Temporary'</code> means that soft wraps registered during
-   *                  the processing should be recalculated on further invocations; they may be reused otherwise
-   */
-  void registerSoftWrapIfNecessary(@NotNull CharSequence text, int start, int end, int x, int fontType, boolean temporary);
-}
diff --git a/platform/platform-impl/src/com/intellij/openapi/editor/impl/softwrap/SoftWrapConstants.java b/platform/platform-impl/src/com/intellij/openapi/editor/impl/softwrap/SoftWrapConstants.java
deleted file mode 100644 (file)
index 9adedf9..0000000
+++ /dev/null
@@ -1,36 +0,0 @@
-/*
- * 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.softwrap;
-
-import com.intellij.openapi.editor.ex.PrioritizedDocumentListener;
-
-/**
- * Common place to store soft wrap-related constants.
- *
- * @author Denis Zhdanov
- * @since Aug 23, 2010 6:16:23 PM
- */
-public class SoftWrapConstants {
-
-  /** {@link PrioritizedDocumentListener#getPriority() document listener's priority} to use with {@link SoftWrapDocumentChangeManager} */
-  public static final int DOCUMENT_CHANGE_LISTENER_PRIORITY = 5;
-
-  /** {@link PrioritizedDocumentListener#getPriority() document listener's priority} to use with {@link SoftWrapApplianceManager} */
-  public static final int SOFT_WRAP_APPLIANCE_LISTENER_PRIORITY = 4;
-
-  private SoftWrapConstants() {
-  }
-}
index 44694e46c9b4a5fc16c9d5c72032303f2813329d..874b0c47355c647d15391531a9a1bf840d0edcbb 100644 (file)
  */
 package com.intellij.openapi.editor.impl.softwrap;
 
-import com.intellij.openapi.editor.*;
-import com.intellij.openapi.editor.ex.EditorEx;
-import com.intellij.openapi.editor.impl.EditorTextRepresentationHelper;
-import com.intellij.openapi.util.text.StringUtil;
-import com.intellij.util.text.CharArrayUtil;
+import com.intellij.openapi.editor.Document;
+import com.intellij.openapi.editor.LogicalPosition;
+import com.intellij.openapi.editor.VisualPosition;
 import org.jetbrains.annotations.NotNull;
-import org.jetbrains.annotations.Nullable;
-
-import java.awt.*;
-import java.util.List;
 
 /**
- * Encapsulates logic of various mappings (e.g. {@code 'offset -> logical position'}) and adjustments (e.g. adjust soft wrap unaware
- * logical position for the target visual position).
+ * Every document that is exposed to end-user via IJ editor has a number of various dimensions ({@link LogicalPosition logical}
+ * and {@link VisualPosition visual} positions, {@link Document#getCharsSequence() text offset} etc. It's very important to be
+ * able to map one dimension to another and do that effectively.
+ * <p/>
+ * Current interface defines a contract for such a mapper.
  *
  * @author Denis Zhdanov
- * @since Jul 7, 2010 2:31:04 PM
+ * @since Aug 31, 2010 10:23:15 AM
  */
-public class SoftWrapDataMapper {
-
-  private final EditorTextRepresentationHelper myTextRepresentationHelper;
-  private final EditorEx                       myEditor;
-  private final SoftWrapsStorage               myStorage;
-  //private final FontTypeProvider               myFontTypeProvider;
-
-  //public SoftWrapDataMapper(EditorEx editor,
-  //                          SoftWrapsStorage storage,
-  //                          EditorTextRepresentationHelper textRepresentationHelper)
-  //{
-  //  this(editor, storage, textRepresentationHelper, new IterationStateFontTypeProvider(editor));
-  //}
-
-  public SoftWrapDataMapper(EditorEx editor,
-                            SoftWrapsStorage storage,
-                            EditorTextRepresentationHelper textRepresentationHelper/*,
-                            FontTypeProvider fontTypeProvider*/)
-  {
-    myEditor = editor;
-    myStorage = storage;
-    myTextRepresentationHelper = textRepresentationHelper;
-    //myFontTypeProvider = fontTypeProvider;
-  }
-
-  @NotNull
-  public LogicalPosition visualToLogical(@NotNull VisualPosition visual) {
-    return toLogical(new VisualPositionBasedStrategy(visual));
-  }
-
-  @NotNull
-  public LogicalPosition offsetToLogicalPosition(int offset) {
-    OffsetBasedStrategy strategy = new OffsetBasedStrategy(myTextRepresentationHelper, myEditor.getDocument(), offset);
-    return toLogical(strategy);
-  }
-
-  @NotNull
-  private LogicalPosition toLogical(LogicalPositionCalculatorStrategy strategy) {
-    LogicalPositionCalculator calculator = new LogicalPositionCalculator(strategy);
-    return calculator.calculate();
-  }
+public interface SoftWrapDataMapper {
 
+  /**
+   * Maps given visual position to corresponding logical.
+   *
+   * @param visual    visual position to map
+   * @return          logical position that corresponds to the given visual position
+   * @throws IllegalStateException    if it's not possible to perform a mapping
+   */
   @NotNull
-  public VisualPosition adjustVisualPosition(@NotNull LogicalPosition logical, @NotNull VisualPosition visual) {
-    if (logical.visualPositionAware) {
-      // We don't need to recalculate logical position adjustments because given object already has them.
-      return logical.toVisualPosition();
-    }
-
-    List<TextChangeImpl> softWraps = myStorage.getSoftWraps();
-
-    // Check if there are registered soft wraps before the target logical position.
-    int maxOffset = myEditor.logicalPositionToOffset(logical);
-    int endIndex = myStorage.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 >= softWraps.size()) {
-      return visual;
-    }
-
-    int lineDiff = 0;
-    int column = -1;
-
-    int targetLogicalLineStartOffset = myEditor.logicalPositionToOffset(new LogicalPosition(logical.line, 0));
-    for (int i = endIndex; i >= 0; i--) {
-      TextChange softWrap = softWraps.get(i);
-      if (softWrap == null) {
-        assert false;
-        continue;
-      }
-
-      if (!isVisible(softWrap)) {
-        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);
-  }
-
-  private boolean isVisible(TextChange softWrap) {
-    FoldingModel foldingModel = myEditor.getFoldingModel();
-    int start = softWrap.getStart();
-
-    // There is a possible case that folding region starts just after soft wrap, i.e. soft wrap and folding region share the
-    // same offset. However, soft wrap is shown, hence, we also check offset just before the target one.
-    return !foldingModel.isOffsetCollapsed(start) || !foldingModel.isOffsetCollapsed(start - 1);
-  }
-
-  private static class Context implements Cloneable {
-
-    public int logicalLine;
-    public int logicalColumn;
-    public int visualLine;
-    public int visualColumn;
-    public int offset;
-    public int softWrapLinesBefore;
-    public int softWrapLinesCurrent;
-    public int softWrapColumnDiff;
-    public int foldedLines;
-    public int foldingColumnDiff;
-    public int x;
-
-    @NotNull
-    public LogicalPosition build() {
-      return new LogicalPosition(
-        logicalLine, logicalColumn, softWrapLinesBefore, softWrapLinesCurrent, softWrapColumnDiff, foldedLines, foldingColumnDiff
-      );
-    }
-
-    @Override
-    protected Context clone() {
-      Context result = new Context();
-      result.logicalLine = logicalLine;
-      result.logicalColumn = logicalColumn;
-      result.visualLine = visualLine;
-      result.visualColumn = visualColumn;
-      result.offset = offset;
-      result.softWrapLinesBefore = softWrapLinesBefore;
-      result.softWrapLinesCurrent = softWrapLinesCurrent;
-      result.softWrapColumnDiff = softWrapColumnDiff;
-      result.foldedLines = foldedLines;
-      result.foldingColumnDiff = foldingColumnDiff;
-      result.x = x;
-      return result;
-    }
-
-    private void onNewLine() {
-      softWrapLinesBefore += softWrapLinesCurrent;
-      softWrapLinesCurrent = 0;
-      softWrapColumnDiff = 0;
-      foldingColumnDiff = 0;
-      x = 0;
-    }
-  }
-
-  private class LogicalPositionCalculator {
-
-    public final LogicalPositionCalculatorStrategy strategy;
-
-    public Context context = new Context();
-
-    LogicalPositionCalculator(LogicalPositionCalculatorStrategy strategy) {
-      this.strategy = strategy;
-    }
-
-    @NotNull
-    public LogicalPosition calculate() {
-      FoldingProvider foldRegions = new FoldingProvider();
-      SoftWrapsProvider softWraps = new SoftWrapsProvider();
-
-      FoldRegion foldRegion = foldRegions.get();
-      TextChange softWrap = softWraps.get();
-
-      LogicalPosition result = null;
-      while (true) {
-        if (foldRegion == null && softWrap == null || strategy.exceeds(context)) {
-          return strategy.build(context);
-        }
-
-        if (foldRegion != null && softWrap != null) {
-          if (softWrap.getStart() <= foldRegion.getStartOffset()) {
-            result = process(softWrap);
-            softWrap = softWraps.get();
-          }
-          else {
-            result = process(foldRegion);
-            foldRegion = foldRegions.get();
-          }
-        }
-        else {
-          if (foldRegion != null) {
-            result = process(foldRegion);
-            foldRegion = foldRegions.get();
-          }
-          if (softWrap != null) {
-            result = process(softWrap);
-            softWrap = softWraps.get();
-          }
-        }
-        if (result != null) {
-          return result;
-        }
-      }
-    }
-
-    @Nullable
-    private LogicalPosition process(@NotNull FoldRegion region) {
-      int endDocumentOffset = myEditor.getDocument().getTextLength();
-      if (region.getEndOffset() >= endDocumentOffset) {
-        return advanceToOffset(endDocumentOffset).build();
-      }
-      if (region.getStartOffset() > context.offset) {
-        Context newContext = advanceToOffset(region.getStartOffset());
-        if (strategy.exceeds(newContext)) {
-          return strategy.build(context);
-        }
-        context = newContext;
-      }
-
-      Document document = myEditor.getDocument();
-      CharSequence text = document.getCharsSequence();
-      int foldingStartLine = document.getLineNumber(region.getStartOffset());
-
-      Context afterFolding = context.clone();
-      afterFolding.logicalLine += document.getLineNumber(region.getEndOffset()) - foldingStartLine;
-      int visualColumnInc = region.getPlaceholderText().length();  // Assuming that no tabulations are used at placeholder.
-      afterFolding.visualColumn += visualColumnInc;
-
-      int i = CharArrayUtil.shiftBackwardUntil(text, region.getEndOffset() - 1, "\n");
-      // Process multi-line folding.
-      if (i >= region.getStartOffset()) {
-        afterFolding.x = myTextRepresentationHelper.textWidth(text, i + 1, region.getEndOffset(), Font.PLAIN, 0);
-        afterFolding.logicalColumn = myTextRepresentationHelper.toVisualColumnSymbolsNumber(text, i + 1, region.getEndOffset(), 0);
-        afterFolding.softWrapLinesBefore += afterFolding.softWrapLinesCurrent;
-        afterFolding.softWrapLinesCurrent = 0;
-        afterFolding.softWrapColumnDiff = 0;
-        afterFolding.foldedLines += document.getLineNumber(region.getEndOffset()) - foldingStartLine;
-        afterFolding.foldingColumnDiff = afterFolding.visualColumn - afterFolding.logicalColumn;
-      }
-      // Process single-line folding
-      else {
-        int width = myTextRepresentationHelper.textWidth(text, region.getStartOffset(), region.getEndOffset(), Font.PLAIN, context.x);
-        int logicalColumnInc = myTextRepresentationHelper.toVisualColumnSymbolsNumber(
-          text, region.getStartOffset(), region.getEndOffset(), context.x
-        );
-        afterFolding.logicalColumn += logicalColumnInc;
-        afterFolding.x += width;
-        afterFolding.foldingColumnDiff += visualColumnInc - logicalColumnInc;
-      }
-      afterFolding.offset = region.getEndOffset();
-
-      if (!strategy.exceeds(afterFolding)) {
-        context = afterFolding;
-        return null;
-      }
-
-      return strategy.build(context, region);
-    }
-
-    @Nullable
-    private LogicalPosition process(@NotNull TextChange softWrap) {
-      int endDocumentOffset = myEditor.getDocument().getTextLength();
-      if (softWrap.getStart() >= endDocumentOffset) {
-        return strategy.build(context);
-      }
-      Context newContext = advanceToOffset(softWrap.getStart());
-      if (strategy.exceeds(newContext)) {
-        return strategy.build(context);
-      }
-      Document document = myEditor.getDocument();
-      int lastUsedLogicalLine = document.getLineNumber(context.offset);
-      context = newContext;
-
-      // Create context that points to the soft wrap end visual position.
-      Context afterSoftWrap = context.clone();
-      int lineFeeds = StringUtil.countNewLines(softWrap.getText());
-      afterSoftWrap.visualLine += lineFeeds;
-      afterSoftWrap.visualColumn = myEditor.getSoftWrapModel().getSoftWrapIndentWidthInColumns(softWrap);
-      afterSoftWrap.x = myEditor.getSoftWrapModel().getSoftWrapIndentWidthInPixels(softWrap);
-      if (lastUsedLogicalLine == context.logicalLine) {
-        afterSoftWrap.softWrapLinesCurrent += lineFeeds;
-      }
-      else {
-        afterSoftWrap.softWrapLinesBefore += context.softWrapLinesCurrent;
-        afterSoftWrap.softWrapLinesCurrent = lineFeeds;
-      }
-      afterSoftWrap.softWrapColumnDiff = afterSoftWrap.visualColumn - afterSoftWrap.logicalColumn;
-      afterSoftWrap.foldingColumnDiff = 0;
-
-      if (!strategy.exceeds(afterSoftWrap)) {
-        context = afterSoftWrap;
-        return null;
-      }
-      return strategy.build(context, softWrap);
-    }
-
-    private Context advanceToOffset(int newOffset) {
-      Context result = context.clone();
-      if (result.offset == newOffset) {
-        return result;
-      }
-
-      Document document = myEditor.getDocument();
-      CharSequence text = document.getCharsSequence();
-      int lastUsedLogicalLine = document.getLineNumber(context.offset);
-      int currentLogicalLine = document.getLineNumber(newOffset);
-
-      // Update state to the offset that corresponds to the same logical line that was used last time.
-      if (currentLogicalLine == lastUsedLogicalLine) {
-        int columnDiff = myTextRepresentationHelper.toVisualColumnSymbolsNumber(text, result.offset, newOffset, result.x);
-        result.logicalColumn += columnDiff;
-        result.visualColumn += columnDiff;
-        if (strategy.recalculateX(result)) {
-          result.x += myTextRepresentationHelper.textWidth(text, result.offset, newOffset, Font.PLAIN, result.x);
-        }
-      }
-      // Update state to offset that doesn't correspond to the same logical line that was used last time.
-      else {
-        int lineDiff = currentLogicalLine - lastUsedLogicalLine;
-        result.logicalLine += lineDiff;
-        result.visualLine += lineDiff;
-        int startLineOffset = document.getLineStartOffset(currentLogicalLine);
-        result.visualColumn = myTextRepresentationHelper.toVisualColumnSymbolsNumber(text, startLineOffset, newOffset, 0);
-        result.logicalColumn = result.visualColumn;
-        result.onNewLine();
-        if (strategy.recalculateX(result)) {
-          result.x = myTextRepresentationHelper.textWidth(text, startLineOffset, newOffset, Font.PLAIN, 0);
-        }
-      }
-      result.offset = newOffset;
-      return result;
-    }
-  }
+  LogicalPosition visualToLogical(@NotNull VisualPosition visual) throws IllegalStateException;
 
   /**
-   * Strategy interface for performing logical position calculation.
+   * Maps given offset to corresponding logical position.
+   *
+   * @param offset      offset to map
+   * @return            logical position that corresponds to the given offset
+   * @throws IllegalStateException    if it's not possible to perform a mapping
    */
-  private interface LogicalPositionCalculatorStrategy {
-    boolean exceeds(Context context);
-
-    /**
-     * Profiling shows that visual symbol width calculation (necessary for <code>'x'</code> coordinate update) is quite expensive.
-     * However, the whole logical position calculation algorithm contains many iterations and there is a big chance that many of them
-     * don't require <code>'x'</code> recalculation (e.g. we may map visual position to logical and see that current context visual
-     * line is less than the target, hence, we understand that 'x' coordinate will be reset to zero).
-     * <p/>
-     * This method allows to answer if we need to perform <code>'x'</code> recalculation for the given context (assuming that all
-     * its another parameters are up-to-date).
-     *
-     * @param context
-     * @return
-     */
-    boolean recalculateX(Context context);
-    @NotNull LogicalPosition build(Context context);
-    @NotNull LogicalPosition build(Context context, FoldRegion region);
-    @NotNull LogicalPosition build(Context context, TextChange softWrap);
-  }
-
-  private static class VisualPositionBasedStrategy implements LogicalPositionCalculatorStrategy {
-
-    private final VisualPosition myTargetVisual;
-
-    VisualPositionBasedStrategy(VisualPosition visual) {
-      myTargetVisual = visual;
-    }
-
-    @Override
-    public boolean exceeds(Context context) {
-      return context.visualLine > myTargetVisual.line
-        || (context.visualLine == myTargetVisual.line && context.visualColumn > myTargetVisual.column);
-    }
-
-    @Override
-    public boolean recalculateX(Context context) {
-      return context.visualLine == myTargetVisual.line;
-    }
-
-    @NotNull
-    @Override
-    public LogicalPosition build(Context context) {
-      if (context.visualLine == myTargetVisual.line) {
-        context.logicalColumn += myTargetVisual.column - context.visualColumn;
-        return context.build();
-      }
-      context.logicalLine += myTargetVisual.line - context.visualLine;
-      context.logicalColumn = myTargetVisual.column;
-      context.onNewLine();
-      return context.build();
-    }
-
-    @NotNull
-    @Override
-    public LogicalPosition build(Context context, FoldRegion region) {
-      // We just point to the logical position of folding region start if visual position points to collapsed fold region placeholder.
-      return context.build();
-    }
-
-    @NotNull
-    @Override
-    public LogicalPosition build(Context context, TextChange softWrap) {
-      if (myTargetVisual.line == context.visualLine) {
-        context.softWrapColumnDiff = myTargetVisual.column - context.logicalColumn - context.foldingColumnDiff;
-      }
-      else {
-        context.foldingColumnDiff = 0;
-        context.softWrapLinesCurrent += myTargetVisual.line - context.visualLine;
-        context.softWrapColumnDiff = myTargetVisual.column - context.logicalColumn;
-      }
-      return context.build();
-    }
-  }
-
-  private static class OffsetBasedStrategy implements LogicalPositionCalculatorStrategy {
-
-    private final EditorTextRepresentationHelper myRepresentationHelper;
-    private final Document                       myDocument;
-
-    private final int myOffset;
-
-    OffsetBasedStrategy(EditorTextRepresentationHelper representationHelper, Document document, int offset) {
-      myRepresentationHelper = representationHelper;
-      myDocument = document;
-      myOffset = offset;
-    }
-
-    @Override
-    public boolean exceeds(Context context) {
-      return context.offset > myOffset;
-    }
-
-    @Override
-    public boolean recalculateX(Context context) {
-      return true;
-    }
-
-    @NotNull
-    @Override
-    public LogicalPosition build(Context context) {
-      int targetLogicalLine = myDocument.getLineNumber(myOffset);
-      if (targetLogicalLine == context.logicalLine) {
-        context.logicalColumn
-          += myRepresentationHelper.toVisualColumnSymbolsNumber(myDocument.getCharsSequence(), context.offset, myOffset, context.x);
-        return context.build();
-      }
-      context.logicalLine = targetLogicalLine;
-      int i = CharArrayUtil.shiftBackwardUntil(myDocument.getCharsSequence(), myOffset - 1, "\n");
-      if (i >= context.offset) {
-        context.logicalColumn = myRepresentationHelper.toVisualColumnSymbolsNumber(myDocument.getCharsSequence(), i + 1, myOffset, 0);
-      }
-      else {
-        context.logicalColumn
-          = myRepresentationHelper.toVisualColumnSymbolsNumber(myDocument.getCharsSequence(), context.offset, myOffset, context.x);
-      }
-      context.onNewLine();
-      return context.build();
-    }
-
-    @NotNull
-    @Override
-    public LogicalPosition build(Context context, FoldRegion region) {
-      // We want to return logical position that corresponds to the visual start of the given folding region.
-      int startLine = myDocument.getLineNumber(region.getStartOffset());
-      int endLine = myDocument.getLineNumber(myOffset);
-      int lineFeeds = endLine - startLine;
-
-      if (lineFeeds > 0) {
-        context.logicalLine += lineFeeds;
-        context.foldedLines += lineFeeds;
-        context.onNewLine();
-        int i = CharArrayUtil.shiftBackwardUntil(myDocument.getCharsSequence(), myOffset - 1, "\n");
-        context.logicalColumn = myRepresentationHelper.toVisualColumnSymbolsNumber(myDocument.getCharsSequence(), i + 1, myOffset, 0);
-        context.foldingColumnDiff = context.visualColumn - context.logicalColumn;
-      }
-      else {
-        int logicalColumns
-          = myRepresentationHelper.toVisualColumnSymbolsNumber(myDocument.getCharsSequence(), region.getStartOffset(), myOffset, context.x);
-        context.logicalColumn += logicalColumns;
-        context.foldingColumnDiff -= logicalColumns;
-      }
-      return context.build();
-    }
-
-    @NotNull
-    @Override
-    public LogicalPosition build(Context context, TextChange softWrap) {
-      assert false; // Don't expect soft wrap do affect offset-based mapping request.
-      return new LogicalPosition(0, 0);
-    }
-  }
+  @NotNull
+  LogicalPosition offsetToLogicalPosition(int offset) throws IllegalStateException;
 
   /**
-   * Strategy interface for providing font type to use during working with editor text.
-   * <p/>
-   * It's primary purpose is to relief unit testing.
+   * Maps given logical position to corresponding visual position.
+   *
+   * @param logical                 logical position to map
+   * @param softWrapUnawareVisual   soft wrap-unaware visual position that corresponds to the given logical position
+   * @return                        visual position that corresponds to the given logical position
+   * @throws IllegalStateException  if it's not possible to perform a mapping
    */
-  //interface FontTypeProvider {
-  //  void init(int start);
-  //  int getFontType(int offset);
-  //  void cleanup();
-  //}
-
-  //private static class IterationStateFontTypeProvider implements FontTypeProvider {
-  //
-  //  private final EditorEx myEditor;
-  //
-  //  private IterationState myState;
-  //  private int            myFontType;
-  //
-  //  private IterationStateFontTypeProvider(EditorEx editor) {
-  //    myEditor = editor;
-  //  }
-  //
-  //  @Override
-  //  public void init(int start) {
-  //    myState = new IterationState(myEditor, start, false);
-  //    myFontType = myState.getMergedAttributes().getFontType();
-  //  }
-  //
-  //  @Override
-  //  public int getFontType(int offset) {
-  //    if (offset >= myState.getEndOffset()) {
-  //      myState.advance();
-  //      myFontType = myState.getMergedAttributes().getFontType();
-  //    }
-  //    return myFontType;
-  //  }
-  //
-  //  @Override
-  //  public void cleanup() {
-  //    myState = null;
-  //  }
-  //}
-
-  private class SoftWrapsProvider {
-
-    private final List<? extends TextChange> mySoftWraps;
-    private int myIndex;
-
-    SoftWrapsProvider() {
-      mySoftWraps = myStorage.getSoftWraps();
-    }
-
-    @Nullable
-    public TextChange get() {
-      if (myIndex < 0) {
-        return null;
-      }
-      while (myIndex < mySoftWraps.size()) {
-        TextChange result = mySoftWraps.get(myIndex++);
-        if (isVisible(result)) {
-          return result;
-        }
-      }
-      return null;
-    }
-  }
-
-  private class FoldingProvider {
-
-    private final FoldRegion[] myFoldRegions;
-    private int myIndex;
-
-    FoldingProvider() {
-      myFoldRegions = myEditor.getFoldingModel().fetchTopLevel();
-    }
-
-    @Nullable
-    public FoldRegion get() {
-      if (myFoldRegions == null || myIndex < 0) {
-        return null;
-      }
-      while (myIndex < myFoldRegions.length) {
-        FoldRegion result = myFoldRegions[myIndex++];
-        if (!result.isExpanded()) {
-          return result;
-        }
-      }
-      return null;
-    }
-  }
+  VisualPosition logicalToVisualPosition(@NotNull LogicalPosition logical, @NotNull VisualPosition softWrapUnawareVisual)
+    throws IllegalStateException;
 }
diff --git a/platform/platform-impl/src/com/intellij/openapi/editor/impl/softwrap/SoftWrapDocumentChangeManager.java b/platform/platform-impl/src/com/intellij/openapi/editor/impl/softwrap/SoftWrapDocumentChangeManager.java
deleted file mode 100644 (file)
index bc822f3..0000000
+++ /dev/null
@@ -1,142 +0,0 @@
-/*
- * 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.softwrap;
-
-import com.intellij.openapi.editor.Document;
-import com.intellij.openapi.editor.Editor;
-import com.intellij.openapi.editor.event.DocumentEvent;
-import com.intellij.openapi.editor.event.DocumentListener;
-import com.intellij.openapi.editor.ex.PrioritizedDocumentListener;
-import gnu.trove.TIntHashSet;
-import gnu.trove.TIntProcedure;
-import org.jetbrains.annotations.NotNull;
-
-import java.util.List;
-
-/**
- * Encapsulates the logic of performing necessary changes on soft wraps during target document change.
- *
- * @author Denis Zhdanov
- * @since Jul 7, 2010 2:28:10 PM
- */
-public class SoftWrapDocumentChangeManager implements PrioritizedDocumentListener {
-
-  private final TIntHashSet mySoftWrapsToRemoveIndices = new TIntHashSet();
-
-  private final SoftWrapsStorage myStorage;
-  private final Editor           myEditor;
-
-  public SoftWrapDocumentChangeManager(@NotNull Editor editor, @NotNull SoftWrapsStorage storage) {
-    myStorage = storage;
-    myEditor = editor;
-  }
-
-  /**
-   * Performs {@code 'soft wrap' -> 'hard wrap'} conversion for soft wrap at the given offset if any.
-   *
-   * @param offset    offset that may point to soft wrap to make hard wrap
-   * @return          <code>true</code> if given offset points to soft wrap that was made hard wrap; <code>false</code> otherwise
-   */
-  public boolean makeHardWrap(int offset) {
-    TextChangeImpl softWrap = myStorage.getSoftWrap(offset);
-    if (softWrap == null) {
-      return false;
-    }
-
-    myStorage.removeByIndex(myStorage.getSoftWrapIndex(offset));
-    myEditor.getDocument().replaceString(softWrap.getStart(), softWrap.getEnd(), softWrap.getText());
-    return true;
-  }
-
-  /**
-   * Flushes all changes to be applied to {@link SoftWrapsStorage registered soft wraps}.
-   * <p/>
-   * I.e. the main idea is that current manager listens for document changes but defers soft wraps modification
-   * until this method is called.
-   */
-  public void syncSoftWraps() {
-    mySoftWrapsToRemoveIndices.forEach(new TIntProcedure() {
-      @Override
-      public boolean execute(int value) {
-        myStorage.removeByIndex(value);
-        return true;
-      }
-    });
-    mySoftWrapsToRemoveIndices.clear();
-  }
-
-  @Override
-  public void beforeDocumentChange(DocumentEvent event) {
-    // Mark soft wraps to be removed.
-    Document document = myEditor.getDocument();
-    int startLine = document.getLineNumber(event.getOffset());
-    int startOffset = document.getLineStartOffset(startLine);
-    int endLine = document.getLineNumber(event.getOffset() + event.getOldLength());
-    int endOffset = document.getLineEndOffset(endLine);
-    markSoftWrapsForDeletion(startOffset, endOffset);
-  }
-
-  @Override
-  public void documentChanged(DocumentEvent event) {
-    // Update offsets for soft wraps that remain after the changed line(s).
-    applyDocumentChangeDiff(event);
-  }
-
-  @Override
-  public int getPriority() {
-    return SoftWrapConstants.DOCUMENT_CHANGE_LISTENER_PRIORITY;
-  }
-
-  private void markSoftWrapsForDeletion(int startOffset, int endOffset) {
-    List<TextChangeImpl> softWraps = myStorage.getSoftWraps();
-    int index = myStorage.getSoftWrapIndex(startOffset);
-    if (index < 0) {
-      index = -index - 1;
-    }
-    for (int i = index; i < softWraps.size(); i++) {
-      TextChangeImpl softWrap = softWraps.get(i);
-      if (softWrap.getStart() >= endOffset) {
-        break;
-      }
-      mySoftWrapsToRemoveIndices.add(i);
-    }
-  }
-
-  private void applyDocumentChangeDiff(DocumentEvent event) {
-    List<TextChangeImpl> softWraps = myStorage.getSoftWraps();
-    
-    // We use 'offset + 1' here because soft wrap is represented before the document symbol at the same offset, hence, document
-    // modification at particular offset doesn't affect soft wrap registered for the same offset.
-    int index = myStorage.getSoftWrapIndex(event.getOffset() + 1);
-    if (index < 0) {
-      index = -index - 1;
-    }
-
-    int diff = event.getNewLength() - event.getOldLength();
-    for (int i = index; i < softWraps.size(); i++) {
-      TextChangeImpl softWrap = softWraps.get(i);
-      if (mySoftWrapsToRemoveIndices.contains(i)) {
-        continue;
-      }
-
-      // Process only soft wraps not affected by the document change. E.g. there is a possible case that whole document text
-      // is replaced, hence, all registered soft wraps are affected by that and no offset advance should be performed.
-      if (softWrap.getStart() >= event.getOffset() + event.getOldLength()) {
-        softWrap.advance(diff);
-      }
-    }
-  }
-}
index 18b00c5e59d5a4d599e1648c6f682831a8e37ee3..bdb9345be3ac4b38ff2c4a512ffcf8f15671c0e8 100644 (file)
  */
 package com.intellij.openapi.editor.impl.softwrap;
 
-import com.intellij.openapi.editor.CaretModel;
-import com.intellij.openapi.editor.Editor;
-import com.intellij.openapi.editor.SoftWrapModel;
-import com.intellij.openapi.editor.TextChange;
+import com.intellij.openapi.editor.*;
 
 /**
  * Holds utility methods for soft wraps-related processing.
@@ -52,7 +49,7 @@ public class SoftWrapHelper {
     CaretModel caretModel = editor.getCaretModel();
     SoftWrapModel softWrapModel = editor.getSoftWrapModel();
     int offset = caretModel.getOffset();
-    TextChange softWrap = softWrapModel.getSoftWrap(offset);
+    SoftWrap softWrap = softWrapModel.getSoftWrap(offset);
     if (softWrap == null) {
       return false;
     }
diff --git a/platform/platform-impl/src/com/intellij/openapi/editor/impl/softwrap/SoftWrapImpl.java b/platform/platform-impl/src/com/intellij/openapi/editor/impl/softwrap/SoftWrapImpl.java
new file mode 100644 (file)
index 0000000..3667a47
--- /dev/null
@@ -0,0 +1,83 @@
+/*
+ * 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.softwrap;
+
+import com.intellij.openapi.editor.SoftWrap;
+import org.jetbrains.annotations.NotNull;
+
+/**
+ * {@link SoftWrap} implementation that is built around {@link TextChangeImpl}.
+ *
+ * @author Denis Zhdanov
+ * @since Sep 1, 2010 2:39:06 PM
+ */
+public class SoftWrapImpl implements SoftWrap {
+
+  private final TextChangeImpl myChange;
+  private final int myIndentInColumns;
+  private final int myIndentInPixels;
+
+  public SoftWrapImpl(@NotNull TextChangeImpl change, int indentInColumns, int indentInPixels) {
+    myChange = change;
+    myIndentInColumns = indentInColumns;
+    myIndentInPixels = indentInPixels;
+  }
+
+  @Override
+  public int getStart() {
+    return myChange.getStart();
+  }
+
+  @Override
+  public int getEnd() {
+    return myChange.getEnd();
+  }
+
+  @NotNull
+  @Override
+  public CharSequence getText() {
+    return myChange.getText();
+  }
+
+  @NotNull
+  @Override
+  public char[] getChars() {
+    return myChange.getChars();
+  }
+
+  @Override
+  public int getIndentInColumns() {
+    return myIndentInColumns;
+  }
+
+  @Override
+  public int getIndentInPixels() {
+    return myIndentInPixels;
+  }
+
+  public TextChangeImpl getChange() {
+    return myChange;
+  }
+
+  public void advance(int diff) {
+    myChange.advance(diff);
+  }
+
+  @Override
+  public String toString() {
+    return myChange.toString();
+  }
+}
index ad24ee00ab42c3f09757c522472382db4bfe4289..a4a7782517d7d83bddef352f27095381f9cc4b11 100644 (file)
  */
 package com.intellij.openapi.editor.impl.softwrap;
 
+import com.intellij.openapi.editor.SoftWrap;
 import com.intellij.openapi.editor.ex.SoftWrapChangeListener;
 import org.jetbrains.annotations.NotNull;
 import org.jetbrains.annotations.Nullable;
 
-import java.util.*;
-import java.util.concurrent.CopyOnWriteArraySet;
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.List;
 
 /**
  * Holds registered soft wraps and provides monitoring and management facilities for them.
@@ -32,9 +34,9 @@ import java.util.concurrent.CopyOnWriteArraySet;
  */
 public class SoftWrapsStorage {
 
-  private final List<TextChangeImpl>        myWraps     = new ArrayList<TextChangeImpl>();
-  private final List<TextChangeImpl>        myWrapsView = Collections.unmodifiableList(myWraps);
-  private final Set<SoftWrapChangeListener> myListeners = new CopyOnWriteArraySet<SoftWrapChangeListener>();
+  private final List<SoftWrapImpl>        myWraps     = new ArrayList<SoftWrapImpl>();
+  private final List<SoftWrapImpl>        myWrapsView = Collections.unmodifiableList(myWraps);
+  private final List<SoftWrapChangeListener> myListeners = new ArrayList<SoftWrapChangeListener>();
 
   /**
    * @return    <code>true</code> if there is at least one soft wrap registered at the current storage; <code>false</code> otherwise
@@ -44,7 +46,7 @@ public class SoftWrapsStorage {
   }
 
   @Nullable
-  public TextChangeImpl getSoftWrap(int offset) {
+  public SoftWrap getSoftWrap(int offset) {
     int i = getSoftWrapIndex(offset);
     return i >= 0 ? myWraps.get(i) : null;
   }
@@ -53,7 +55,7 @@ public class SoftWrapsStorage {
    * @return    view for registered soft wraps sorted by offset in ascending order if any; empty collection otherwise
    */
   @NotNull
-  public List<TextChangeImpl> getSoftWraps() {
+  public List<SoftWrapImpl> getSoftWraps() {
     return myWrapsView;
   }
 
@@ -74,7 +76,7 @@ public class SoftWrapsStorage {
     // is a bottleneck. The most probable reason is a big number of interface calls.
     while (start <= end) {
       int i = (start + end) >>> 1;
-      TextChangeImpl softWrap = myWraps.get(i);
+      SoftWrap softWrap = myWraps.get(i);
       int softWrapOffset = softWrap.getStart();
       if (softWrapOffset > offset) {
         end = i - 1;
@@ -96,8 +98,9 @@ public class SoftWrapsStorage {
    * @param notifyListeners   flag that indicates if registered listeners should be notified about soft wrap registration
    * @return                  previous soft wrap object stored for the same offset if any; <code>null</code> otherwise
    */
+  @SuppressWarnings({"ForLoopReplaceableByForEach"})
   @Nullable
-  public TextChangeImpl storeOrReplace(TextChangeImpl softWrap, boolean notifyListeners) {
+  public SoftWrap storeOrReplace(SoftWrapImpl softWrap, boolean notifyListeners) {
     int i = getSoftWrapIndex(softWrap.getStart());
     if (i >= 0) {
       return myWraps.set(i, softWrap);
@@ -106,8 +109,9 @@ public class SoftWrapsStorage {
     i = -i - 1;
     myWraps.add(i, softWrap);
     if (notifyListeners) {
-      for (SoftWrapChangeListener listener : myListeners) {
-        listener.softWrapAdded(softWrap);
+      // Use explicit loop as profiling shows that iterator-based processing is quite slow.
+      for (int j = 0; j < myListeners.size(); j++) {
+        myListeners.get(j).softWrapAdded(softWrap);
       }
     }
     return null;
@@ -121,11 +125,11 @@ public class SoftWrapsStorage {
    * @return        removed soft wrap if the one was found for the given index; <code>null</code> otherwise
    */
   @Nullable
-  public TextChangeImpl removeByIndex(int index) {
+  public SoftWrap removeByIndex(int index) {
     if (index < 0 || index >= myWraps.size()) {
       return null;
     }
-    TextChangeImpl removed = myWraps.remove(index);
+    SoftWrap removed = myWraps.remove(index);
     notifyListenersAboutRemoval();
     return removed;
   }
@@ -148,12 +152,12 @@ public class SoftWrapsStorage {
 
     int endIndex = startIndex;
     for (; endIndex < myWraps.size(); endIndex++) {
-      TextChangeImpl softWrap = myWraps.get(endIndex);
+      SoftWrap softWrap = myWraps.get(endIndex);
       if (softWrap.getStart() >= endOffset) {
         break;
       }
     }
-    
+
     if (endIndex > startIndex) {
       myWraps.subList(startIndex, endIndex).clear();
       notifyListenersAboutRemoval();
index 52f41be992cbe1d360a86e5150b6e026e01c2331..d4a08d2b13c9d63c7c1d4b2885e1a638db2bd5f7 100644 (file)
@@ -39,7 +39,9 @@ public class TextBasedSoftWrapPainter implements SoftWrapPainter {
 
   private final Map<SoftWrapDrawingType, char[]> mySymbols = new EnumMap<SoftWrapDrawingType, char[]>(SoftWrapDrawingType.class);
   private final Map<SoftWrapDrawingType, FontInfo> myFonts = new EnumMap<SoftWrapDrawingType, FontInfo>(SoftWrapDrawingType.class);
-  private final Map<SoftWrapDrawingType, Integer> myWidths = new EnumMap<SoftWrapDrawingType, Integer>(SoftWrapDrawingType.class);
+
+  /** Use array here because profiling indicates that using EnumMap here gives significant performance degradation. */
+  private final int[] myWidths = new int[SoftWrapDrawingType.values().length];
   private final Map<SoftWrapDrawingType, Integer> myVGaps = new EnumMap<SoftWrapDrawingType, Integer>(SoftWrapDrawingType.class);
 
   private final TextDrawingCallback myDrawingCallback;
@@ -77,7 +79,7 @@ public class TextBasedSoftWrapPainter implements SoftWrapPainter {
 
   @Override
   public int getMinDrawingWidth(@NotNull SoftWrapDrawingType drawingType) {
-    return myWidths.get(drawingType);
+    return myWidths[drawingType.ordinal()];
   }
 
   @Override
@@ -107,7 +109,7 @@ public class TextBasedSoftWrapPainter implements SoftWrapPainter {
       mySymbols.put(entry.getKey(), buffer);
       myFonts.put(entry.getKey(), fontInfo);
       FontMetrics metrics = component.getFontMetrics(fontInfo.getFont());
-      myWidths.put(entry.getKey(), metrics.charWidth(buffer[0]));
+      myWidths[entry.getKey().ordinal()] = metrics.charWidth(buffer[0]);
       int vGap = metrics.getDescent();
       myVGaps.put(entry.getKey(), vGap);
     }
diff --git a/platform/platform-impl/src/com/intellij/openapi/editor/impl/softwrap/mapping/AbstractDataProvider.java b/platform/platform-impl/src/com/intellij/openapi/editor/impl/softwrap/mapping/AbstractDataProvider.java
new file mode 100644 (file)
index 0000000..03f7b0d
--- /dev/null
@@ -0,0 +1,48 @@
+/*
+ * 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.softwrap.mapping;
+
+import com.intellij.openapi.editor.impl.softwrap.mapping.DataProvider;
+import com.intellij.openapi.util.Pair;
+import org.jetbrains.annotations.Nullable;
+
+/**
+ * Abstract super class for {@link DataProvider} implementations that use the same key all the time.
+ *
+ * @author Denis Zhdanov
+ * @since Aug 31, 2010 12:19:32 PM
+ */
+public abstract class AbstractDataProvider<K extends Comparable<? super K>, V> implements DataProvider<K, V> {
+
+  private final K myKey;
+
+  public AbstractDataProvider(K key) {
+    myKey = key;
+  }
+
+  @Nullable
+  @Override
+  public Pair<K, V> getData() {
+    V data = doGetData();
+    return data == null ? null : new Pair<K, V>(myKey, data);
+  }
+
+  /**
+   * @return    current data
+   */
+  @Nullable
+  protected abstract V doGetData();
+}
diff --git a/platform/platform-impl/src/com/intellij/openapi/editor/impl/softwrap/mapping/AbstractListBasedDataProvider.java b/platform/platform-impl/src/com/intellij/openapi/editor/impl/softwrap/mapping/AbstractListBasedDataProvider.java
new file mode 100644 (file)
index 0000000..cfbb7b2
--- /dev/null
@@ -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.impl.softwrap.mapping;
+
+import org.jetbrains.annotations.NotNull;
+
+import java.util.List;
+
+/**
+ * Generic {@link DataProvider} implementation that assumes that target data is available as list.
+ * <p/>
+ * Not thread-safe.
+ *
+ * @author Denis Zhdanov
+ * @since Aug 31, 2010 12:56:58 PM
+ */
+public abstract class AbstractListBasedDataProvider<K extends Comparable<? super K>, V> extends AbstractDataProvider<K, V> {
+
+  private final List<V> myData;
+  private int myIndex;
+
+  public AbstractListBasedDataProvider(@NotNull K key, @NotNull List<V> data) {
+    super(key);
+
+    // We use given collection instead of copying it to private list because profiling indicates that as an expensive operation
+    // when performed frequently.
+    myData = data;
+  }
+
+  @Override
+  protected V doGetData() {
+    if (myIndex < myData.size()) {
+      return myData.get(myIndex);
+    }
+    return null;
+  }
+
+  @Override
+  public boolean next() {
+    return ++myIndex < myData.size();
+  }
+
+  @Override
+  public void advance(int sortingKey) {
+
+    // We inline binary search here because profiling indicates that as a performance boost.
+    int start = myIndex;
+    int end = myData.size() - 1;
+
+    // We inline binary search here because profiling indicates that it becomes bottleneck to use Collections.binarySearch().
+    while (start <= end) {
+      int i = (end + start) >>> 1;
+      V data = myData.get(i);
+      if (getSortingKey(data) < sortingKey) {
+        start = i + 1;
+        continue;
+      }
+      if (getSortingKey(data) > sortingKey) {
+        end = i - 1;
+        continue;
+      }
+
+      myIndex = i;
+      return;
+    }
+    myIndex = start;
+  }
+
+  @Override
+  public int getSortingKey() {
+    if (myIndex < myData.size()) {
+      return getSortingKey(myData.get(myIndex));
+    }
+    return 0;
+  }
+
+  /**
+   * Sub-classes are expected to be able to derive sorting key for the every data instance.
+   *
+   * @param data    target data which sorting key should be returned
+   * @return        sorting key for the given data
+   */
+  protected abstract int getSortingKey(@NotNull V data);
+}
diff --git a/platform/platform-impl/src/com/intellij/openapi/editor/impl/softwrap/mapping/AbstractMappingStrategy.java b/platform/platform-impl/src/com/intellij/openapi/editor/impl/softwrap/mapping/AbstractMappingStrategy.java
new file mode 100644 (file)
index 0000000..3408ba4
--- /dev/null
@@ -0,0 +1,150 @@
+/*
+ * 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.softwrap.mapping;
+
+import com.intellij.openapi.editor.Document;
+import com.intellij.openapi.editor.Editor;
+import com.intellij.openapi.editor.FoldRegion;
+import com.intellij.openapi.editor.impl.EditorTextRepresentationHelper;
+import com.intellij.openapi.editor.impl.softwrap.SoftWrapsStorage;
+import com.intellij.openapi.util.Computable;
+import com.intellij.openapi.util.Pair;
+import org.jetbrains.annotations.NotNull;
+import org.jetbrains.annotations.Nullable;
+
+import java.awt.*;
+
+/**
+ * Abstract super class for mapping strategies that encapsulates shared logic of advancing the context to the points
+ * of specific types. I.e. it's main idea is to ask sub-class if target dimension lays inside particular region
+ * (e.g. soft wrap, fold region etc) and use common algorithm for advancing context to the region's end in the case
+ * of negative answer.
+ * <p/>
+ * Not thread-safe.
+ *
+ * @param <T>     resulting document dimension type
+ */
+abstract class AbstractMappingStrategy<T> implements MappingStrategy<T> {
+
+  protected static final CacheEntry SEARCH_KEY = new CacheEntry(0, null, null, null);
+
+  protected final CacheEntry myCacheEntry;
+  protected final Editor myEditor;
+  protected final EditorTextRepresentationHelper myRepresentationHelper;
+  protected final SoftWrapsStorage myStorage;
+  private final T myEagerMatch;
+
+  AbstractMappingStrategy(Computable<Pair<CacheEntry, T>> cacheEntryProvider, Editor editor, SoftWrapsStorage storage,
+                          EditorTextRepresentationHelper representationHelper) throws IllegalStateException
+  {
+    myEditor = editor;
+    myStorage = storage;
+    myRepresentationHelper = representationHelper;
+
+    Pair<CacheEntry, T> pair = cacheEntryProvider.compute();
+    myCacheEntry = pair.first;
+    myEagerMatch = pair.second;
+  }
+
+  @Nullable
+  @Override
+  public T eagerMatch() {
+    return myEagerMatch;
+  }
+
+  @NotNull
+  @Override
+  public ProcessingContext buildInitialContext() {
+    return myCacheEntry.buildStartLineContext();
+  }
+
+  protected FoldingData getFoldRegionData(FoldRegion foldRegion) {
+    return myCacheEntry.getFoldingData().get(foldRegion.getStartOffset());
+  }
+
+  @Override
+  public T advance(ProcessingContext context, int offset) {
+    T result = buildIfExceeds(context, offset);
+    if (result != null) {
+      return result;
+    }
+
+    // Update context state and continue processing.
+    context.logicalLine = myEditor.getDocument().getLineNumber(offset);
+    int diff = offset - context.offset;
+    context.visualColumn += diff;
+    context.logicalColumn += diff;
+    context.offset = offset;
+    return null;
+  }
+
+  @Nullable
+  protected abstract T buildIfExceeds(ProcessingContext context, int offset);
+
+  @Override
+  public T processFoldRegion(ProcessingContext context, FoldRegion foldRegion) {
+    T result = buildIfExceeds(context, foldRegion);
+    if (result != null) {
+      return result;
+    }
+
+    Document document = myEditor.getDocument();
+    int endOffsetLogicalLine = document.getLineNumber(foldRegion.getEndOffset());
+    int collapsedSymbolsWidthInColumns;
+    if (context.logicalLine == endOffsetLogicalLine) {
+      // Single-line fold region.
+      FoldingData foldingData = getFoldRegionData(foldRegion);
+      if (foldingData == null) {
+        assert false;
+        collapsedSymbolsWidthInColumns = context.visualColumn * myRepresentationHelper.textWidth(" ", 0, 1, Font.PLAIN, 0);
+      }
+      else {
+        collapsedSymbolsWidthInColumns = foldingData.getCollapsedSymbolsWidthInColumns();
+      }
+    }
+    else {
+      // Multi-line fold region.
+      collapsedSymbolsWidthInColumns = myRepresentationHelper.toVisualColumnSymbolsNumber(
+        document.getCharsSequence(), foldRegion.getStartOffset(), foldRegion.getEndOffset(), 0
+      );
+      context.softWrapColumnDiff = 0;
+      context.softWrapLinesBefore += context.softWrapLinesCurrent;
+      context.softWrapLinesCurrent = 0;
+    }
+
+    context.advance(foldRegion, collapsedSymbolsWidthInColumns);
+    return null;
+  }
+
+  @Nullable
+  protected abstract T buildIfExceeds(@NotNull ProcessingContext context, @NotNull FoldRegion foldRegion);
+
+  @Override
+  public T processTabulation(ProcessingContext context, TabData tabData) {
+    T result = buildIfExceeds(context, tabData);
+    if (result != null) {
+      return result;
+    }
+
+    context.visualColumn += tabData.widthInColumns;
+    context.logicalColumn += tabData.widthInColumns;
+    context.offset++;
+    return null;
+  }
+
+  @Nullable
+  protected abstract T buildIfExceeds(ProcessingContext context, TabData tabData);
+}
diff --git a/platform/platform-impl/src/com/intellij/openapi/editor/impl/softwrap/mapping/CacheEntry.java b/platform/platform-impl/src/com/intellij/openapi/editor/impl/softwrap/mapping/CacheEntry.java
new file mode 100644 (file)
index 0000000..9718f67
--- /dev/null
@@ -0,0 +1,229 @@
+/*
+ * 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.softwrap.mapping;
+
+import com.intellij.openapi.editor.Editor;
+import com.intellij.openapi.editor.FoldRegion;
+import com.intellij.openapi.editor.impl.EditorTextRepresentationHelper;
+import gnu.trove.TIntObjectHashMap;
+import gnu.trove.TIntObjectProcedure;
+import org.jetbrains.annotations.NotNull;
+
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.List;
+
+/**
+ * Encapsulates information to cache for the single visual line.
+ */
+@SuppressWarnings("unchecked")
+class CacheEntry implements Comparable<CacheEntry>, Cloneable {
+
+  private static final TIntObjectHashMap<FoldingData> DUMMY = new TIntObjectHashMap<FoldingData>();
+
+  public int visualLine;
+
+  public int startLogicalLine;
+  public int startLogicalColumn;
+  public int startOffset;
+  public int startSoftWrapLinesBefore;
+  public int startSoftWrapLinesCurrent;
+  public int startSoftWrapColumnDiff;
+  public int startFoldedLines;
+  public int startFoldingColumnDiff;
+
+  public int endOffset;
+  public int endLogicalLine;
+  public int endLogicalColumn;
+  public int endVisualColumn;
+  public int endSoftWrapLinesBefore;
+  public int endSoftWrapLinesCurrent;
+  public int endSoftWrapColumnDiff;
+  public int endFoldedLines;
+  public int endFoldingColumnDiff;
+
+  public boolean locked;
+
+  private final List<CacheEntry> myCache;
+  private final Editor myEditor;
+  private final EditorTextRepresentationHelper myRepresentationHelper;
+
+  /** Holds positions for the tabulation symbols on a target visual line sorted by offset in ascending order. */
+  private List<TabData> myTabPositions = Collections.EMPTY_LIST;
+
+  /** Holds information about single line fold regions representation data. */
+  private TIntObjectHashMap<FoldingData> myFoldingData = DUMMY;
+
+  CacheEntry(int visualLine, Editor editor, EditorTextRepresentationHelper representationHelper, List<CacheEntry> cache) {
+    this.visualLine = visualLine;
+    myEditor = editor;
+    myRepresentationHelper = representationHelper;
+    myCache = cache;
+  }
+
+  public void setLineStartPosition(@NotNull ProcessingContext context, int i) {
+    assert context.visualColumn == 0;
+    startLogicalLine = context.logicalLine;
+    startLogicalColumn = context.logicalColumn;
+    visualLine = context.visualLine;
+    startOffset = context.offset;
+    startSoftWrapLinesBefore = context.softWrapLinesBefore;
+    startSoftWrapLinesCurrent = context.softWrapLinesCurrent;
+    startSoftWrapColumnDiff = context.softWrapColumnDiff;
+    startFoldedLines = context.foldedLines;
+    startFoldingColumnDiff = context.foldingColumnDiff;
+
+    if (i > 1 && (startOffset - myCache.get(i - 1).endOffset) > 1) {
+      assert false;
+    }
+  }
+
+  public void setLineEndPosition(@NotNull ProcessingContext context) {
+    endOffset = context.offset;
+    endLogicalLine = context.logicalLine;
+    endLogicalColumn = context.logicalColumn;
+    endVisualColumn = context.visualColumn;
+    endSoftWrapLinesBefore = context.softWrapLinesBefore;
+    endSoftWrapLinesCurrent = context.softWrapLinesCurrent;
+    endSoftWrapColumnDiff = context.softWrapColumnDiff;
+    endFoldedLines = context.foldedLines;
+    endFoldingColumnDiff = context.foldingColumnDiff;
+  }
+
+  public ProcessingContext buildStartLineContext() {
+    ProcessingContext result = new ProcessingContext(myEditor, myRepresentationHelper);
+    result.logicalLine = startLogicalLine;
+    result.logicalColumn = startLogicalColumn;
+    result.offset = startOffset;
+    result.visualLine = visualLine;
+    result.visualColumn = 0;
+    result.softWrapLinesBefore = startSoftWrapLinesBefore;
+    result.softWrapLinesCurrent = startSoftWrapLinesCurrent;
+    result.softWrapColumnDiff = startSoftWrapColumnDiff;
+    result.foldedLines = startFoldedLines;
+    result.foldingColumnDiff = startFoldingColumnDiff;
+    return result;
+  }
+
+  public ProcessingContext buildEndLineContext() {
+    ProcessingContext result = new ProcessingContext(myEditor, myRepresentationHelper);
+    result.logicalLine = endLogicalLine;
+    result.logicalColumn = endLogicalColumn;
+    result.offset = endOffset;
+    result.visualLine = visualLine;
+    result.visualColumn = endVisualColumn;
+    result.softWrapLinesBefore = endSoftWrapLinesBefore;
+    result.softWrapLinesCurrent = endSoftWrapLinesCurrent;
+    result.softWrapColumnDiff = endSoftWrapColumnDiff;
+    result.foldedLines = endFoldedLines;
+    result.foldingColumnDiff = endFoldingColumnDiff;
+    return result;
+  }
+
+  public void store(FoldRegion foldRegion, int startX) {
+    store(new FoldingData(foldRegion, startX, myRepresentationHelper, myEditor), foldRegion.getStartOffset());
+  }
+
+  public void store(FoldingData foldData, int offset) {
+    if (myFoldingData == DUMMY) {
+      myFoldingData = new TIntObjectHashMap<FoldingData>();
+    }
+    myFoldingData.put(offset, foldData);
+  }
+
+  public TIntObjectHashMap<FoldingData> getFoldingData() {
+    return myFoldingData;
+  }
+
+  public List<TabData> getTabData() {
+    return myTabPositions;
+  }
+
+  public void storeTabData(ProcessingContext context) {
+    storeTabData(new TabData(context));
+  }
+
+  public void storeTabData(TabData tabData) {
+    if (myTabPositions == Collections.EMPTY_LIST) {
+      myTabPositions = new ArrayList<TabData>();
+    }
+    myTabPositions.add(tabData);
+  }
+
+  public void advance(final int offsetDiff) {
+    startOffset += offsetDiff;
+    endOffset += offsetDiff;
+    for (TabData tabData : myTabPositions) {
+      tabData.offset += offsetDiff;
+    }
+    final TIntObjectHashMap<FoldingData> newFoldingData = new TIntObjectHashMap<FoldingData>();
+    myFoldingData.forEachEntry(new TIntObjectProcedure<FoldingData>() {
+      @Override
+      public boolean execute(int offset, FoldingData foldingData) {
+        newFoldingData.put(offset + offsetDiff, foldingData);
+        return true;
+      }
+    });
+    myFoldingData = newFoldingData;
+  }
+
+  @Override
+  public int compareTo(CacheEntry e) {
+    return visualLine - e.visualLine;
+  }
+
+  @Override
+  public String toString() {
+    return "visual line: " + visualLine + ", offsets: " + startOffset + "-" + endOffset;
+  }
+
+  @Override
+  protected CacheEntry clone() {
+    final CacheEntry result = new CacheEntry(visualLine, myEditor, myRepresentationHelper, myCache);
+
+    result.startLogicalLine = startLogicalLine;
+    result.startLogicalColumn = startLogicalColumn;
+    result.startOffset = startOffset;
+    result.startSoftWrapLinesBefore = startSoftWrapLinesBefore;
+    result.startSoftWrapLinesCurrent = startSoftWrapLinesCurrent;
+    result.startSoftWrapColumnDiff = startSoftWrapColumnDiff;
+    result.startFoldedLines = startFoldedLines;
+    result.startFoldingColumnDiff = startFoldingColumnDiff;
+
+    result.endOffset = endOffset;
+    result.endLogicalLine = endLogicalLine;
+    result.endLogicalColumn = endLogicalColumn;
+    result.endVisualColumn = endVisualColumn;
+    result.endSoftWrapLinesBefore = endSoftWrapLinesBefore;
+    result.endSoftWrapLinesCurrent = endSoftWrapLinesCurrent;
+    result.endSoftWrapColumnDiff = endSoftWrapColumnDiff;
+    result.endFoldedLines = endFoldedLines;
+    result.endFoldingColumnDiff = endFoldingColumnDiff;
+
+    myFoldingData.forEachEntry(new TIntObjectProcedure<FoldingData>() {
+      @Override
+      public boolean execute(int offset, FoldingData foldData) {
+        result.store(foldData, offset);
+        return true;
+      }
+    });
+    for (TabData tabPosition : myTabPositions) {
+      result.storeTabData(tabPosition);
+    }
+
+    return result;
+  }
+}
diff --git a/platform/platform-impl/src/com/intellij/openapi/editor/impl/softwrap/mapping/CachingSoftWrapDataMapper.java b/platform/platform-impl/src/com/intellij/openapi/editor/impl/softwrap/mapping/CachingSoftWrapDataMapper.java
new file mode 100644 (file)
index 0000000..9b5aefb
--- /dev/null
@@ -0,0 +1,531 @@
+/*
+ * 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.softwrap.mapping;
+
+import com.intellij.openapi.editor.*;
+import com.intellij.openapi.editor.ex.EditorEx;
+import com.intellij.openapi.editor.ex.FoldingModelEx;
+import com.intellij.openapi.editor.impl.EditorTextRepresentationHelper;
+import com.intellij.openapi.editor.impl.softwrap.*;
+import com.intellij.openapi.util.Pair;
+import gnu.trove.TIntObjectProcedure;
+import org.jetbrains.annotations.NotNull;
+
+import java.util.*;
+import java.util.List;
+
+/**
+ * {@link SoftWrapDataMapper} implementation that is implemented using the following principles:
+ * <pre>
+ * <ul>
+ *   <li>it caches document dimension for starts of all visual lines;</li>
+ *   <li>
+ *        every time document position mapping should be performed this mapper chooses target visual line to process
+ *        and calculates target position from its start (note that we can optimize that in order to choose calculation
+ *        direction - 'from start' or 'from end');
+ *   </li>
+ *   <li>
+ *        information about target visual lines is updated incrementally on document changes, i.e. we're trying to reduce
+ *        calculations number as much as possible;
+ *   </li>
+ * </ul>
+ * </pre>
+ * <p/>
+ * Not thread-safe.
+ *
+ * @author Denis Zhdanov
+ * @since Aug 31, 2010 10:24:47 AM
+ */
+public class CachingSoftWrapDataMapper implements SoftWrapDataMapper, SoftWrapAwareDocumentParsingListener {
+
+  /** Caches information for the document visual line starts sorted in ascending order. */
+  private final List<CacheEntry> myCache                               = new ArrayList<CacheEntry>();
+  private final CacheEntry       mySearchKey                           = new CacheEntry(0, null, null, null);
+  private final List<CacheEntry> myNotAffectedByUpdateTailCacheEntries = new ArrayList<CacheEntry>();
+  private final CacheState       myBeforeChangeState                   = new CacheState();
+  private final CacheState       myAfterChangeState                    = new CacheState();
+
+  private final EditorEx myEditor;
+  private final SoftWrapsStorage myStorage;
+  private final EditorTextRepresentationHelper myRepresentationHelper;
+
+  public CachingSoftWrapDataMapper(@NotNull EditorEx editor, @NotNull SoftWrapsStorage storage,
+                                   @NotNull EditorTextRepresentationHelper representationHelper)
+  {
+    myEditor = editor;
+    myStorage = storage;
+    myRepresentationHelper = representationHelper;
+  }
+
+  @NotNull
+  @Override
+  public LogicalPosition visualToLogical(@NotNull VisualPosition visual) throws IllegalStateException {
+    if (myCache.isEmpty()) {
+      return new LogicalPosition(visual.line, visual.column, 0, 0, 0, 0, 0);
+    }
+
+    // There is a possible case that we're asked to map visual position that lay beyond the document content (e.g. there is a
+    // 'virtual space' below the document text and it's perfectly possible to map visual position that points into it).
+    // We consider that there are no soft wraps, tabulations and fold regions between the last cached visual line and target line then.
+    if (!myCache.isEmpty()) {
+      CacheEntry cacheEntry = myCache.get(myCache.size() - 1);
+      if (visual.line > cacheEntry.visualLine) {
+        int lineDiff = visual.line - cacheEntry.visualLine;
+        return new LogicalPosition(
+          cacheEntry.endLogicalLine + lineDiff, visual.column, cacheEntry.endSoftWrapLinesBefore + cacheEntry.endSoftWrapLinesCurrent,
+          0, 0, cacheEntry.endFoldedLines, 0
+        );
+      }
+    }
+
+    return calculate(new VisualToLogicalCalculationStrategy(visual, myCache, myEditor, myStorage, myRepresentationHelper));
+  }
+
+  @NotNull
+  @Override
+  public LogicalPosition offsetToLogicalPosition(int offset) {
+    return calculate(new OffsetToLogicalCalculationStrategy(offset, myEditor, myRepresentationHelper, myCache, myStorage));
+  }
+
+  @Override
+  public VisualPosition logicalToVisualPosition(@NotNull LogicalPosition logical, @NotNull VisualPosition softWrapUnawareVisual)
+    throws IllegalStateException
+  {
+    // We can't use standard strategy-based approach with logical -> visual mapping because folding processing quite often
+    // temporarily disables folding. So, there is an inconsistency between cached data (folding aware) and current folding
+    // state. So, we use direct soft wraps adjustment instead of normal calculation.
+
+
+    if (logical.visualPositionAware) {
+      // We don't need to recalculate logical position adjustments because given object already has them.
+      return logical.toVisualPosition();
+    }
+
+    List<? extends SoftWrap> softWraps = myStorage.getSoftWraps();
+
+    // Check if there are registered soft wraps before the target logical position.
+    int maxOffset = myEditor.logicalPositionToOffset(logical);
+    int endIndex = myStorage.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 that
+                                // is located before the given logical position.
+    }
+
+    // Return eagerly if no soft wraps are registered before the target offset.
+    if (endIndex < 0 || endIndex >= softWraps.size()) {
+      return softWrapUnawareVisual;
+    }
+
+    int lineDiff = 0;
+    int column = -1;
+
+    int targetLogicalLineStartOffset = myEditor.logicalPositionToOffset(new LogicalPosition(logical.line, 0));
+    for (int i = endIndex; i >= 0; i--) {
+      SoftWrap softWrap = softWraps.get(i);
+      if (softWrap == null) {
+        assert false;
+        continue;
+      }
+
+      // Count soft wrap column offset only if it's located at the same line as the target offset.
+      if (column < 0 && softWrap.getStart() >= targetLogicalLineStartOffset) {
+        column = softWrap.getIndentInColumns() + myRepresentationHelper.toVisualColumnSymbolsNumber(
+          myEditor.getDocument().getCharsSequence(), softWrap.getStart(), maxOffset, softWrap.getIndentInPixels()
+        );
+
+        // Count lines introduced by the current soft wrap. We assume that every soft wrap has a single line feed.
+        lineDiff++;
+      }
+      else {
+        // We assume that every soft wrap has a single line feed.
+        lineDiff += i + 1;
+        break;
+      }
+    }
+
+    int columnToUse = column >= 0 ? column : softWrapUnawareVisual.column;
+    return new VisualPosition(softWrapUnawareVisual.line + lineDiff, columnToUse);
+  }
+
+  public void release() {
+    myCache.clear();
+  }
+
+  private <T> T calculate(@NotNull MappingStrategy<T> strategy) throws IllegalStateException {
+    T eagerMatch = strategy.eagerMatch();
+    if (eagerMatch != null) {
+      return eagerMatch;
+    }
+
+    ProcessingContext context = strategy.buildInitialContext();
+
+    // Folding model doesn't return information about fold regions if their 'enabled' state is set to 'false'. Unfortunately,
+    // such situation occurs not only on manual folding disabling but on internal processing as well. E.g. 'enabled' is set
+    // to 'false' during fold preview representation. So, we set it to 'true' in order to retrieve fold regions and restore
+    // to previous state after that.
+    FoldingModelEx foldingModel = myEditor.getFoldingModel();
+    boolean foldingState = foldingModel.isFoldingEnabled();
+    foldingModel.setFoldingEnabled(true);
+    CompositeDataProvider provider;
+    try {
+      provider = new CompositeDataProvider(
+        new SoftWrapsDataProvider(myStorage), new FoldingDataProvider(myEditor.getFoldingModel().fetchTopLevel()),
+        getTabulationDataProvider(context.visualLine)
+      );
+    }
+    finally {
+      foldingModel.setFoldingEnabled(foldingState);
+    }
+    provider.advance(context.offset);
+
+    while (provider.hasData()) {
+      Pair<SoftWrapDataProviderKeys, ?> data = provider.getData();
+      T result = null;
+      int sortingKey = provider.getSortingKey();
+
+      // There is a possible case that, say, fold region is soft wrapped. We don't want to perform unnecessary then.
+      if (context.offset < sortingKey) {
+        result = strategy.advance(context, sortingKey);
+        if (result != null) {
+          return result;
+        }
+      }
+
+      switch (data.first) {
+        case SOFT_WRAP: result = strategy.processSoftWrap(context, (SoftWrap)data.second); break;
+        case COLLAPSED_FOLDING: result = strategy.processFoldRegion(context, (FoldRegion)data.second); break;
+        case TABULATION: result = strategy.processTabulation(context, (TabData)data.second); break;
+      }
+      if (result != null) {
+        return result;
+      }
+      provider.next();
+    }
+    return strategy.build(context);
+  }
+
+  private TabulationDataProvider getTabulationDataProvider(int visualLine) throws IllegalStateException {
+    mySearchKey.visualLine = visualLine;
+    int i = Collections.binarySearch(myCache, mySearchKey);
+    List<TabData> tabs;
+    if (i >= 0) {
+      tabs = myCache.get(i).getTabData();
+    }
+    else {
+      assert false;
+      tabs = Collections.emptyList();
+    }
+    return new TabulationDataProvider(tabs);
+  }
+
+  @Override
+  public void onProcessedSymbol(@NotNull ProcessingContext context) {
+    switch (context.symbol) {
+      case '\n': onLineFeed(context); break;
+      case '\t': onTabulation(context); break;
+      default: onNonLineFeedSymbol(context); break;
+    }
+  }
+
+  @Override
+  public void onCollapsedFoldRegion(@NotNull FoldRegion foldRegion, int x, int visualLine) {
+    CacheEntry cacheEntry = getCacheEntryForVisualLine(visualLine);
+    cacheEntry.store(foldRegion, x);
+  }
+
+  @Override
+  public void beforeSoftWrap(@NotNull ProcessingContext context) {
+    CacheEntry cacheEntry = getCacheEntryForVisualLine(context.visualLine);
+    cacheEntry.setLineEndPosition(context);
+  }
+
+  @Override
+  public void afterSoftWrapLineFeed(@NotNull ProcessingContext context) {
+    CacheEntry cacheEntry = getCacheEntryForVisualLine(context.visualLine);
+    cacheEntry.setLineStartPosition(context, context.visualLine);
+  }
+
+  @Override
+  public void revertToOffset(final int offset, int visualLine) {
+    CacheEntry cacheEntry = getCacheEntryForVisualLine(visualLine);
+
+    // Remove information about cached tabulation symbol data.
+    int tabIndex = 0;
+    for (; tabIndex < cacheEntry.getTabData().size(); tabIndex++) {
+      if (cacheEntry.getTabData().get(tabIndex).offset >= offset) {
+        break;
+      }
+    }
+    if (tabIndex < cacheEntry.getTabData().size() - 1) {
+      cacheEntry.getTabData().subList(tabIndex, cacheEntry.getTabData().size()).clear();
+    }
+
+    // Remove information about cached single line fold regions.
+    cacheEntry.getFoldingData().retainEntries(new TIntObjectProcedure<FoldingData>() {
+      @Override
+      public boolean execute(int foldStartOffset, FoldingData data) {
+        return foldStartOffset < offset;
+      }
+    });
+  }
+
+  /**
+   * Updates inner cache for the context that points to line feed symbol
+   *
+   * @param context    processing context that points to line feed symbol
+   */
+  private void onLineFeed(@NotNull ProcessingContext context) {
+    CacheEntry cacheEntry = getCacheEntryForVisualLine(context.visualLine);
+    cacheEntry.setLineEndPosition(context);
+
+    cacheEntry = getCacheEntryForVisualLine(context.visualLine + 1);
+
+    ProcessingContext newLineContext = context.clone();
+    newLineContext.onNewLine();
+    newLineContext.offset++;
+    cacheEntry.setLineStartPosition(newLineContext, context.visualLine + 1);
+    cacheEntry.setLineEndPosition(newLineContext);
+  }
+
+  /**
+   * Updates inner cache for the context that points to tabulation symbol.
+   *
+   * @param context    processing context that points to tabulation symbol
+   */
+  private void onTabulation(@NotNull ProcessingContext context) {
+    onNonLineFeedSymbol(context);
+    CacheEntry cacheEntry = getCacheEntryForVisualLine(context.visualLine);
+    cacheEntry.storeTabData(context);
+  }
+
+  /**
+   * Process given context in assumption that it points to non-line feed symbol.
+   *
+   * @param context   context that is assumed to point to non-line feed symbol
+   */
+  private void onNonLineFeedSymbol(@NotNull ProcessingContext context) {
+    CacheEntry cacheEntry = getCacheEntryForVisualLine(context.visualLine);
+    Document document = myEditor.getDocument();
+    if (context.offset >= document.getTextLength() - 1) {
+      cacheEntry.setLineEndPosition(context);
+    }
+
+    if (context.offset < 0 || (context.offset > 0 && context.offset < document.getTextLength()
+                               && document.getCharsSequence().charAt(context.offset - 1) != '\n'))
+    {
+      return;
+    }
+
+    // Process first symbol on a new line.
+    cacheEntry.getTabData().clear();
+    cacheEntry.getFoldingData().clear();
+    cacheEntry.setLineStartPosition(context.clone(), context.visualLine);
+  }
+
+  private CacheEntry getCacheEntryForVisualLine(int visualLine) {
+    mySearchKey.visualLine = visualLine;
+    int i = Collections.binarySearch(myCache, mySearchKey);
+    CacheEntry result;
+    if (i < 0) {
+      i = -i - 1;
+      myCache.add(i, result = new CacheEntry(visualLine, myEditor, myRepresentationHelper, myCache));
+    }
+    else if (myBeforeChangeState.valid && i > myBeforeChangeState.endCacheEntryIndex && myCache.get(i).locked) {
+      myCache.set(i, result = myCache.get(i).clone());
+    }
+    else {
+      result = myCache.get(i);
+    }
+    return result;
+  }
+
+  @Override
+  public void onRecalculationStart(int startOffset, int endOffset) {
+    myBeforeChangeState.updateByDocumentOffsets(startOffset, endOffset);
+    if (!myBeforeChangeState.valid) {
+      return;
+    }
+    myNotAffectedByUpdateTailCacheEntries.clear();
+    myNotAffectedByUpdateTailCacheEntries.addAll(myCache.subList(myBeforeChangeState.endCacheEntryIndex + 1, myCache.size()));
+    myCache.subList(myBeforeChangeState.startCacheEntryIndex + 1, myCache.size()).clear();
+    for (CacheEntry entry : myNotAffectedByUpdateTailCacheEntries) {
+      entry.locked = true;
+    }
+  }
+
+  @Override
+  public void onRecalculationEnd(int startOffset, int endOffset) {
+    if (!myBeforeChangeState.valid) {
+      return;
+    }
+    int endIndex = Math.max(0, myCache.size() - 2); // -1 because of zero-based indexing; one more -1 in assumption that
+                                                    // re-parsing always adds number of target cache entries plus one
+                                                    // (because of line feed at the end).
+    myAfterChangeState.updateByCacheIndices(myBeforeChangeState.startCacheEntryIndex, endIndex);
+    myCache.subList(myAfterChangeState.endCacheEntryIndex + 1, myCache.size()).clear();
+    myCache.addAll(myNotAffectedByUpdateTailCacheEntries);
+    for (CacheEntry entry : myNotAffectedByUpdateTailCacheEntries) {
+      entry.locked = false;
+    }
+    applyStateChange();
+
+
+    //Document document = myEditor.getDocument();
+    //CharSequence text = document.getCharsSequence();
+    //System.out.println("--------------------------------------------------");
+    //System.out.println("|");
+    //System.out.println("|");
+    //System.out.println(text);
+    //System.out.println("-  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -");
+    //System.out.println("text length: " + text.length() + ", soft wraps: " + myStorage.getSoftWraps());
+    //for (int i = 0; i < myCache.size(); i++) {
+    //  CacheEntry entry = myCache.get(i);
+    //  // TODO den unwrap
+    //  try {
+    //    System.out.printf("line %d. %d-%d: '%s'%n", i, entry.startOffset, entry.endOffset,
+    //                      text.subSequence(entry.startOffset,Math.min(entry.endOffset, text.length())));
+    //  }
+    //  catch (Throwable e) {
+    //    e.printStackTrace();
+    //  }
+    //}
+    //if (!myCache.isEmpty() && myCache.get(myCache.size() - 1).endOffset < text.length() - 1) {
+    //  System.out.printf("Incomplete re-parsing detected! Document length is %d but last processed offset is %s%n", text.length(),
+    //                    myCache.get(myCache.size() - 1).endOffset);
+    //}
+
+
+    //for (CacheEntry cacheEntry : myCache) {
+    //  if (cacheEntry.startOffset > 0) {
+    //    if (text.charAt(cacheEntry.startOffset - 1) != '\n' && myStorage.getSoftWrap(cacheEntry.startOffset) == null) {
+    //      assert false;
+    //    }
+    //  }
+    //  if (cacheEntry.endOffset < document.getTextLength()) {
+    //    if (text.charAt(cacheEntry.endOffset) != '\n' && myStorage.getSoftWrap(cacheEntry.endOffset) == null) {
+    //      assert false;
+    //    }
+    //  }
+    //}
+  }
+
+  /**
+   * Is assumed to be called for updating {@link #myCache document dimensions cache} entries that lay after document position identified
+   * by {@link #myAfterChangeState} in order to apply to them diff between {@link #myBeforeChangeState} and {@link #myAfterChangeState}.
+   * <p/>
+   * I.e. the general idea of incremental cache update is the following:
+   * <p/>
+   * <pre>
+   * <ol>
+   *   <li>We are notified on document region update;</li>
+   *   <li>We remember significant information about target region;</li>
+   *   <li>Region data recalculation is performed;</li>
+   *   <li>Diff between current region state and remembered one is applied to the cache;</li>
+   * </ol>
+   </pre>
+   */
+  private void applyStateChange() {
+    int visualLinesDiff = myAfterChangeState.visualLines - myBeforeChangeState.visualLines;
+    int logicalLinesDiff = myAfterChangeState.logicalLines - myBeforeChangeState.logicalLines;
+    int softWrappedLinesDiff = myAfterChangeState.softWrapLines - myBeforeChangeState.softWrapLines;
+    int foldedLinesDiff = myAfterChangeState.foldedLines - myBeforeChangeState.foldedLines;
+    int offsetsDiff = (myAfterChangeState.endOffset - myAfterChangeState.startOffset)
+                      - (myBeforeChangeState.endOffset - myBeforeChangeState.startOffset);
+
+    if (myNotAffectedByUpdateTailCacheEntries.isEmpty()) {
+      return;
+    }
+
+    for (CacheEntry cacheEntry : myNotAffectedByUpdateTailCacheEntries) {
+      cacheEntry.visualLine += visualLinesDiff;
+      cacheEntry.startLogicalLine += logicalLinesDiff;
+      cacheEntry.endLogicalLine += logicalLinesDiff;
+      cacheEntry.advance(offsetsDiff);
+      cacheEntry.startSoftWrapLinesBefore += softWrappedLinesDiff;
+      cacheEntry.endSoftWrapLinesBefore += softWrappedLinesDiff;
+      cacheEntry.startFoldedLines += foldedLinesDiff;
+      cacheEntry.endFoldedLines += foldedLinesDiff;
+    }
+
+    int offset = myNotAffectedByUpdateTailCacheEntries.get(0).startOffset + 1/* in order to exclude soft wrap from previous line if any*/;
+    if (offsetsDiff == 0 || offset >= myEditor.getDocument().getTextLength()) {
+      return;
+    }
+
+    int softWrapIndex = myStorage.getSoftWrapIndex(offset);
+    if (softWrapIndex < 0) {
+      softWrapIndex = -softWrapIndex - 1;
+    }
+
+    List<SoftWrapImpl> softWraps = myStorage.getSoftWraps();
+    for (int i = softWrapIndex; i < softWraps.size(); i++) {
+      softWraps.get(i).advance(offsetsDiff);
+    }
+  }
+
+  private class CacheState {
+
+    public boolean valid = true;
+
+    public int visualLines;
+    public int logicalLines;
+    public int softWrapLines;
+    public int foldedLines;
+    public int startOffset;
+    public int endOffset;
+    public int startCacheEntryIndex;
+    public int endCacheEntryIndex;
+
+    public void updateByDocumentOffsets(int startOffset, int endOffset) {
+      this.startOffset = startOffset;
+      this.endOffset = endOffset;
+      startCacheEntryIndex = MappingUtil.getCacheEntryIndexForOffset(startOffset, myEditor.getDocument(), myCache);
+      endCacheEntryIndex = MappingUtil.getCacheEntryIndexForOffset(endOffset, myEditor.getDocument(), myCache);
+
+      if (startCacheEntryIndex < 0 || endCacheEntryIndex < 0) {
+        valid = false;
+        // This is correct situation for, say, initial cache update.
+        return;
+      }
+      updateByCacheIndices(startCacheEntryIndex, endCacheEntryIndex);
+
+    }
+
+    public void updateByCacheIndices(int startIndex, int endIndex) {
+      reset();
+      startOffset = myCache.get(startIndex).startOffset;
+      endOffset = myCache.get(endIndex).endOffset;
+      startCacheEntryIndex = startIndex;
+      endCacheEntryIndex = endIndex;
+      visualLines = endIndex - startIndex + 1;
+
+      CacheEntry startEntry = myCache.get(startIndex);
+      CacheEntry endEntry = myCache.get(endIndex);
+      logicalLines = endEntry.endLogicalLine - startEntry.startLogicalLine + 1;
+      foldedLines = endEntry.endFoldedLines - startEntry.startFoldedLines;
+      softWrapLines = endEntry.endSoftWrapLinesBefore + endEntry.endSoftWrapLinesCurrent - startEntry.startSoftWrapLinesBefore
+                      - startEntry.startSoftWrapLinesCurrent;
+    }
+
+    public void reset() {
+      logicalLines = 0;
+      softWrapLines = 0;
+      foldedLines = 0;
+      endCacheEntryIndex = 0;
+      valid = true;
+    }
+  }
+}
\ No newline at end of file
diff --git a/platform/platform-impl/src/com/intellij/openapi/editor/impl/softwrap/mapping/CompositeDataProvider.java b/platform/platform-impl/src/com/intellij/openapi/editor/impl/softwrap/mapping/CompositeDataProvider.java
new file mode 100644 (file)
index 0000000..2f4a5df
--- /dev/null
@@ -0,0 +1,202 @@
+/*
+ * 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.softwrap.mapping;
+
+import com.intellij.openapi.editor.impl.softwrap.mapping.DataProvider;
+import com.intellij.openapi.util.Pair;
+import org.jetbrains.annotations.NotNull;
+
+import java.util.Arrays;
+
+/**
+ * There is an ubiquitous situation when single source has various different sub-sources. E.g. there is a document
+ * and its soft wraps, fold regions etc.
+ * <p/>
+ * So, it's often convenient to encapsulate those sources in order to provide every sub-source instance one-by-one
+ * in particular order. This class does exactly that job, i.e. it encapsulates multiple {@link DataProvider} instance
+ * and exposes their data by {@link DataProvider#getSortingKey() sorting order}.
+ * <p/>
+ * Not thread-safe.
+ *
+ * @author Denis Zhdanov
+ * @since Aug 31, 2010 10:57:44 AM
+ */
+public class CompositeDataProvider {
+
+  /**
+   * We use array here for performance reasons (on the basis of profiling results analysis).
+   */
+  private final DataProvider<? extends Comparable<?>, ?>[] myProviders;
+
+  /**
+   * Creates new <code>CompositeDataProvider</code> object with the given providers to register.
+   *
+   * @param providers   providers to register within the current composite provider
+   */
+  public CompositeDataProvider(@NotNull DataProvider<? extends Comparable<?>, ?> ... providers) {
+    // We assume here that given array ownership belongs to the current object now.
+    for (int i = 0; i < providers.length; i++) {
+      DataProvider<? extends Comparable<?>, ?> provider = providers[i];
+      if (provider.getData() == null) {
+        providers[i] = null;
+      }
+    }
+    myProviders = providers;
+    sort();
+  }
+
+  /**
+   * Allows to answer if there is any data at the current provider.
+   * <p/>
+   * I.e. it's safe to call {@link #getData()} if this method returns <code>true</code>.
+   *
+   * @return    <code>true</code> if there is a data at the current provider; <code>false</code> otherwise
+   */
+  public boolean hasData() {
+    for (DataProvider<? extends Comparable<?>, ?> provider : myProviders) {
+      if (provider != null) {
+        return true;
+      }
+    }
+    return false;
+  }
+
+  /**
+   * Asks current provider to ignore all data which {@link DataProvider#getSortingKey() sorting keys} are less than the given value.
+   *
+   * @param sortingKey    min sorting key to use
+   */
+  public void advance(int sortingKey) {
+    for (int i = 0; i < myProviders.length; i++) {
+      DataProvider<? extends Comparable<?>, ?> provider = myProviders[i];
+      if (provider == null) {
+        continue;
+      }
+      provider.advance(sortingKey);
+      if (provider.getData() == null) {
+        myProviders[i] = null;
+      }
+    }
+    sort();
+  }
+
+  /**
+   * Retrieves the data with the the smallest {@link DataProvider#getSortingKey() sorting key} from registered providers.
+   * <p/>
+   * <b>Note:</b> subsequent invocations of this method are expected to return the same value unless {@link #next()} is called.
+   *
+   * @param <K>   data key type
+   * @param <V>   data value type
+   * @return      <code>(key; value)</code> pair that is {@link DataProvider#getData() returned from data provider} with the
+   *              smallest {@link DataProvider#getSortingKey() sorting key}
+   * @throws IllegalStateException    if no data left at the current composite provider
+   * @see #hasData()
+   */
+  @SuppressWarnings({"unchecked"})
+  @NotNull
+  public <K, V> Pair<K, V> getData() throws IllegalStateException {
+    if (!hasData()) {
+      throw new IllegalStateException("No more data is available within the current provider.");
+    }
+    DataProvider<?, ?> provider = myProviders[0];
+    Pair<K, V> result = (Pair<K, V>)provider.getData();
+    if (result == null) {
+      throw new IllegalStateException(String.format("Programming error detected - registered data provider doesn't have data (%s). "
+                                                      + "Registered providers: %s", provider, Arrays.toString(myProviders)));
+    }
+    return result;
+  }
+
+  /**
+   * Allows to ask for the {@link DataProvider#getSortingKey() sorting key} of the {@link #getData() current data}.
+   * <p/>
+   * It was possible to add sorting key to the {@link #getData()} result but the idea was to avoid unnecessary boxing/un-boxing.
+   *
+   * @return    sorting key for the {@link #getData() current data} if any.
+   * @throws IllegalStateException    if there is no data within the current provider
+   */
+  public int getSortingKey() throws IllegalStateException {
+    if (!hasData()) {
+      throw new IllegalStateException("No more data is available within the current provider");
+    }
+
+    return myProviders[0].getSortingKey();
+  }
+
+  /**
+   * Asks current provider to drop the data {@link #getData() returned last time} and advance to the next data with the
+   * smallest {@link DataProvider#getSortingKey() sorting key}.
+   *
+   * @return      <code>true</code> if there is more data at the current provider ({@link #hasData()} returns <code>true</code>
+   *              and {@link #getData()} doesn't throw exception); <code>false</code> otherwise
+   */
+  public boolean next() {
+    if (!hasData()) {
+      return false;
+    }
+
+    DataProvider<?, ?> provider = myProviders[0];
+    if (!provider.next()) {
+      myProviders[0] = null;
+    }
+    if (hasData()) {
+      sort();
+      return true;
+    }
+    return false;
+  }
+
+  private void sort() {
+    if (myProviders.length <= 0) {
+      return;
+    }
+
+    // Profiling results analysis-implied optimization.
+    int i = 0;
+    DataProvider<? extends Comparable<?>, ?> dataProvider = myProviders[0];
+    for (int j = 1; j < myProviders.length; j++) {
+      DataProvider<? extends Comparable<?>, ?> candidate = myProviders[j];
+      if (candidate == null) {
+        continue;
+      }
+      if (dataProvider == null || compare(dataProvider, candidate) > 0) {
+        i = j;
+        dataProvider = candidate;
+      }
+    }
+
+    if (i > 0) {
+      myProviders[i] = myProviders[0];
+      myProviders[0] = dataProvider;
+    }
+  }
+
+  @SuppressWarnings({"unchecked"})
+  private static int compare(DataProvider<? extends Comparable<?>, ?> o1, DataProvider<? extends Comparable<?>, ?> o2) {
+    // We assume here that given data providers have data to expose.
+    int result = o1.getSortingKey() - o2.getSortingKey();
+    if (result != 0) {
+      return result;
+    }
+
+    Pair<? extends Comparable<Object>, ?> d1 = (Pair<? extends Comparable<Object>, ?>)o1.getData();
+    Pair<? extends Comparable<Object>, ?> d2 = (Pair<? extends Comparable<Object>, ?>)o2.getData();
+    if (d1 != null && d2 != null) {
+      return d1.first.compareTo(d2.first);
+    }
+    return result;
+  }
+}
diff --git a/platform/platform-impl/src/com/intellij/openapi/editor/impl/softwrap/mapping/DataProvider.java b/platform/platform-impl/src/com/intellij/openapi/editor/impl/softwrap/mapping/DataProvider.java
new file mode 100644 (file)
index 0000000..726a2c0
--- /dev/null
@@ -0,0 +1,61 @@
+/*
+ * 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.softwrap.mapping;
+
+import com.intellij.openapi.util.Pair;
+import org.jetbrains.annotations.Nullable;
+
+import java.util.Iterator;
+
+/**
+ * Defines contract for providing data sorted by int values.
+ * <p/>
+ * Expected usage is very similar to the {@link Iterator}'s one - the same {@link #getData() current data} and
+ * {@link #getSortingKey() sorting key} are exposed until {@link #next()} is called.
+ * <p/>
+ * Implementations of this interface are not obliged to be thread-safe.
+ *
+ * @author Denis Zhdanov
+ * @since Aug 31, 2010 10:56:14 AM
+ */
+public interface DataProvider<K extends Comparable<? super K>, V> {
+
+  /**
+   * @return    current data if any is available; <code>null</code> otherwise
+   */
+  @Nullable
+  Pair<K, V> getData();
+
+  /**
+   * @return    sorting key for the {@link #getData() current data}
+   */
+  int getSortingKey();
+
+  /**
+   * Asks current data provider to advance to the next data if any.
+   *
+   * @return    flag that shows if current provider has more data, i.e. {@link #getData()} is guaranteed to return
+   *            not-<code>null</code> value if this method returns <code>true</code>
+   */
+  boolean next();
+
+  /**
+   * Asks current provider to ignore all data which {@link DataProvider#getSortingKey() sorting keys} are less than the given value.
+   *
+   * @param sortingKey    min sorting key to use
+   */
+  void advance(int sortingKey);
+}
diff --git a/platform/platform-impl/src/com/intellij/openapi/editor/impl/softwrap/mapping/FoldingData.java b/platform/platform-impl/src/com/intellij/openapi/editor/impl/softwrap/mapping/FoldingData.java
new file mode 100644 (file)
index 0000000..821958c
--- /dev/null
@@ -0,0 +1,51 @@
+/*
+ * 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.softwrap.mapping;
+
+import com.intellij.openapi.editor.Document;
+import com.intellij.openapi.editor.Editor;
+import com.intellij.openapi.editor.FoldRegion;
+import com.intellij.openapi.editor.impl.EditorTextRepresentationHelper;
+
+/**
+ * Caches information about number of logical columns inside the collapsed single line folding.
+ */
+class FoldingData {
+
+  int widthInColumns = -1;
+  int startX;
+  private final Editor myEditor;
+  private final EditorTextRepresentationHelper myRepresentationHelper;
+  private final FoldRegion myFoldRegion;
+
+  FoldingData(FoldRegion foldRegion, int startX, EditorTextRepresentationHelper representationHelper, Editor editor) {
+    myFoldRegion = foldRegion;
+    this.startX = startX;
+    myRepresentationHelper = representationHelper;
+    myEditor = editor;
+  }
+
+  public int getCollapsedSymbolsWidthInColumns() {
+    if (widthInColumns < 0) {
+      Document document = myEditor.getDocument();
+      widthInColumns = myRepresentationHelper.toVisualColumnSymbolsNumber(
+        document.getCharsSequence(), myFoldRegion.getStartOffset(), myFoldRegion.getEndOffset(), startX
+      );
+    }
+
+    return widthInColumns;
+  }
+}
diff --git a/platform/platform-impl/src/com/intellij/openapi/editor/impl/softwrap/mapping/FoldingDataProvider.java b/platform/platform-impl/src/com/intellij/openapi/editor/impl/softwrap/mapping/FoldingDataProvider.java
new file mode 100644 (file)
index 0000000..ab424f9
--- /dev/null
@@ -0,0 +1,100 @@
+/*
+ * 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.softwrap.mapping;
+
+import com.intellij.openapi.editor.FoldRegion;
+import org.jetbrains.annotations.Nullable;
+
+/**
+ * Adapts information about fold regions received as an array to {@link DataProvider} API.
+ *
+ * @author Denis Zhdanov
+ * @since Aug 31, 2010 12:42:02 PM
+ */
+public class FoldingDataProvider extends AbstractDataProvider<SoftWrapDataProviderKeys, FoldRegion> {
+
+  private final FoldRegion[] myFoldRegions;
+  private int myIndex;
+
+  public FoldingDataProvider(@Nullable FoldRegion[] foldRegions) {
+    super(SoftWrapDataProviderKeys.COLLAPSED_FOLDING);
+    myFoldRegions = foldRegions;
+
+    if (foldRegions == null) {
+      return;
+    }
+    for (; myIndex < myFoldRegions.length; myIndex++) {
+      if (!myFoldRegions[myIndex].isExpanded()) {
+        break;
+      }
+    }
+  }
+
+  @Override
+  protected FoldRegion doGetData() {
+    if (myFoldRegions == null || myIndex >= myFoldRegions.length) {
+      return null;
+    }
+    return myFoldRegions[myIndex];
+  }
+
+  @Override
+  public boolean next() {
+    if (myFoldRegions == null) {
+      return false;
+    }
+
+    while (++myIndex < myFoldRegions.length) {
+      if (!myFoldRegions[myIndex].isExpanded()) {
+        return true;
+      }
+    }
+    return false;
+  }
+
+  @Override
+  public void advance(int sortingKey) {
+    // We inline binary search here because profiling indicates that as a performance boost.
+    int start = myIndex;
+    int end = myFoldRegions.length - 1;
+
+    // We inline binary search here because profiling indicates that it becomes bottleneck to use Collections.binarySearch().
+    while (start <= end) {
+      int i = (end + start) >>> 1;
+      FoldRegion foldRegion = myFoldRegions[i];
+      if (foldRegion.getStartOffset() < sortingKey) {
+        start = i + 1;
+        continue;
+      }
+      if (foldRegion.getStartOffset() > sortingKey) {
+        end = i - 1;
+        continue;
+      }
+
+      myIndex = i;
+      break;
+    }
+    myIndex = start;
+  }
+
+  @Override
+  public int getSortingKey() {
+    if (myFoldRegions == null || myIndex >= myFoldRegions.length) {
+      return 0;
+    }
+    return myFoldRegions[myIndex].getStartOffset();
+  }
+}
diff --git a/platform/platform-impl/src/com/intellij/openapi/editor/impl/softwrap/mapping/LogicalToVisualMappingStrategy.java b/platform/platform-impl/src/com/intellij/openapi/editor/impl/softwrap/mapping/LogicalToVisualMappingStrategy.java
new file mode 100644 (file)
index 0000000..ea35c09
--- /dev/null
@@ -0,0 +1,172 @@
+/*
+ * 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.softwrap.mapping;
+
+import com.intellij.openapi.editor.*;
+import com.intellij.openapi.editor.impl.EditorTextRepresentationHelper;
+import com.intellij.openapi.editor.impl.softwrap.SoftWrapsStorage;
+import com.intellij.openapi.util.Computable;
+import com.intellij.openapi.util.Pair;
+import org.jetbrains.annotations.NotNull;
+
+import java.util.List;
+
+/**
+* @author Denis Zhdanov
+* @since Sep 9, 2010 10:08:08 AM
+*/
+@SuppressWarnings({"UnusedDeclaration"})
+class LogicalToVisualMappingStrategy extends AbstractMappingStrategy<VisualPosition> {
+
+  private final LogicalPosition myTargetLogical;
+
+  LogicalToVisualMappingStrategy(@NotNull final LogicalPosition logical, Editor editor, final SoftWrapsStorage storage,
+                                 EditorTextRepresentationHelper representationHelper, final List<CacheEntry> cache)
+    throws IllegalStateException
+  {
+    super(new Computable<Pair<CacheEntry, VisualPosition>>() {
+      @Override
+      public Pair<CacheEntry, VisualPosition> compute() {
+        int start = 0;
+        int end = cache.size() - 1;
+
+        // We inline binary search here because profiling indicates that it becomes bottleneck to use Collections.binarySearch().
+        while (start <= end) {
+          int i = (end + start) >>> 1;
+          CacheEntry cacheEntry = cache.get(i);
+
+          // There is a possible case that single logical line is represented on multiple visual lines due to soft wraps processing.
+          // Hence, we check for bot logical line and logical columns during searching 'anchor' cache entry.
+
+          if (cacheEntry.endLogicalLine < logical.line
+              || (cacheEntry.endLogicalLine == logical.line && storage.getSoftWrap(cacheEntry.endOffset) != null
+                  && cacheEntry.endLogicalColumn <= logical.column))
+          {
+            start = i + 1;
+            continue;
+          }
+          if (cacheEntry.startLogicalLine > logical.line
+              || (cacheEntry.startLogicalLine == logical.line
+                  && cacheEntry.startLogicalColumn > logical.column))
+          {
+            end = i - 1;
+            continue;
+          }
+
+          // There is a possible case that currently found cache entry corresponds to soft-wrapped line and soft wrap occurred
+          // at target logical column. We need to return cache entry for the next visual line then (because single logical column
+          // is shared for 'before soft wrap' and 'after soft wrap' positions and we want to use the one that points to
+          // 'after soft wrap' position).
+          if (cacheEntry.endLogicalLine == logical.line && cacheEntry.endLogicalColumn == logical.column && i < cache.size() - 1) {
+            CacheEntry nextLineCacheEntry = cache.get(i + 1);
+            if (nextLineCacheEntry.startLogicalLine == logical.line
+                && nextLineCacheEntry.startLogicalColumn == logical.column)
+            {
+              return new Pair<CacheEntry, VisualPosition>(nextLineCacheEntry, null);
+            }
+          }
+          return new Pair<CacheEntry, VisualPosition>(cacheEntry, null);
+        }
+
+        throw new IllegalStateException(String.format(
+          "Can't map logical position (%s) to visual position. Reason: no cached information information about target visual "
+          + "line is found. Registered entries: %s", logical, cache
+        ));
+      }
+    }, editor, storage, representationHelper);
+    myTargetLogical = logical;
+  }
+
+  @Override
+  protected VisualPosition buildIfExceeds(ProcessingContext context, int offset) {
+    if (context.logicalLine < myTargetLogical.line) {
+       return null;
+    }
+
+    int diff = myTargetLogical.column - context.logicalColumn;
+    if (offset - context.offset < diff) {
+      return null;
+    }
+
+    context.visualColumn += diff;
+    // Don't update other dimensions like logical position and offset because we need only visual position here.
+    return context.buildVisualPosition();
+  }
+
+  @Override
+  protected VisualPosition buildIfExceeds(@NotNull ProcessingContext context, @NotNull FoldRegion foldRegion) {
+    int foldEndLine = myEditor.getDocument().getLineNumber(foldRegion.getEndOffset());
+    if (myTargetLogical.line > foldEndLine) {
+      return null;
+    }
+
+    if (myTargetLogical.line < foldEndLine) {
+      // Map all logical position that point inside collapsed fold region to visual position of its start.
+      return context.buildVisualPosition();
+    }
+
+    int foldEndColumn = getFoldRegionData(foldRegion).getCollapsedSymbolsWidthInColumns();
+    if (foldEndLine == context.logicalLine) {
+      // Single-line fold region.
+      foldEndColumn += context.logicalColumn;
+    }
+
+    if (foldEndColumn <= myTargetLogical.column) {
+      return null;
+    }
+
+    // Map all logical position that point inside collapsed fold region to visual position of its start.
+    return context.buildVisualPosition();
+  }
+
+  @Override
+  protected VisualPosition buildIfExceeds(ProcessingContext context, TabData tabData) {
+    if (context.logicalLine < myTargetLogical.line) {
+      return null;
+    }
+
+    int diff = myTargetLogical.column - context.logicalColumn;
+    if (diff >= tabData.widthInColumns) {
+      return null;
+    }
+
+    context.logicalColum