Source

sublime_plugins / scroll_offset.py

'''
Simulate vim's scroll offset functionality.

Vim has a very nice feature called 'scrolloff' which allows to define a minimal
number of lines to keep above and below the cursor when scrolling. It makes it
very convenient to work with files where there's already content below the
cursor.

Configurable file settings:
* scroll_offset_top: Minimum number of lines to keep above cursor.
* scroll_offset_top_treshold: When to move (1 = always, 3 = every 3 rows).
* scroll_offset_bottom: Minimum number of lines to keep below cursor.
* scroll_offset_bottom_treshold: When to move (1 = always, 3 = every 3 rows).

@author: Oktay Acikalin <ok@ryotic.de>

@license: MIT (http://www.opensource.org/licenses/mit-license.php)

@since: 2011-02-14
'''

import sublime
import sublime_plugin


DEFAULT_OFFSET_TOP = 5
DEFAULT_OFFSET_BOTTOM = 5
DEFAULT_OFFSET_TOP_TRESHOLD = 1
DEFAULT_OFFSET_BOTTOM_TRESHOLD = 1


class ScrollOffsetListener(sublime_plugin.EventListener):
    '''
    An event listener to keep the cursor from the top and bottom of the view.
    '''

    def on_selection_modified(self, view):
        '''
        Checks and eventually scrolls the view to keep the cursor in the middle
        of the view.

        @type  view: sublime.View
        @param view: View to work with.

        @return: None
        '''
        if getattr(self, 'paused', False):
            return
        
        get = view.settings().get
        offset_top = get('scroll_offset_top', DEFAULT_OFFSET_TOP)
        offset_top_treshold = get('scroll_offset_top_treshold',
                                  DEFAULT_OFFSET_TOP_TRESHOLD)
        offset_bottom = get('scroll_offset_bottom', DEFAULT_OFFSET_BOTTOM)
        offset_bottom_treshold = get('scroll_offset_bottom_treshold',
                                     DEFAULT_OFFSET_BOTTOM_TRESHOLD)

        file_region = view.size()
        file_begin = 0
        file_end = view.rowcol(file_region)[0]

        visible_region = view.visible_region()
        view_begin = view.rowcol(visible_region.begin())[0]
        view_end = view.rowcol(visible_region.end())[0]

        if view_end - view_begin <= offset_top + offset_bottom:
            # print 'view is too small.'
            return

        selected_region = view.sel()[0]
        selection_end = view.rowcol(selected_region.b)[0]

        diff_top = (selection_end - view_begin)
        diff_bottom = (view_end - selection_end)

        # print 'file:', file_begin, file_end
        # print 'view:', view_begin, view_end
        # print 'selection:', selection_end
        # print 'diff:', diff_top, diff_bottom

        if diff_top < offset_top:
            amount = offset_top - diff_top
            amount_is_enough = amount >= offset_top_treshold
            if view_begin - amount >= file_begin and amount_is_enough:
                # print 'move up:', amount
                if amount > 1 and selected_region.size() > 0:
                    self.paused = True
                    def _move_up():
                        view.run_command('scroll_lines', {'amount': 1})
                        self.paused = False
                    sublime.set_timeout(_move_up, 100)
                else:
                    view.run_command('scroll_lines', {'amount': amount})
        if diff_bottom < offset_bottom:
            amount = offset_bottom - diff_bottom
            amount_is_enough = amount >= offset_bottom_treshold
            if view_end + amount <= file_end + 1 and amount_is_enough:
                # print 'move down:', amount
                if amount > 1 and selected_region.size() > 0:
                    self.paused = True
                    def _move_down():
                        view.run_command('scroll_lines', {'amount': -1})
                        self.paused = False
                    sublime.set_timeout(_move_down, 100)
                else:
                    view.run_command('scroll_lines', {'amount': -amount})
Tip: Filter by directory path e.g. /media app.js to search for public/media/app.js.
Tip: Use camelCasing e.g. ProjME to search for ProjectModifiedEvent.java.
Tip: Filter by extension type e.g. /repo .js to search for all .js files in the /repo directory.
Tip: Separate your search with spaces e.g. /ssh pom.xml to search for src/ssh/pom.xml.
Tip: Use ↑ and ↓ arrow keys to navigate and return to view the file.
Tip: You can also navigate files with Ctrl+j (next) and Ctrl+k (previous) and view the file with Ctrl+o.
Tip: You can also navigate files with Alt+j (next) and Alt+k (previous) and view the file with Alt+o.