Issue #9 resolved

ST3 - autocheckout not working

thomas Little
created an issue

In ST2 the autocheckout has been working fine. I recently upgraded to ST3, and it's no longer auto-checking out my files.

Line 3 of sublime_tfs.sublime.settings is set to true. "auto_checkout_enabled" : true

It seems nothing happens at all when I begin to edit code and have to manually checkout the file using the TFS context menu.

Comments (16)

  1. david_joham

    Hi Denis,

    I'm running Sublime v3 (stable channel build 3059.) Visual Studio 2012.

    There's nothing in the console window that's would indicate an error.

    I did some further digging and discovered that the following code is evaluating as None:

    self.manager.auto_checkout_enabled
    

    inside the on_modified event handler within the TfsEventListener class.

    class TfsEventListener(sublime_plugin.EventListener):
        def __init__(self):
            self.manager = TfsManager()
    
        def on_modified(self, view):
    
            #The following line will display "None"
            sublime.ok_cancel_dialog(str(self.manager.auto_checkout_enabled))
            if not self.manager.auto_checkout_enabled:
                return
    

    If I modify sublime_tfs.py in an external editor, sublime apparently reloads the script in the background and then the evaluation of the self.manager.auto_checkout_enabled works properly from then on - for that instance of Sublime. Closing it and re-opening it causes the problem to come back.

    To get this to work in my environment, I made the following change:

    class TfsEventListener(sublime_plugin.EventListener):
    
        def __init__(self):
            self.manager = TfsManager()
            self.attemptedAutoCheckout = False
            self.autoCheckoutStatus = False
    
        def on_pre_save(self, view):
            self.attemptedAutoCheckout = False    
            self.autoCheckoutStatus = False
            path = view.file_name()
            if isReadonly(path):
                thread = TfsRunnerThread(path, self.manager.auto_checkout)
                thread.start()
                #no point in this, the status doesn't come through
                #ThreadProgress(view, thread, "Checkout...", "Checkout success: %s" % path)
                thread.join(10000)
                self.attemptedAutoCheckout = True
                if thread.isAlive():
                    self.autoCheckoutStatus = False
                else:
                    self.autoCheckoutStatus = thread.success
    
        def on_post_save(self, view):
            if self.attemptedAutoCheckout:
                sublime.set_timeout(lambda: DisplayAutoCheckoutStatus(view.file_name(), self.autoCheckoutStatus), 100)
    
    def isReadonly(p_path):
        try:
            fileAttrs = os.stat(p_path)
            fileAtt = fileAttrs[0]
            return not fileAtt & stat.S_IWRITE
        except WindowsError:
            pass
    
    
    def DisplayAutoCheckoutStatus(path, checkoutWorked):
        if checkoutWorked:
            sublime.status_message("Save and automatic checkout of %s succeeded" % path)
        else:
            sublime.status_message("Save and automatic checkout of %s failed" % path)
    

    The changes are listed below:

    Ignore the auto checkout setting and always has auto checkout turned on. This "works around" the bug described below by always turning the feature on - which is what I want it to do - but not a fix to the true problem :)

    The auto-checkout now happens on the file save. Previously, the operation was called every time a change was made in the view – resulting in the operation being called more than I would desire (a personal preference). Additionally, the previous implementation prevented the tfs:undo operation from working the way I think it should. You could call the undo from within Sublime, but due to the on_modified implementation, sublime would automatically re-check out the file for you right after the undo operation finished. My change will not re-check out the file until a subsequent save takes place.

    The auto-checkout operation now blocks, waiting for the checkout to finish. This prevents the “overwrite the read-only file?” dialog from displaying

    I added some messaging to the status bar indicating whether or not the auto checkout succeeded or not

    I didn't fix the true bug (the initialization of the variable) but I did get it to work in an acceptable way. I might look at the variable initialization a little later in the week if I have time.

    Best regards,

    David

  2. david_joham

    I tool a longer look at this today. Here's the results of my analysis.

    For reasons I don't yet understand, the __init__ of the TfsEventListener is not being called when Sublime first loads. As a result, the logic to access auto_checkout_enabled doesn't work.

    If sublime_tfs.py is saved while Sublime is running, __init__ is fired and everything works as expected.

    My python and sublime experience started last Friday :) so I'm still learning - but nothing seems overtly wrong in the code so I'm a little baffled as to what's going wrong.

    I did find what could best be described as a work around. In the following code, you can see that I started listening for the on_activated event of the view inside TfsEventListener. In that event, I do a self.manager = TfsManager(). Sublime does call this event on startup and by setting up self.manager there I'm able to later read the configuration variable as I would expect inside the on_pre_save.

    I would assume this work-around would also work for the previous implementation where the checkout was happening inside the on_modified event as well.

    Here's the new code:

    class TfsEventListener(sublime_plugin.EventListener):
    
        def __init__(self):
            self.attemptedAutoCheckout = False
            self.autoCheckoutStatus = False
    
        def on_activated(self, view):
            self.manager = TfsManager()
    
        def on_pre_save(self, view):
            self.attemptedAutoCheckout = False    
            self.autoCheckoutStatus = False
    
            if not self.manager.auto_checkout_enabled:
                return
    
            path = view.file_name()
            if isReadonly(path):
                thread = TfsRunnerThread(path, self.manager.auto_checkout)
                thread.start()
                #no point in this, the status doesn't come through
                #ThreadProgress(view, thread, "Checkout...", "Checkout success: %s" % path)
                thread.join(10000)
                self.attemptedAutoCheckout = True
                if thread.isAlive():
                    self.autoCheckoutStatus = False
                else:
                    self.autoCheckoutStatus = thread.success
    
        def on_post_save(self, view):
            if self.attemptedAutoCheckout:
                sublime.set_timeout(lambda: DisplayAutoCheckoutStatus(view.file_name(), self.autoCheckoutStatus), 100)
    
    def isReadonly(p_path):
        try:
            fileAttrs = os.stat(p_path)
            fileAtt = fileAttrs[0]
            return not fileAtt & stat.S_IWRITE
        except WindowsError:
            pass
    
    
    def DisplayAutoCheckoutStatus(path, checkoutWorked):
        if checkoutWorked:
            sublime.status_message("Save and automatic checkout of %s succeeded" % path)
        else:
            sublime.status_message("Save and automatic checkout of %s failed" % path)
    

    Hope this is useful...

  3. Ciwan

    Guys, I am facing the same issue as you, but I am a total n00b to Python and the inner workings of Sublime.

    What are the locations of the files that I must change? Can you please provide n00b friendly (step by step) instructions to get the checkout to work for TFS.

    I'm on Sublime Text 3 - Stable Channel, Build 3059

  4. aj troxell

    As a temporary fix I created custom keybindings for checkout and checkin:

    { "keys": ["ctrl+shift+o"], "command": "tfs_checkout"},
    { "keys": ["ctrl+shift+i"], "command": "tfs_checkin"}
    
  5. david_joham

    Ciwan

    The steps below assume you already have the latest version of the TFS plugin installed within sublime and have configured it to point to the correct TFS command line executables.

    1) Open Sublime

    2) Select Preferences -> Browse Packages from the application menu

    3) That will open an Explorer window to something like C:\Users\djoham\AppData\Roaming\Sublime Text 3\Packages

    4) Go into the "Sublime TFS" directory

    5) Open sublime_tfs.py in a text editor (I would use something other than Sublime here, but it probably doesn't matter)

    6) Scroll all the way to the bottom until you see this code (on or around line 258):

    class TfsEventListener(sublime_plugin.EventListener):
        def __init__(self):
            self.manager = TfsManager()
    

    7) Delete everything from that point to the end of the file. When you're done, the last thing you should see in your file is this:

    class TfsAnnotateCommand(sublime_plugin.TextCommand):
        def run(self, edit):
            path = self.view.file_name()
            if not (path is None):
                manager = TfsManager()
                thread = TfsRunnerThread(path, manager.annotate)
                thread.start()
                ThreadProgress(self.view, thread, "Annotating...", "Annotate done")
    

    8) Grab the code from my last comment and paste it into the bottom of the file.

    9) Save the file

    10) Close and re-open sublime. Auto checkout should now work when you attempt to save a file. When you save a file, you'll notice a second or two delay as the checkout happens and then you'll see a notification in the status bar of Sublime indicating whether or not the checkout succeeded.

    Good luck

  6. Ciwan

    david_joham

    Thank You, I followed your steps to the letter, but still not working for me. :(

    If my understanding is correct, your code checks out my file when I save it with my changes ... which is fine for me ... but when I then press Ctrl + Shift + P and choose TFS: CheckIn ... I do not get an option to check in, a black console appears quickly and goes away, so no check-in :(

  7. Ciwan

    Denis Kulikov Awesome. Is the fix available for download right now? i.e. can I uninstall then re-install to have the fix? I'm sure you're aware, this is essential functionality. Thanks :)

  8. Log in to comment