Sublime Forum

Starting a new plugin

#1

Hi,

After switching to Sublime last week I wanted to start developing my own plugins so here I am asking for some advice … Needs saying that I’m new to Sublime API and Python, so please be benevolent :wink:

I’m trying to develop a plugin that would allow user to go back to the last editing position in the current view (a feature I found very interesting in NetBeans) and using only the current line I got stuck here:

[code]import sublime_plugin

last_line = 0

def setLastLine(line):
last_line = line

def getLastLine():
return last_line

class LastEditPositionCommand(sublime_plugin.TextCommand):
def run_(self, action):
self.view.show(getLastLine())

class CaptureEditing(sublime_plugin.EventListener):
def on_modified(self, view):
sel = view.sel()[0]
setLastLine(view.line(sel))
[/code]

This is probably very wrong but if you could tell me what I did wrong would be very helpful for me.

Thanks

0 Likes

#2

Hello. I’m quite new to this as well. Part of the problem is that last_line is a number/integer. In this:

setLastLine(view.line(sel))

view.line(sel) is a Region. You could amend it to

setLastLine(sel.begin())

Use ‘print last_line’ at various points and then view the Console window (Ctrl-’ apostrophe) to see the results. The Console window will also display any error messages.

0 Likes

#3

The following seems to be working for me:

[code]import sublime, sublime_plugin

class LastEditLineCommand(sublime_plugin.TextCommand):
def run(self, edit):
self.view.show(CaptureEditing.last_line)

class CaptureEditing(sublime_plugin.EventListener):
last_line = 0
def on_modified(self, view):
sel = view.sel()[0]
CaptureEditing.last_line = sel.begin()[/code]
This makes ‘last_line’ an attribute of the class CaptureEditing, which the TextCommand can read by referring to the class: CaptureEditing.last_line

0 Likes

#4

Out of interest :wink: this version allows us to go back to previous edit positions:

[code]import sublime, sublime_plugin

class LastEditLineCommand(sublime_plugin.TextCommand):
def run(self, edit):
self.view.show(CaptureEditing.last_line.pop())

class CaptureEditing(sublime_plugin.EventListener):
last_line = ]
def on_modified(self, view):
sel = view.sel()[0]
CaptureEditing.last_line.append(sel.begin())
# print CaptureEditing.last_line[/code]
To explain, it maintains the editing position in a list and uses append() to keep adding to this list. It then uses pop() to extract the last position from the list.

I might play with this a little so that I can navigate back and forward.

0 Likes

#5

With apologies, but I’ve extended this example a bit, as it’s helping me to study Python:

It cycles through the last five edit positions;
It won’t cause an error if there isn’t a position to navigate to;
It only stores an edit position if it’s on a different line from the previous edit;
It only remembers the last 10 edit positions (so that the list doesn’t grow continuously).

It could be extended to go forward through edit positions as well…

[code]import sublime, sublime_plugin

class LastEditLineCommand(sublime_plugin.TextCommand):
posn = 0
def run(self, edit):
if len(CaptureEditing.last_line) > self.posn:
self.view.show(CaptureEditing.last_line-(self.posn+1)])
self.posn = (self.posn + 1) % 5
else: self.posn = 0

class CaptureEditing(sublime_plugin.EventListener):
last_line = ]
prev_line = -1
def on_modified(self, view):
sel = view.sel()[0]
curr_line, _ = view.rowcol(sel.begin())
CE = CaptureEditing
if CE.prev_line == -1 or curr_line != CE.prev_line:
CE.prev_line = curr_line
CE.last_line.append(sel.begin())
if len(CE.last_line) > 10: CE.last_line.pop(0)[/code]

0 Likes

#6

Wow! That is quite nice, I’ve got so much to learn. Thanks for the python lesson :smile:

You should publish that in github or something like that, a lot of people will find it useful.

0 Likes

#7

Hello. This is interesting in terms of learning Python, and the ST API. But on further investigation my example is of restricted use. Most significantly, because the last position(s) are stored in a class attribute (rather than an instance attribute), editing in two different tabs will cause mayhem :laughing:. I’m not sure how to get around this at the moment, although one way might be to store a reference to the current view with each edit position - but this sounds messy.

But many other discrepancies will arise: pressing Enter a few times, deleting text, copying, editing the same line again - all of these actions will make the code* less useful*.

I notice that someone has already created an editing history plug-in in a recent thread of this forum. Andy.

0 Likes

#8

It’d great if besides from scrolling to the last position, the plugin placed the cursor in the line from the last position. I’m trying to modify the code from

self.view.show(CaptureEditing.last_line-(self.posn+1)])

to

self.view.sel().clear()
self.view.sel().add(self.view.line(CaptureEditing.last_line-(self.posn + 1)]))

But it doesn’t work, add method should place the cursor in the line specified… but I think I misunderstood something

0 Likes

#9

@khritz That works for me :smile:

0 Likes

#10

Uops! :smiley:

Ok, now it works for me too, I’ve missed something. I’ll try to improve the code to learn a little bit more about ST API, specially for the different files problem you mentioned.

BTW, do you remember the thread where the plugin was posted?

Thanks

0 Likes

#11

This versions now works for whichever view you are in. That is, it cycles through the last five edit locations for the current view.

[code]import sublime, sublime_plugin

POSITIONS = {}

class LastEditLineCommand(sublime_plugin.TextCommand):
posn = 0
def run(self, edit):
vid = self.view.id()
if not POSITIONS.has_key(vid): return
if len(POSITIONS[vid]) <= self.posn + 1:
self.posn = 0
self.view.sel().clear()
self.view.show(POSITIONS[vid]-(self.posn+1)])
self.view.sel().add(self.view.line(POSITIONS[vid]-(self.posn + 1)]))
self.posn = (self.posn + 1) % 5

class CaptureEditing(sublime_plugin.EventListener):
def on_modified(self, view):
sel = view.sel()[0]
vid = view.id()
curr_line, _ = view.rowcol(sel.begin())
if not POSITIONS.has_key(vid):
POSITIONS[vid] = [curr_line, sel.begin()]
elif POSITIONS[vid][0] != curr_line:
POSITIONS[vid].append(sel.begin())
POSITIONS[vid][0] = curr_line
if len(POSITIONS[vid]) > 6: POSITIONS[vid].pop(1)[/code]

0 Likes

#12

I’ve done some improvements, now the plugin is multi-file safe and swtiches the view if the last edit position was in another opened file. Please if someone could take a look at the code and tell me if you see anything wrong:

[code]import sublime
import sublime_plugin

class LastEditLineCommand(sublime_plugin.TextCommand):

def run(self, edit):
    if (len(CaptureEditing.last_view) > 0 and self.view.id() != CaptureEditing.last_view-1] and CaptureEditing.last_view-1] > 0):
        sublime.active_window().focus_view(CaptureEditing.view_refs[CaptureEditing.last_view[-1]])
        CaptureEditing.last_view.pop(-1)
    if len(CaptureEditing.last_line[self.view.id()]) > CaptureEditing.posn[self.view.id()]:
        self.view.show(self.view.line(CaptureEditing.last_line[self.view.id()]-(CaptureEditing.posn[self.view.id()] + 1)]))
        self.view.sel().clear()
        self.view.sel().add(self.view.line(CaptureEditing.last_line[self.view.id()]-(CaptureEditing.posn[self.view.id()] + 1)]))
        CaptureEditing.posn[self.view.id()] = (CaptureEditing.posn[self.view.id()] + 1) % 5
    else:
        CaptureEditing.posn[self.view.id()] = 0

class CaptureEditing(sublime_plugin.EventListener):
last_line = {}
prev_line = {}
posn = {}
last_view = ]
view_refs = {}

def on_modified(self, view):
    sel = view.sel()[0]
    curr_line, _ = view.rowcol(sel.begin())
    CE = CaptureEditing
    if CE.prev_line[view.id()] == -1 or curr_line != CE.prev_line[view.id()]:
        CE.prev_line[view.id()] = curr_line
        CE.last_line[view.id()].append(sel.begin())
        if len(CE.last_line[view.id()]) > 10:
            CE.last_line[view.id()].pop(0)
        CE.last_view.append(view.id())
        if len(CE.last_view) > 10:
            CE.last_view.pop(0)

def on_close(self, view):
    CE = CaptureEditing
    CE.last_line.remove(view.id())
    CE.prev_line.remove(view.id())
    CE.posn.remove(view.id())
    CE.view_refs.remove(view.id())

def on_new(self, view):
    CE = CaptureEditing
    CE.last_line[view.id()] = ]
    CE.prev_line[view.id()] = -1
    CE.posn[view.id()] = 0
    CE.view_refs[view.id()] = view

def on_activated(self, view):
    CE = CaptureEditing
    if view.id() not in CE.last_line:
        CE.last_line[view.id()] = ]
    if view.id() not in CE.prev_line:
        CE.prev_line[view.id()] = -1
    if view.id() not in CE.posn:
        CE.posn[view.id()] = 0
    if view.id() not in CE.view_refs:
        CE.view_refs[view.id()] = view

[/code]

Thanks a lot

0 Likes