Package org.rstudio.studio.client.workbench.views.source.editors.text

Source Code of org.rstudio.studio.client.workbench.views.source.editors.text.AceEditor

/*
* AceEditor.java
*
* Copyright (C) 2009-12 by RStudio, Inc.
*
* Unless you have received this program directly from RStudio pursuant
* to the terms of a commercial license agreement with RStudio, then
* this program is licensed to you under the terms of version 3 of the
* GNU Affero General Public License. This program is distributed WITHOUT
* ANY EXPRESS OR IMPLIED WARRANTY, INCLUDING THOSE OF NON-INFRINGEMENT,
* MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE. Please refer to the
* AGPL (http://www.gnu.org/licenses/agpl-3.0.txt) for more details.
*
*/
package org.rstudio.studio.client.workbench.views.source.editors.text;

import com.google.gwt.core.client.JavaScriptObject;
import com.google.gwt.core.client.JsArray;
import com.google.gwt.core.client.Scheduler;
import com.google.gwt.core.client.Scheduler.RepeatingCommand;
import com.google.gwt.core.client.Scheduler.ScheduledCommand;
import com.google.gwt.dom.client.Document;
import com.google.gwt.dom.client.NativeEvent;
import com.google.gwt.dom.client.PreElement;
import com.google.gwt.dom.client.Style.Unit;
import com.google.gwt.event.dom.client.*;
import com.google.gwt.event.logical.shared.ValueChangeEvent;
import com.google.gwt.event.logical.shared.ValueChangeHandler;
import com.google.gwt.event.shared.GwtEvent;
import com.google.gwt.event.shared.HandlerManager;
import com.google.gwt.event.shared.HandlerRegistration;
import com.google.gwt.user.client.Command;
import com.google.gwt.user.client.ui.RootPanel;
import com.google.gwt.user.client.ui.ScrollPanel;
import com.google.gwt.user.client.ui.Widget;
import com.google.inject.Inject;

import org.rstudio.core.client.CommandWithArg;
import org.rstudio.core.client.ElementIds;
import org.rstudio.core.client.ExternalJavaScriptLoader;
import org.rstudio.core.client.ExternalJavaScriptLoader.Callback;
import org.rstudio.core.client.Rectangle;
import org.rstudio.core.client.StringUtil;
import org.rstudio.core.client.command.KeyboardShortcut;
import org.rstudio.core.client.dom.DomUtils;
import org.rstudio.core.client.dom.WindowEx;
import org.rstudio.core.client.regex.Match;
import org.rstudio.core.client.regex.Pattern;
import org.rstudio.core.client.widget.DynamicIFrame;
import org.rstudio.studio.client.RStudioGinjector;
import org.rstudio.studio.client.common.codetools.CodeToolsServerOperations;
import org.rstudio.studio.client.common.debugging.model.Breakpoint;
import org.rstudio.studio.client.common.filetypes.TextFileType;
import org.rstudio.studio.client.server.Void;
import org.rstudio.studio.client.workbench.model.ChangeTracker;
import org.rstudio.studio.client.workbench.model.EventBasedChangeTracker;
import org.rstudio.studio.client.workbench.views.console.shell.assist.CompletionManager;
import org.rstudio.studio.client.workbench.views.console.shell.assist.CompletionManager.InitCompletionFilter;
import org.rstudio.studio.client.workbench.views.console.shell.assist.CompletionPopupPanel;
import org.rstudio.studio.client.workbench.views.console.shell.assist.NullCompletionManager;
import org.rstudio.studio.client.workbench.views.console.shell.assist.RCompletionManager;
import org.rstudio.studio.client.workbench.views.console.shell.editor.InputEditorDisplay;
import org.rstudio.studio.client.workbench.views.console.shell.editor.InputEditorPosition;
import org.rstudio.studio.client.workbench.views.console.shell.editor.InputEditorSelection;
import org.rstudio.studio.client.workbench.views.console.shell.editor.InputEditorUtil;
import org.rstudio.studio.client.workbench.views.source.editors.text.ace.*;
import org.rstudio.studio.client.workbench.views.source.editors.text.ace.AceClickEvent.Handler;
import org.rstudio.studio.client.workbench.views.source.editors.text.ace.Mode.InsertChunkInfo;
import org.rstudio.studio.client.workbench.views.source.editors.text.ace.Renderer.ScreenCoordinates;
import org.rstudio.studio.client.workbench.views.source.editors.text.ace.spelling.CharClassifier;
import org.rstudio.studio.client.workbench.views.source.editors.text.ace.spelling.TokenPredicate;
import org.rstudio.studio.client.workbench.views.source.editors.text.ace.spelling.WordIterable;
import org.rstudio.studio.client.workbench.views.source.editors.text.cpp.CppCompletionContext;
import org.rstudio.studio.client.workbench.views.source.editors.text.cpp.CppCompletionManager;
import org.rstudio.studio.client.workbench.views.source.editors.text.events.*;
import org.rstudio.studio.client.workbench.views.source.events.RecordNavigationPositionEvent;
import org.rstudio.studio.client.workbench.views.source.events.RecordNavigationPositionHandler;
import org.rstudio.studio.client.workbench.views.source.model.RnwCompletionContext;
import org.rstudio.studio.client.workbench.views.source.model.SourcePosition;

public class AceEditor implements DocDisplay,
                                  InputEditorDisplay,
                                  NavigableSourceEditor
{
   public enum NewLineMode
   {
      Windows("windows"),
      Unix("unix"),
      Auto("auto");

      NewLineMode(String type)
      {
         this.type = type;
      }

      public String getType()
      {
         return type;
      }

      private String type;
   }

   private class Filter implements InitCompletionFilter
   {
      public boolean shouldComplete(NativeEvent event)
      {
         // Never complete if there's an active selection
         Range range = getSession().getSelection().getRange();
         if (!range.isEmpty())
            return false;
        
         // Don't consider Tab to be a completion if we're at the start of a
         // line (e.g. only zero or more whitespace characters between the
         // beginning of the line and the cursor)
        
         if (event != null && event.getKeyCode() != KeyCodes.KEY_TAB)
            return true;
        
         int col = range.getStart().getColumn();
         if (col == 0)
            return false;
        
         String line = getSession().getLine(range.getStart().getRow());
         return line.substring(0, col).trim().length() != 0;     

      }
   }

   private class AnchoredSelectionImpl implements AnchoredSelection
   {
      private AnchoredSelectionImpl(Anchor start, Anchor end)
      {
         start_ = start;
         end_ = end;
      }

      public String getValue()
      {
         return getSession().getTextRange(getRange());
      }

      public void apply()
      {
         getSession().getSelection().setSelectionRange(
               getRange());
      }

      public Range getRange()
      {
         return Range.fromPoints(start_.getPosition(), end_.getPosition());
      }

      public void detach()
      {
         start_.detach();
         end_.detach();
      }

      private final Anchor start_;
      private final Anchor end_;
   }

   private class AceEditorChangeTracker extends EventBasedChangeTracker<Void>
   {
      private AceEditorChangeTracker()
      {
         super(AceEditor.this);
         AceEditor.this.addFoldChangeHandler(new org.rstudio.studio.client.workbench.views.source.editors.text.events.FoldChangeEvent.Handler()
         {
            @Override
            public void onFoldChange(FoldChangeEvent event)
            {
               changed_ = true;
            }
         });
      }

      @Override
      public ChangeTracker fork()
      {
         AceEditorChangeTracker forked = new AceEditorChangeTracker();
         forked.changed_ = changed_;
         return forked;
      }
   }

   public static void preload()
   {
      load(null);
   }

   public static void load(final Command command)
   {
      aceLoader_.addCallback(new Callback()
      {
         public void onLoaded()
         {
            aceSupportLoader_.addCallback(new Callback()
            {
               public void onLoaded()
               {
                  if (command != null)
                     command.execute();
               }
            });
         }
      });
   }

   @Inject
   public AceEditor()
   {
      widget_ = new AceEditorWidget();
      ElementIds.assignElementId(widget_.getElement(),
                                 ElementIds.SOURCE_TEXT_EDITOR);

      completionManager_ = new NullCompletionManager();
      RStudioGinjector.INSTANCE.injectMembers(this);

      widget_.addValueChangeHandler(new ValueChangeHandler<Void>()
      {
         public void onValueChange(ValueChangeEvent<Void> evt)
         {
            ValueChangeEvent.fire(AceEditor.this, null);
         }
      });
      widget_.addFoldChangeHandler(new FoldChangeEvent.Handler()
      {
         @Override
         public void onFoldChange(FoldChangeEvent event)
         {
            AceEditor.this.fireEvent(new FoldChangeEvent());
         }
      });
     
      addCapturingKeyDownHandler(new KeyDownHandler()
      {
         @Override
         public void onKeyDown(KeyDownEvent event)
         {
            if (useVimMode_)
               return;
           
            int mod = KeyboardShortcut.getModifierValue(event.getNativeEvent());
            if (mod == KeyboardShortcut.CTRL)
            {
               switch (event.getNativeKeyCode())
               {
                  case 'U':
                     event.preventDefault();
                     InputEditorUtil.yankBeforeCursor(AceEditor.this, true);
                     break;
                  case 'K':
                     event.preventDefault();
                     InputEditorUtil.yankAfterCursor(AceEditor.this, true);
                     break;
                  case 'Y':
                     event.preventDefault();
                     Position start = getSelectionStart();
                     InputEditorUtil.pasteYanked(AceEditor.this);
                     indentPastedRange(Range.fromPoints(start,
                                                        getSelectionEnd()));
                     break;
               }
            }

         }
      });

      addPasteHandler(new PasteEvent.Handler()
      {
         @Override
         public void onPaste(PasteEvent event)
         {
            final Position start = getSelectionStart();

            Scheduler.get().scheduleDeferred(new ScheduledCommand()
            {
               @Override
               public void execute()
               {
                  Range range = Range.fromPoints(start, getSelectionEnd());
                  indentPastedRange(range);
               }
            });
         }
      });
     
      // handle click events
      addAceClickHandler(new AceClickEvent.Handler()
      {   
         @Override
         public void onClick(AceClickEvent event)
         {   
            fixVerticalOffsetBug();
            if (DomUtils.isCommandClick(event.getNativeEvent()))
            {    
               // eat the event so ace doesn't do anything with it
               event.preventDefault();
               event.stopPropagation();
              
               // set the cursor position
               setCursorPosition(event.getDocumentPosition());
              
               // go to function definition
               fireEvent(new CommandClickEvent());
            }
            else
            {
               // if the focus in the Help pane or another iframe
               // we need to make sure to get it back
               WindowEx.get().focus();
            }
         }
      });

      addCursorChangedHandler(new CursorChangedHandler()
      {
         @Override
         public void onCursorChanged(CursorChangedEvent event)
         {
            fixVerticalOffsetBug();
            clearLineHighlight();
         }
      });
     
      addValueChangeHandler(new ValueChangeHandler<Void>()
      {
         @Override
         public void onValueChange(ValueChangeEvent<Void> event)
         {
            clearDebugLineHighlight();
         }
      });
   }

   private void indentPastedRange(Range range)
   {
      if (fileType_ == null ||
          !fileType_.canAutoIndent() ||
          !RStudioGinjector.INSTANCE.getUIPrefs().reindentOnPaste().getValue())
      {
         return;
      }

      String firstLinePrefix = getSession().getTextRange(
            Range.fromPoints(Position.create(range.getStart().getRow(), 0),
                             range.getStart()));

      if (firstLinePrefix.trim().length() != 0)
      {
         Position newStart = Position.create(range.getStart().getRow() + 1, 0);
         if (newStart.compareTo(range.getEnd()) >= 0)
            return;

         range = Range.fromPoints(newStart, range.getEnd());
      }

      getSession().reindent(range);
   }

   @Inject
   void initialize(CodeToolsServerOperations server)
   {
      server_ = server; 
   }

   public TextFileType getFileType()
   {
      return fileType_;
   }

   public void setFileType(TextFileType fileType)
   {
      setFileType(fileType, false);
   }

   public void setFileType(TextFileType fileType, boolean suppressCompletion)
   {
      fileType_ = fileType;
      updateLanguage(suppressCompletion);
   }
  
   public void setFileType(TextFileType fileType,
                           CompletionManager completionManager)
   {
      fileType_ = fileType;
      updateLanguage(completionManager);
   }

   @Override
   public void setRnwCompletionContext(RnwCompletionContext rnwContext)
   {
      rnwContext_ = rnwContext;
   }
  
   @Override
   public void setCppCompletionContext(CppCompletionContext cppContext)
   {
      cppContext_ = cppContext;
   }
  
   @Override
   public void setRCompletionContext(RCompletionContext rContext)
   {
      rContext_ = rContext;
   }

   private void updateLanguage(boolean suppressCompletion)
   {
      if (fileType_ == null)
         return;

      CompletionManager completionManager;
      if (!suppressCompletion)
      {
         if (fileType_.getEditorLanguage().useRCompletion())
         {
            completionManager = new RCompletionManager(
                  this,
                  this,
                  new CompletionPopupPanel(),
                  server_,
                  new Filter(),
                  rContext_,
                  fileType_.canExecuteChunks() ? rnwContext_ : null,
                  this,
                  true);
           
            // if this is cpp then we use our own completion manager
            // that can optionally delegate to the R completion manager
            if (fileType_.isC() || fileType_.isRmd())
            {
               completionManager = new CppCompletionManager(
                                                     this,
                                                     new Filter(),
                                                     cppContext_,
                                                     completionManager);
            }
         }
         else
            completionManager = new NullCompletionManager();
      }
      else
         completionManager = new NullCompletionManager();
     
      updateLanguage(completionManager);
   }
  
   private void updateLanguage(CompletionManager completionManager)
   {
      if (fileType_ == null)
         return;
     
      completionManager_ = completionManager;
     
      updateKeyboardHandlers();
    
      getSession().setEditorMode(
            fileType_.getEditorLanguage().getParserName(),
            false);
      getSession().setUseWrapMode(fileType_.getWordWrap());
      syncWrapLimit();
   }  
  
   private void syncWrapLimit()
   {
      // bail if there is no filetype yet
      if (fileType_ == null)
         return;
     
      // We originally observed that large word-wrapped documents
      // would cause Chrome on Liunx to freeze (bug #3207), eventually
      // running of of memory. Running the profiler indicated that the
      // time was being spent inside wrap width calculations in Ace.
      // Looking at the Ace bug database there were other wrapping problems
      // that were solvable by changing the wrap mode from "free" to a
      // specific range. So, for Chrome on Linux we started syncing the
      // wrap limit to the user-specified margin width.
      //
      // Unfortunately, this caused another problem whereby the ace
      // horizontal scrollbar would show up over the top of the editor
      // and the console (bug #3428). We tried reverting the fix to
      // #3207 and sure enough this solved the horizontal scrollbar
      // problem _and_ no longer froze Chrome (so perhaps there was a
      // bug in Chrome).
      //
      // In the meantime we added user pref to soft wrap to the margin
      // column, essentially allowing users to opt-in to the behavior
      // we used to fix the bug. So the net is:
      //
      // (1) To fix the horizontal scrollbar problem we revereted
      //     the wrap mode behavior we added from Chrome (under the
      //     assumption that the issue has been fixed in Chrome)
      //
      // (2) We added another check for desktop mode (since we saw
      //     the problem in both Chrome and Safari) to prevent the
      //     application of the problematic wrap mode setting.
      //
      // Perhaps there is an ace issue here as well, so the next time
      // we sync to Ace tip we should see if we can bring back the
      // wrapping option for Chrome (note the repro for this
      // is having a soft-wrapping source document in the editor that
      // exceed the horizontal threshold)
     
      // NOTE: we no longer do this at all since we observed the
      // scollbar problem on desktop as well
   }
  
   private void updateKeyboardHandlers()
   {
      // create a keyboard previewer for our special hooks
      AceKeyboardPreviewer previewer = new AceKeyboardPreviewer(
                                                         completionManager_);
     
      // reset keyboard handlers
      widget_.getEditor().setKeyboardHandler(null);
     
      // if required add vim handlers (to main editor and our previewer)
      if (useVimMode_)
      {
         widget_.getEditor().addKeyboardHandler(KeyboardHandler.vim());
         previewer.addHandler(
            new AceVimCommandHandler(new CommandWithArg<Boolean>() {
               @Override
               public void execute(Boolean arg)
               {
                 fireEvent(new FindRequestedEvent(arg));
               }            
            }));
      }
     
      // add the previewer's handler
      widget_.getEditor().addKeyboardHandler(previewer.getKeyboardHandler());
   }

   public String getCode()
   {
      return getSession().getValue();
   }

   public void setCode(String code, boolean preserveCursorPosition)
   {
      // Calling setCode("", false) while the editor contains multiple lines of
      // content causes bug 2928: Flickering console when typing. Empirically,
      // first setting code to a single line of content and then clearing it,
      // seems to correct this problem.
      if (StringUtil.isNullOrEmpty(code))
         doSetCode(" ", preserveCursorPosition);

      doSetCode(code, preserveCursorPosition);
   }

   private void doSetCode(String code, boolean preserveCursorPosition)
   {
      // Filter out Escape characters that might have snuck in from an old
      // bug in 0.95. We can choose to remove this when 0.95 ships, hopefully
      // any documents that would be affected by this will be gone by then.
      code = code.replaceAll("\u001B", "");

      final AceEditorNative ed = widget_.getEditor();

      if (preserveCursorPosition)
      {
         final Position cursorPos;
         final int scrollTop, scrollLeft;

         cursorPos = ed.getSession().getSelection().getCursor();
         scrollTop = ed.getRenderer().getScrollTop();
         scrollLeft = ed.getRenderer().getScrollLeft();

         // Setting the value directly on the document prevents undo/redo
         // stack from being blown away
         widget_.getEditor().getSession().getDocument().setValue(code);

         ed.getSession().getSelection().moveCursorTo(cursorPos.getRow(),
                                                     cursorPos.getColumn(),
                                                     false);
         ed.getRenderer().scrollToY(scrollTop);
         ed.getRenderer().scrollToX(scrollLeft);
         Scheduler.get().scheduleDeferred(new ScheduledCommand()
         {
            @Override
            public void execute()
            {
               ed.getRenderer().scrollToY(scrollTop);
               ed.getRenderer().scrollToX(scrollLeft);
            }
         });
      }
      else
      {
         ed.getSession().setValue(code);
         ed.getSession().getSelection().moveCursorTo(0, 0, false);
      }
   }
  
   public int getScrollLeft()
   {
      return widget_.getEditor().getRenderer().getScrollLeft();
   }

   public void scrollToX(int x)
   {
      widget_.getEditor().getRenderer().scrollToX(x);
   }
  
   public int getScrollTop()
   {
      return widget_.getEditor().getRenderer().getScrollTop();
   }
  
   public void scrollToY(int y)
   {
      widget_.getEditor().getRenderer().scrollToY(y);
   }
  
   public void insertCode(String code)
   {
      insertCode(code, false);
   }
  
   public void insertCode(String code, boolean blockMode)
   {
      // TODO: implement block mode
      getSession().replace(
            getSession().getSelection().getRange(), code);
   }

   public String getCode(Position start, Position end)
   {
      return getSession().getTextRange(Range.fromPoints(start, end));
   }
  
  
   @Override
   public InputEditorSelection search(String needle,
                                      boolean backwards,
                                      boolean wrap,
                                      boolean caseSensitive,
                                      boolean wholeWord,
                                      Position start,
                                      Range range,
                                      boolean regexpMode)
   {
      Search search = Search.create(needle,
                                    backwards,
                                    wrap,
                                    caseSensitive,
                                    wholeWord,
                                    start,
                                    range,
                                    regexpMode);

      Range resultRange = search.find(getSession());
      if (resultRange != null)
      {
         return createSelection(resultRange.getStart(), resultRange.getEnd());
      }
      else
      {
         return null;
      }
   }
  
   @Override
   public void insertCode(InputEditorPosition position, String content)
   {
     getSession().insert(selectionToPosition(position), content);
   }

   @Override
   public String getCode(InputEditorSelection selection)
   {
      return getCode(((AceInputEditorPosition)selection.getStart()).getValue(),
                     ((AceInputEditorPosition)selection.getEnd()).getValue());
   }

   public void focus()
   {
      widget_.getEditor().focus();
   }
  
   public boolean isFocused()
   {
      return widget_.getEditor().isFocused();
   }
  
  
   public void codeCompletion()
   {
      completionManager_.codeCompletion();
   }
  
   public void goToHelp()
   {
      completionManager_.goToHelp();
   }
  
   public void goToFunctionDefinition()
   {
      completionManager_.goToFunctionDefinition();
   }

   class PrintIFrame extends DynamicIFrame
   {
      public PrintIFrame(String code, double fontSize)
      {
         code_ = code;
         fontSize_ = fontSize;

         getElement().getStyle().setPosition(com.google.gwt.dom.client.Style.Position.ABSOLUTE);
         getElement().getStyle().setLeft(-5000, Unit.PX);
      }

      @Override
      protected void onFrameLoaded()
      {
         Document doc = getDocument();
         PreElement pre = doc.createPreElement();
         pre.setInnerText(code_);
         pre.getStyle().setProperty("whiteSpace", "pre-wrap");
         pre.getStyle().setFontSize(fontSize_, Unit.PT);
         doc.getBody().appendChild(pre);

         getWindow().print();

         // Bug 1224: ace: print from source causes inability to reconnect
         // This was caused by the iframe being removed from the document too
         // quickly after the print job was sent. As a result, attempting to
         // navigate away from the page at any point afterwards would result
         // in the error "Document cannot change while printing or in Print
         // Preview". The only thing you could do is close the browser tab.
         // By inserting a 5-minute delay hopefully Firefox would be done with
         // whatever print related operations are important.
         Scheduler.get().scheduleFixedDelay(new RepeatingCommand()
         {
            public boolean execute()
            {
               PrintIFrame.this.removeFromParent();
               return false;
            }
         }, 1000 * 60 * 5);
      }

      private final String code_;
      private final double fontSize_;
   }

   public void print()
   {
      PrintIFrame printIFrame = new PrintIFrame(
            getCode(),
            RStudioGinjector.INSTANCE.getUIPrefs().fontSize().getValue());
      RootPanel.get().add(printIFrame);
   }

   public String getText()
   {
      return getSession().getLine(
            getSession().getSelection().getCursor().getRow());
   }

   public void setText(String string)
   {
      setCode(string, false);
      getSession().getSelection().moveCursorFileEnd();
   }

   public boolean hasSelection()
   {
      return true;
   }

   public InputEditorSelection getSelection()
   {
      Range selection = getSession().getSelection().getRange();
      return new InputEditorSelection(
            new AceInputEditorPosition(getSession(), selection.getStart()),
            new AceInputEditorPosition(getSession(), selection.getEnd()));

   }

   public String getSelectionValue()
   {
      return getSession().getTextRange(
            getSession().getSelection().getRange());
   }

   public Position getSelectionStart()
   {
      return getSession().getSelection().getRange().getStart();
   }

   public Position getSelectionEnd()
   {
      return getSession().getSelection().getRange().getEnd();
   }

   @Override
   public Range getSelectionRange()
   {
      return Range.fromPoints(getSelectionStart(), getSelectionEnd());
   }

   @Override
   public void setSelectionRange(Range range)
   {
      getSession().getSelection().setSelectionRange(range);
   }

   public int getLength(int row)
   {
      return getSession().getDocument().getLine(row).length();
   }

   public int getRowCount()
   {
      return getSession().getDocument().getLength();
   }
  
   public String getLine(int row)
   {
      return getSession().getLine(row);
   }

   @Override
   public InputEditorSelection createSelection(Position pos1, Position pos2)
   {
      return new InputEditorSelection(
            new AceInputEditorPosition(getSession(), pos1),
            new AceInputEditorPosition(getSession(), pos2));
   }
  
   @Override
   public Position selectionToPosition(InputEditorPosition pos)
   {
      // HACK: This cast is gross, InputEditorPosition should just become
      // AceInputEditorPosition
      return Position.create((Integer) pos.getLine(), pos.getPosition());
   }
  
   @Override
   public InputEditorPosition createInputEditorPosition(Position pos)
   {
      return new AceInputEditorPosition(getSession(), pos);
   }

   @Override
   public Iterable<Range> getWords(TokenPredicate tokenPredicate,
                                   CharClassifier charClassifier,
                                   Position start,
                                   Position end)
   {
      return new WordIterable(getSession(),
                              tokenPredicate,
                              charClassifier,
                              start,
                              end);
   }

   @Override
   public String getTextForRange(Range range)
   {
      return getSession().getTextRange(range);
   }

   @Override
   public Anchor createAnchor(Position pos)
   {
      return Anchor.createAnchor(getSession().getDocument(),
                                 pos.getRow(),
                                 pos.getColumn());
   }

   private void fixVerticalOffsetBug()
   {
      widget_.getEditor().getRenderer().fixVerticalOffsetBug();
   }

   @Override
   public String debug_getDocumentDump()
   {
      return widget_.getEditor().getSession().getDocument().getDocumentDump();
   }

   @Override
   public void debug_setSessionValueDirectly(String s)
   {
      widget_.getEditor().getSession().setValue(s);
   }

   public void setSelection(InputEditorSelection selection)
   {
      AceInputEditorPosition start = (AceInputEditorPosition)selection.getStart();
      AceInputEditorPosition end = (AceInputEditorPosition)selection.getEnd();
      getSession().getSelection().setSelectionRange(Range.fromPoints(
            start.getValue(), end.getValue()));
   }

   public Rectangle getCursorBounds()
   {
      Range range = getSession().getSelection().getRange();
      Renderer renderer = widget_.getEditor().getRenderer();
      ScreenCoordinates start = renderer.textToScreenCoordinates(
                  range.getStart().getRow(),
                  range.getStart().getColumn());
      ScreenCoordinates end = renderer.textToScreenCoordinates(
                  range.getEnd().getRow(),
                  range.getEnd().getColumn());
      return new Rectangle(start.getPageX(),
                           start.getPageY(),
                           end.getPageX() - start.getPageX(),
                           renderer.getLineHeight());
   }

   public Rectangle getPositionBounds(InputEditorPosition position)
   {
      Renderer renderer = widget_.getEditor().getRenderer();

      Position pos = ((AceInputEditorPosition) position).getValue();

      ScreenCoordinates start = renderer.textToScreenCoordinates(
            pos.getRow(),
            pos.getColumn());

      return new Rectangle(start.getPageX(), start.getPageY(),
                           (int) Math.round(renderer.getCharacterWidth()),
                           (int) (renderer.getLineHeight() * 0.8));
   }

   public Rectangle getBounds()
   {
      return new Rectangle(
            widget_.getAbsoluteLeft(),
            widget_.getAbsoluteTop(),
            widget_.getOffsetWidth(),
            widget_.getOffsetHeight());
   }

   public void setFocus(boolean focused)
   {
      if (focused)
         widget_.getEditor().focus();
      else
         widget_.getEditor().blur();
   }

   public String replaceSelection(String value, boolean collapseSelection)
   {
      Selection selection = getSession().getSelection();
      String oldValue = getSession().getTextRange(selection.getRange());

      replaceSelection(value);

      if (collapseSelection)
      {
         collapseSelection(false);
      }

      return oldValue;
   }

   public boolean isSelectionCollapsed()
   {
      return getSession().getSelection().isEmpty();
   }

   public boolean isCursorAtEnd()
   {
      int lastRow = getRowCount() - 1;
      Position cursorPos = getCursorPosition();
      return cursorPos.compareTo(Position.create(lastRow,
                                                 getLength(lastRow))) == 0;
   }
  
   public void clear()
   {
      setCode("", false);
   }

   public void collapseSelection(boolean collapseToStart)
   {
      Selection selection = getSession().getSelection();
      Range rng = selection.getRange();
      Position pos = collapseToStart ? rng.getStart() : rng.getEnd();
      selection.setSelectionRange(Range.fromPoints(pos, pos));
   }

   public InputEditorSelection getStart()
   {
      return new InputEditorSelection(
            new AceInputEditorPosition(getSession(), Position.create(0, 0)));
   }

   public InputEditorSelection getEnd()
   {
      EditSession session = getSession();
      int rows = session.getLength();
      Position end = Position.create(rows, session.getLine(rows).length());
      return new InputEditorSelection(new AceInputEditorPosition(session, end));
   }

   public String getCurrentLine()
   {
      int row = getSession().getSelection().getRange().getStart().getRow();
      return getSession().getLine(row);
   }

   public int getCurrentLineNum()
   {
      Position pos = getCursorPosition();
      return getSession().documentToScreenRow(pos);
   }

   public int getCurrentLineCount()
   {
      return getSession().getScreenLength();
   }

   @Override
   public String getLanguageMode(Position position)
   {
      return getSession().getMode().getLanguageMode(position);
   }

   public void replaceCode(String code)
   {
      int endRow, endCol;

      endRow = getSession().getLength() - 1;
      if (endRow < 0)
      {
         endRow = 0;
         endCol = 0;
      }
      else
      {
         endCol = getSession().getLine(endRow).length();
      }

      Range range = Range.fromPoints(Position.create(0, 0),
                                     Position.create(endRow, endCol));
      getSession().replace(range, code);
   }

   public void replaceSelection(String code)
   {
      Range selRange = getSession().getSelection().getRange();
      Position position = getSession().replace(selRange, code);
      Range range = Range.fromPoints(selRange.getStart(), position);
      getSession().getSelection().setSelectionRange(range);
   }

   public boolean moveSelectionToNextLine(boolean skipBlankLines)
   {
      int curRow = getSession().getSelection().getCursor().getRow();
      while (++curRow < getSession().getLength())
      {
         String line = getSession().getLine(curRow);
         Pattern pattern = Pattern.create("[^\\s]");
         Match match = pattern.match(line, 0);
         if (skipBlankLines && match == null)
            continue;
         int col =  (match != null) ? match.getIndex() : 0;
         getSession().getSelection().moveCursorTo(curRow, col, false);
         getSession().unfold(curRow, true);
         return true;
      }
      return false;
   }
  
   @Override
   public boolean moveSelectionToBlankLine()
   {
      int curRow = getSession().getSelection().getCursor().getRow();
     
      // if the current row is the last row then insert a new row
      if (curRow == (getSession().getLength() - 1))
      {
         int rowLen = getSession().getLine(curRow).length();
         getSession().getSelection().moveCursorTo(curRow, rowLen, false);
         insertCode("\n");
      }
     
      while (curRow < getSession().getLength())
      {
         String line = getSession().getLine(curRow);
         if (line.length() == 0)
         {
            getSession().getSelection().moveCursorTo(curRow, 0, false);
            getSession().unfold(curRow, true);
            return true;
         }
         
         curRow++;
      }
      return false;
   }

   @Override
   public void reindent()
   {
      boolean emptySelection = getSelection().isEmpty();
      getSession().reindent(getSession().getSelection().getRange());
      if (emptySelection)
         moveSelectionToNextLine(false);
   }
  
   @Override
   public void reindent(Range range)
   {
      getSession().reindent(range);
   }
  
   @Override
   public void toggleCommentLines()
   {
      widget_.getEditor().toggleCommentLines();
   }

   public ChangeTracker getChangeTracker()
   {
      return new AceEditorChangeTracker();
   }

   public AnchoredSelection createAnchoredSelection(Position startPos,
                                                    Position endPos)
   {
      Anchor start = Anchor.createAnchor(getSession().getDocument(),
                                         startPos.getRow(),
                                         startPos.getColumn());
      Anchor end = Anchor.createAnchor(getSession().getDocument(),
                                       endPos.getRow(),
                                       endPos.getColumn());
      return new AnchoredSelectionImpl(start, end);
   }

   public void fitSelectionToLines(boolean expand)
   {
      Range range = getSession().getSelection().getRange();
      Position start = range.getStart();
      Position newStart = start;

      if (start.getColumn() > 0)
      {
         if (expand)
         {
            newStart = Position.create(start.getRow(), 0);
         }
         else
         {
            String firstLine = getSession().getLine(start.getRow());
            if (firstLine.substring(0, start.getColumn()).trim().length() == 0)
               newStart = Position.create(start.getRow(), 0);
         }
      }

      Position end = range.getEnd();
      Position newEnd = end;
      if (expand)
      {
         int endRow = end.getRow();
         if (endRow == newStart.getRow() || end.getColumn() > 0)
         {
            // If selection ends at the start of a line, keep the selection
            // there--unless that means less than one line will be selected
            // in total.
            newEnd = Position.create(
                  endRow, getSession().getLine(endRow).length());
         }
      }
      else
      {
         while (newEnd.getRow() != newStart.getRow())
         {
            String line = getSession().getLine(newEnd.getRow());
            if (line.substring(0, newEnd.getColumn()).trim().length() != 0)
               break;

            int prevRow = newEnd.getRow() - 1;
            int len = getSession().getLine(prevRow).length();
            newEnd = Position.create(prevRow, len);
         }
      }

      getSession().getSelection().setSelectionRange(
            Range.fromPoints(newStart, newEnd));
   }

   public int getSelectionOffset(boolean start)
   {
      Range range = getSession().getSelection().getRange();
      if (start)
         return range.getStart().getColumn();
      else
         return range.getEnd().getColumn();
   }

   public void onActivate()
   {
      Scheduler.get().scheduleFinally(new RepeatingCommand()
      {
         public boolean execute()
         {
            widget_.onResize();
            widget_.onActivate();

            return false;
         }
      });
   }

   public void onVisibilityChanged(boolean visible)
   {
      if (visible)
         widget_.getEditor().getRenderer().updateFontSize();
   }

   public void setHighlightSelectedLine(boolean on)
   {
      widget_.getEditor().setHighlightActiveLine(on);
   }

   public void setHighlightSelectedWord(boolean on)
   {
      widget_.getEditor().setHighlightSelectedWord(on);
   }

   public void setShowLineNumbers(boolean on)
   {
      widget_.getEditor().getRenderer().setShowGutter(on);
   }

   public void setUseSoftTabs(boolean on)
   {
      getSession().setUseSoftTabs(on);
   }

   /**
    * Warning: This will be overridden whenever the file type is set
    */
   public void setUseWrapMode(boolean useWrapMode)
   {
      getSession().setUseWrapMode(useWrapMode);
   }

   public void setTabSize(int tabSize)
   {
      getSession().setTabSize(tabSize);
   }

   public void setShowInvisibles(boolean show)
   {
      widget_.getEditor().getRenderer().setShowInvisibles(show);
   }
  
   public void setShowIndentGuides(boolean show)
   {
      widget_.getEditor().getRenderer().setShowIndentGuides(show);
   }
  
   public void setBlinkingCursor(boolean blinking)
   {
      widget_.getEditor().getRenderer().setBlinkingCursor(blinking);
   }
  
   public void setShowPrintMargin(boolean on)
   {
      widget_.getEditor().getRenderer().setShowPrintMargin(on);
   }
  
   @Override
   public void setUseVimMode(boolean use)
   {
      // no-op if the editor is read-only (since vim mode doesn't
      // work for read-only ace instances)
      if (widget_.getEditor().getReadOnly())
         return;
     
      useVimMode_ = use;
      updateKeyboardHandlers();
   }

   public void setPadding(int padding)
   {
      widget_.getEditor().getRenderer().setPadding(padding);
   }

   public void setPrintMarginColumn(int column)
   {
      widget_.getEditor().getRenderer().setPrintMarginColumn(column);
      syncWrapLimit();
   }

   @Override
   public JsArray<AceFold> getFolds()
   {
      return getSession().getAllFolds();
   }

   @Override
   public void addFold(Range range)
   {
      getSession().addFold("...", range);
   }

   @Override
   public void addFoldFromRow(int row)
   {
      FoldingRules foldingRules = getSession().getMode().getFoldingRules();
      if (foldingRules == null)
         return;
      Range range = foldingRules.getFoldWidgetRange(getSession(),
                                                    "markbegin",
                                                    row);

      if (range != null)
         addFold(range);
   }

   @Override
   public void unfold(AceFold fold)
   {
      getSession().unfold(Range.fromPoints(fold.getStart(), fold.getEnd()),
                          false);
   }

   @Override
   public void unfold(int row)
   {
      getSession().unfold(row, false);
   }

   @Override
   public void unfold(Range range)
   {
      getSession().unfold(range, false);
   }

   public void setReadOnly(boolean readOnly)
   {
      widget_.getEditor().setReadOnly(readOnly);
   }

   public HandlerRegistration addCursorChangedHandler(final CursorChangedHandler handler)
   {
      return widget_.addCursorChangedHandler(handler);
   }
  
   public HandlerRegistration addEditorFocusHandler(FocusHandler handler)
   {
      return widget_.addFocusHandler(handler);
   }

   public Scope getCurrentScope()
   {
      return getSession().getMode().getCodeModel().getCurrentScope(
            getCursorPosition());
   }
  
   @Override
   public String getNextLineIndent()
   {
      EditSession session = getSession();
     
      Position cursorPosition = getCursorPosition();
      int row = cursorPosition.getRow();
      String state = getSession().getState(row);
     
      String line = getCurrentLine().substring(
            0, cursorPosition.getColumn());
      String tab = session.getTabString();
      int tabSize = session.getTabSize();
     
      return session.getMode().getNextLineIndent(
            state,
            line,
            tab,
            tabSize,
            row);
   }

   public Scope getCurrentChunk()
   {
      return getCurrentChunk(getCursorPosition());
   }

   @Override
   public Scope getCurrentChunk(Position position)
   {
      return getSession().getMode().getCodeModel().getCurrentChunk(position);
   }

   @Override
   public Scope getCurrentFunction()
   {
      return getFunctionAtPosition(getCursorPosition());
   }
  
   @Override
   public ScopeFunction getFunctionAtPosition(Position position)
   {
      return getSession().getMode().getCodeModel().getCurrentFunction(
            position);
   }

   @Override
   public Scope getCurrentSection()
   {
      return getSectionAtPosition(getCursorPosition());
   }

   @Override
   public Scope getSectionAtPosition(Position position)
   {
      return getSession().getMode().getCodeModel().getCurrentSection(position);
   }

   public Position getCursorPosition()
   {
      return getSession().getSelection().getCursor();
   }

   public void setCursorPosition(Position position)
   {
      getSession().getSelection().setSelectionRange(
            Range.fromPoints(position, position));
   }

   @Override
   public void moveCursorNearTop(int rowOffset)
   {
      int screenRow = getSession().documentToScreenRow(getCursorPosition());
      widget_.getEditor().scrollToRow(Math.max(0, screenRow - rowOffset));
   }
  
   @Override
   public void moveCursorNearTop()
   {
      moveCursorNearTop(7);
   }
  
   @Override
   public void ensureCursorVisible()
   {
      int screenRow = getSession().documentToScreenRow(getCursorPosition());    
      if (!widget_.getEditor().isRowFullyVisible(screenRow))
         moveCursorNearTop();
   }
  
   public void scrollToBottom()
   {
      SourcePosition pos = SourcePosition.create(getCurrentLineCount() - 1, 0);
      navigate(pos, false);
   }
  
   public void revealRange(Range range, boolean animate)
   {
      widget_.getEditor().revealRange(range, animate);
   }

   public boolean hasScopeTree()
   {
      return getSession().getMode().getCodeModel().hasScopes();
   }
  
   public JsArray<Scope> getScopeTree()
   {
      return getSession().getMode().getCodeModel().getScopeTree();
   }

   @Override
   public InsertChunkInfo getInsertChunkInfo()
   {
      return getSession().getMode().getInsertChunkInfo();
   }

   @Override
   public void foldAll()
   {
      getSession().foldAll();
   }

   @Override
   public void unfoldAll()
   {
      getSession().unfoldAll();
   }

   @Override
   public void toggleFold()
   {
      getSession().toggleFold();
   }

   @Override
   public void jumpToMatching()
   {
      widget_.getEditor().jumpToMatching();
   }
  
   @Override
   public SourcePosition findFunctionPositionFromCursor(String functionName)
   {
      Scope func =
         getSession().getMode().getCodeModel().findFunctionDefinitionFromUsage(
                                                      getCursorPosition(),
                                                      functionName);
      if (func != null)
      {
         Position position = func.getPreamble();
         return SourcePosition.create(position.getRow(), position.getColumn());
      }
      else
      {
         return null;
      }
   }
  
   @Override
   public void recordCurrentNavigationPosition()
   {
      fireRecordNavigationPosition(getCursorPosition());
   }
  
   @Override
   public void navigateToPosition(SourcePosition position,
                                  boolean recordCurrent)
   {
      navigateToPosition(position, recordCurrent, false);
   }
  
   @Override
   public void navigateToPosition(SourcePosition position,
                                  boolean recordCurrent,
                                  boolean highlightLine)
   {
      if (recordCurrent)
         recordCurrentNavigationPosition();

      navigate(position, true, highlightLine);
   }
  
   @Override
   public void restorePosition(SourcePosition position)
   {
      navigate(position, false);
   }
  
   @Override
   public boolean isAtSourceRow(SourcePosition position)
   {
      Position currPos = getCursorPosition();
      return currPos.getRow() == position.getRow();
   }
  
   @Override
   public void highlightDebugLocation(SourcePosition startPosition,
                                      SourcePosition endPosition,
                                      boolean executing)
   {     
      int firstRow = widget_.getEditor().getFirstVisibleRow();
      int lastRow = widget_.getEditor().getLastVisibleRow();
     
      // if the expression is large, let's just try to land in the middle
      int debugRow = (int) Math.floor(startPosition.getRow() + (
            endPosition.getRow() - startPosition.getRow())/2);
     
      // if the row at which the debugging occurs is inside a fold, unfold it
      getSession().unfold(debugRow, true);

      // if the line to be debugged is past or near the edges of the screen,
      // scroll it into view. allow some lines of context.
      if (debugRow <= (firstRow + DEBUG_CONTEXT_LINES) ||
          debugRow >= (lastRow - DEBUG_CONTEXT_LINES))
      {
         widget_.getEditor().scrollToLine(debugRow, true);
      }
      applyDebugLineHighlight(
            startPosition.asPosition(),
            endPosition.asPosition(),
            executing);
   }
  
   @Override
   public void endDebugHighlighting()
   {
      clearDebugLineHighlight();
   }
  
   @Override
   public HandlerRegistration addBreakpointSetHandler(
         BreakpointSetEvent.Handler handler)
   {
      return widget_.addBreakpointSetHandler(handler);
   }
  
   @Override
   public HandlerRegistration addBreakpointMoveHandler(
         BreakpointMoveEvent.Handler handler)
   {
      return widget_.addBreakpointMoveHandler(handler);
   }
  
   @Override
   public void addOrUpdateBreakpoint(Breakpoint breakpoint)
   {
      widget_.addOrUpdateBreakpoint(breakpoint);
   }
  
   @Override
   public void removeBreakpoint(Breakpoint breakpoint)
   {
      widget_.removeBreakpoint(breakpoint);
   }
  
   @Override
   public void toggleBreakpointAtCursor()
   {
      widget_.toggleBreakpointAtCursor();
   }
  
   @Override
   public void removeAllBreakpoints()
   {
      widget_.removeAllBreakpoints();
   }
  
   @Override
   public boolean hasBreakpoints()
   {
      return widget_.hasBreakpoints();
   }
  
   private void navigate(SourcePosition srcPosition, boolean addToHistory)
   {
      navigate(srcPosition, addToHistory, false);
   }
  
   private void navigate(SourcePosition srcPosition,
                         boolean addToHistory,
                         boolean highlightLine)
   { 
      // get existing cursor position
      Position previousCursorPos = getCursorPosition();
     
      // set cursor to function line
      Position position = Position.create(srcPosition.getRow(),
                                          srcPosition.getColumn());
      setCursorPosition(position);

      // skip whitespace if necessary
      if (srcPosition.getColumn() == 0)
      {
         int curRow = getSession().getSelection().getCursor().getRow();
         String line = getSession().getLine(curRow);
         int funStart = line.indexOf(line.trim());
         position = Position.create(curRow, funStart);
         setCursorPosition(position);
      }
     
      // scroll as necessary
      if (srcPosition.getScrollPosition() != -1)
         scrollToY(srcPosition.getScrollPosition());
      else if (position.getRow() != previousCursorPos.getRow())
         moveCursorNearTop();
     
      // set focus
      focus();
     
      if (highlightLine)
         applyLineHighlight(position.getRow());
     
      // add to navigation history if requested and our current mode
      // supports history navigation
      if (addToHistory)
         fireRecordNavigationPosition(position);
   }
  
   private void fireRecordNavigationPosition(Position pos)
   {
      SourcePosition srcPos = SourcePosition.create(pos.getRow(),
                                                    pos.getColumn());
      fireEvent(new RecordNavigationPositionEvent(srcPos));
   }
  
   @Override
   public HandlerRegistration addRecordNavigationPositionHandler(
                                    RecordNavigationPositionHandler handler)
   {
      return handlers_.addHandler(RecordNavigationPositionEvent.TYPE, handler);
   }

   @Override
   public HandlerRegistration addCommandClickHandler(
                                             CommandClickEvent.Handler handler)
   {
      return handlers_.addHandler(CommandClickEvent.TYPE, handler);
   }
  
   @Override
   public HandlerRegistration addFindRequestedHandler(
                                 FindRequestedEvent.Handler handler)
   {
      return handlers_.addHandler(FindRequestedEvent.TYPE, handler);
   }
  
   public void setFontSize(double size)
   {
      // No change needed--the AceEditorWidget uses the "normalSize" style
      // However, we do need to resize the gutter
      widget_.getEditor().getRenderer().updateFontSize();
      widget_.forceResize();
   }

   public HandlerRegistration addValueChangeHandler(
         ValueChangeHandler<Void> handler)
   {
      return handlers_.addHandler(ValueChangeEvent.getType(), handler);
   }

   public HandlerRegistration addFoldChangeHandler(
         FoldChangeEvent.Handler handler)
   {
      return handlers_.addHandler(FoldChangeEvent.TYPE, handler);
   }
  
   public HandlerRegistration addCapturingKeyDownHandler(KeyDownHandler handler)
   {
      return widget_.addCapturingKeyDownHandler(handler);
   }

   public HandlerRegistration addCapturingKeyPressHandler(KeyPressHandler handler)
   {
      return widget_.addCapturingKeyPressHandler(handler);
   }

   public HandlerRegistration addCapturingKeyUpHandler(KeyUpHandler handler)
   {
      return widget_.addCapturingKeyUpHandler(handler);
   }

   public HandlerRegistration addUndoRedoHandler(UndoRedoHandler handler)
   {
      return widget_.addUndoRedoHandler(handler);
   }

   public HandlerRegistration addPasteHandler(PasteEvent.Handler handler)
   {
      return widget_.addPasteHandler(handler);
   }

   public HandlerRegistration addAceClickHandler(Handler handler)
   {
      return widget_.addAceClickHandler(handler);
   }

   public JavaScriptObject getCleanStateToken()
   {
      return getSession().getUndoManager().peek();
   }

   public boolean checkCleanStateToken(JavaScriptObject token)
   {
      JavaScriptObject other = getSession().getUndoManager().peek();
      if (token == null ^ other == null)
         return false;
      return token == null || other.equals(token);
   }

   public void fireEvent(GwtEvent<?> event)
   {
      handlers_.fireEvent(event);
   }

   public Widget asWidget()
   {
      return widget_;
   }

   public EditSession getSession()
   {
      return widget_.getEditor().getSession();
   }

   public HandlerRegistration addBlurHandler(BlurHandler handler)
   {
      return widget_.addBlurHandler(handler);
   }

   public HandlerRegistration addClickHandler(ClickHandler handler)
   {
      return widget_.addClickHandler(handler);
   }

   public HandlerRegistration addFocusHandler(FocusHandler handler)
   {
      return widget_.addFocusHandler(handler);
   }

   public Widget getWidget()
   {
      return widget_;
   }

   public HandlerRegistration addKeyDownHandler(KeyDownHandler handler)
   {
      return widget_.addKeyDownHandler(handler);
   }

   public HandlerRegistration addKeyPressHandler(KeyPressHandler handler)
   {
      return widget_.addKeyPressHandler(handler);
   }

   public void autoHeight()
   {
      widget_.autoHeight();
   }

   public void forceCursorChange()
   {
      widget_.forceCursorChange();
   }

   public void scrollToCursor(ScrollPanel scrollPanel,
                              int paddingVert,
                              int paddingHoriz)
   {
      DomUtils.ensureVisibleVert(
            scrollPanel.getElement(),
            widget_.getEditor().getRenderer().getCursorElement(),
            paddingVert);
      DomUtils.ensureVisibleHoriz(
            scrollPanel.getElement(),
            widget_.getEditor().getRenderer().getCursorElement(),
            paddingHoriz, paddingHoriz,
            false);
   }

   public void forceImmediateRender()
   {
      widget_.getEditor().getRenderer().forceImmediateRender();
   }

   public void setNewLineMode(NewLineMode mode)
   {
      getSession().setNewLineMode(mode.getType());
   }

   public boolean isPasswordMode()
   {
      return passwordMode_;
   }

   public void setPasswordMode(boolean passwordMode)
   {
      passwordMode_ = passwordMode;
      widget_.getEditor().getRenderer().setPasswordMode(passwordMode);
   }

   public void setDisableOverwrite(boolean disableOverwrite)
   {
      getSession().setDisableOverwrite(disableOverwrite);
   }

   private Integer createLineHighlightMarker(int line, String style)
   {
      return createRangeHighlightMarker(Position.create(line, 0),
                                        Position.create(line+1, 0),
                                        style);
   }
  
   private Integer createRangeHighlightMarker(
         Position start,
         Position end,
         String style)
   {
      Range range = Range.fromPoints(start, end);
      return getSession().addMarker(range, style, "text", false);
   }
  

   private void applyLineHighlight(int line)
   {
      clearLineHighlight();
     
      if (!widget_.getEditor().getHighlightActiveLine())
      {
         lineHighlightMarkerId_ = createLineHighlightMarker(line,
                                                            "ace_find_line");
     
   }
  
   private void clearLineHighlight()
   {
      if (lineHighlightMarkerId_ != null)
      {
         getSession().removeMarker(lineHighlightMarkerId_);
         lineHighlightMarkerId_ = null;
      }
   }

   private void applyDebugLineHighlight(
         Position startPos,
         Position endPos,
         boolean executing)
   {
      clearDebugLineHighlight();
      lineDebugMarkerId_ = createRangeHighlightMarker(
            startPos, endPos,
            "ace_active_debug_line");
      if (executing)
      {
         executionLine_ = startPos.getRow();
         widget_.getEditor().getRenderer().addGutterDecoration(
               executionLine_,
               "ace_executing-line");
      }
   }

   private void clearDebugLineHighlight()
   {
      if (lineDebugMarkerId_ != null)
      {
         getSession().removeMarker(lineDebugMarkerId_);
         lineDebugMarkerId_ = null;
      }
      if (executionLine_ != null)
      {
         widget_.getEditor().getRenderer().removeGutterDecoration(
               executionLine_,
               "ace_executing-line");
         executionLine_ = null;
      }
   }

   private static final int DEBUG_CONTEXT_LINES = 2;
   private final HandlerManager handlers_ = new HandlerManager(this);
   private final AceEditorWidget widget_;
   private CompletionManager completionManager_;
   private CodeToolsServerOperations server_;
   private TextFileType fileType_;
   private boolean passwordMode_;
   private boolean useVimMode_ = false;
   private RnwCompletionContext rnwContext_;
   private CppCompletionContext cppContext_;
   private RCompletionContext rContext_;
   private Integer lineHighlightMarkerId_ = null;
   private Integer lineDebugMarkerId_ = null;
   private Integer executionLine_ = null;
   private static final ExternalJavaScriptLoader aceLoader_ =
         new ExternalJavaScriptLoader(AceResources.INSTANCE.acejs().getSafeUri().asString());
   private static final ExternalJavaScriptLoader aceSupportLoader_ =
         new ExternalJavaScriptLoader(AceResources.INSTANCE.acesupportjs().getSafeUri().asString());
}
TOP

Related Classes of org.rstudio.studio.client.workbench.views.source.editors.text.AceEditor

TOP
Copyright © 2018 www.massapi.com. All rights reserved.
All source code are property of their respective owners. Java is a trademark of Sun Microsystems, Inc and owned by ORACLE Inc. Contact coftware#gmail.com.