on_load method doesn't work as expected - sublimetext2

Here is simple test for on_load method.
import sublime_plugin
class OnLoadTest(sublime_plugin.EventListener):
def on_load(self, view):
print("Tested")
If I open some file, then close this file (Ctrl-W), and then reopen it (Ctrl-Shift-T), the plugin works fine.
However, if I open some file, then close the editor, and then reopen the editor, the plugin will not be launched. (Despite that the file was successfully reopened, thanks to "hot_exit": true and "remember_open_files": true in my preferences).
Is it some bug or just my lack of skills?
I use ST3, build 3126.

Whether this is a bug or a conscious design decision has been debated quite a bit over the years, but is fairly well known.
When restoring from the previous session, all open files are put back to the state they were in, which includes things such as the selected text, unsaved changes, modified settings and so on. Sublime starts up and performs those tasks before or while it's actively loading in plugin code in order to make startup as fast as possible.
If on_load is doing something that you need to be done again while coming back from a restored session, you can detect when your plugin is being loaded by defining a module level plugin_loaded() function, which Sublime will call once everything is loaded. In it you can scan through all of the windows and files and take some action.
An example might be:
import sublime
import sublime_plugin
import os
def plugin_loaded ():
# Show project in all views of all windows
for window in sublime.windows ():
for view in window.views ():
show_project (view)
def show_project(view):
# Sometimes a view is not associated with a window
if view.window() is None:
return
# Is there a project file defined?
project_file = view.window ().project_file_name ()
if project_file is not None:
# Get the project filename without path or extension
project_name = os.path.splitext (os.path.basename (project_file))[0]
view.set_status ("00ProjectName", "[" + project_name + "]")
# Display the current project name in the status bar
class ProjectInStatusbar(sublime_plugin.EventListener):
# When you create a new empty file
def on_new(self, view):
show_project (view)
# When you load an existing file
def on_load(self, view):
show_project (view)
# When you use File > New view into file on an existing file
def on_clone(self, view):
show_project (view)

Related

Shortcut to open the currently opened file's folder left panel

I know the "File > Open folder..." dialog box in Sublime.
The problem is that:
it first opens a "file picker" dialog box
after choosing the right folder, it opens the folder in a new Sublime Text window, instead of the current window
How to open the current file's folder in the left "Folder view" of the current Sublime window, without any popup? (I would like to bind a keyboard shortcut for this). Note: I still use Sublime 2.
The menu item Project > Add Folder to project... will prompt you for the name of a folder and then add it to the current window rather than create a new one. Contrary to the name, this will always work even if you're not explicitly using sublime-project files directly.
For doing this without any sort of prompt, a plugin would be needed to adjust the list of folders that are open in the window currently.
In Sublime Text 3 and above, there is API support for directly modifying the list of folders that are open in the window, while Sublime Text 2 only has an API for querying the list of folders.
All versions of Sublime have a command line helper that can be used to interact with the running copy of Sublime (generally referred to as subl), and one of the things it can do is augment the list of folders in the window by adding an additional one. In Sublime Text 2, the subl helper is just the main Sublime Text executable itself.
The following is a plugin that can be used in Sublime Text 2 and above that will perform the appropriate action to get the path of the current file to open in the side bar. If you're unsure of how to use plugins, see this video on how to install them.
import sublime
import sublime_plugin
import os
# This needs to point to the "sublime_text" executable for your platform; if
# you have the location for this in your PATH, this can just be the name of the
# executable; otherwise it needs to be a fully qualified path to the
# executable.
_subl_path = "/home/tmartin/local/sublime_text_2_2221/sublime_text"
def run_subl(path):
"""
Run the configured Sublime Text executable, asking it to add the path that
is provided to the side bar of the current window.
This is only needed for Sublime Text 2; newer versions of Sublime Text have
an enhanced API that can adjust the project contents directly.
"""
import subprocess
# Hide the console window on Windows; otherwise it will flash a window
# while the task runs.
startupinfo = None
if os.name == "nt":
startupinfo = subprocess.STARTUPINFO()
startupinfo.dwFlags |= subprocess.STARTF_USESHOWWINDOW
subprocess.Popen([_subl_path, "-a", path], startupinfo=startupinfo)
class AddFileFolderToSideBarCommand(sublime_plugin.WindowCommand):
"""
This command will add the path of the currently focused file in the window
to the side bar; the command will disable itself if the current file does
not have a name on disk, or if it's path is already open in the side bar.
"""
def run(self):
# Get the path to the current file and add it to the window
self.add_path(self.get_current_path())
def is_enabled(self):
"""
The command should only be enabled if the current file has a filename
on disk, and the path to that file isn't already in the list of folders
that are open in this window.
"""
path = self.get_current_path()
return path is not None and path not in self.window.folders()
def get_current_path(self):
"""
Gather the path of the file that is currently focused in the window;
will return None if the current file doesn't have a name on disk yet.
"""
if self.window.active_view().file_name() is not None:
return os.path.dirname(self.window.active_view().file_name())
return None
def add_path(self, path):
"""
Add the provided path to the side bar of this window; if this is a
version of Sublime Text 3 or beyond, this will directly adjust the
contents of the project data to include the path. On Sublime Text 2 it
is required to execute the Sublime executable to ask it to adjust the
window's folder list.
"""
if int(sublime.version()) >= 3000:
# Get the project data out of the window, and then the list of
# folders out of the project data; either could be missing if this
# is the first project data/folders in this window.
project_data = self.window.project_data() or {}
folders = project_data.get("folders", [])
# Add in a folder entry for the current file path and update the
# project information in the window; this will also update the
# project file on disk, if there is one.
folders.append({"path": path})
project_data["folders"] = folders
self.window.set_project_data(project_data)
else:
# Run the Sublime executable and ask it to add this file.
run_subl(path)
This will define a command named add_file_folder_to_side_bar which will add the path of the current file to the side bar; the command disables itself if the current file doesn't have a name on disk, or if that path is already open in the side bar.
If you're using Sublime Text 2, note that you need to adjust the variable at the top to point to where your copy of Sublime is installed (including the name of the program itself, as seen in the example code), since the plugin will need to be able to invoke it to adjust the side bar.
In order to trigger the command, you can use a key binding such as:
{ "keys": ["ctrl+alt+a"], "command": "add_file_folder_to_side_bar"},
You can also create a file named Context.sublime-menu in your User package (the same place where you put the plugin) with the following contents to have a context menu item for this as well:
[
{ "caption": "-", "id": "file" },
{ "command": "add_file_folder_to_side_bar", "caption": "Add Folder to Side Bar",}
]
Solved for ST2 with #OdatNurd's idea:
class OpenthisfolderCommand(sublime_plugin.TextCommand):
def run(self, edit):
current_dir = os.path.dirname(self.view.file_name())
subprocess.Popen('"%s" -a "%s"' % ("c:\path\to\sublime_text.exe", current_dir))
Add the key binding with for example:
{ "keys": ["ctrl+shift+o"], "command": "openthisfolder"}

How to make a buffer have a read-only in Sublime Text 2

This is not about those files that have their read-only flag set at the OS level, but about every file that users don't intend to modify.
I want Sublime Text to ignore any changes and prevent saving anything to such files. One example for this scenario is when the user is reading source code that shouldn't be altered in anyway.
"Just be really careful, and don't press any buttons" is undoubtedly a good advice, but if I were to "accidentally" delete that octothorpe in front of a comment, or add new lines to a file that is sensitive to such things (some config files in Linux) and then accidently hit save....
I found "toggle-readonly" at GitHub, but it is actually toggling the file permissions ("Read Only", "Write"), which is not quite what I wanted.
Yes, this is possible, but you'll have to write a plugin (which actually isn't that hard, especially if you know Python). The API call is view.set_read_only(flag) in the sublime module, where Flag is a boolean. Here's a quick example which checks if a newly-opened file has a certain suffix, and if so sets it to read-only.
import sublime
import sublime_plugin
class MakeViewReadOnlyCommand(sublime_plugin.TextCommand):
def run(self, edit):
if self.view.file_name().endswith(".cfg"):
self.view.set_read_only(True)
class ConfigFileListener(sublime_plugin.EventListener):
def on_load(self, view):
view.run_command("make_view_read_only")
Open a new file with Python syntax, copy the code into it, alter it as needed, then save it in your Packages/User directory as make_view_read_only.py. Restart Sublime to load it, and you should be all set. To test if a certain view is read-only, open the console and enter
view.is_read_only()
The plugin "Toggle the View Read-Only" will do it. It basically does what MattDMo said: when you set the view as read-only, the file can still be changed by another program (or another user), and Sublime Text will pick up those changes. It also has the context menu item you asked for. I like the "Readonly" indicator in status bar.
I didn't test it on Sublime Text 2, but in Sublime Text 3 it works great, and it claims to work on Sublime Text 2 as well.

Sublime Text Prompt to Save File

I often will have a scratch pad in ST that I then close via Ctrl-F4. It always prompts me to save it, which I find to be a pain.
Is there a setting in ST where I can either change to default of this dialog to "Close without saving", or do not even prompt me at all if it is a new file (i.e. has no name).
You can set a tab to be a scratch buffer (doesn't prompt to save when closed). With the desired tab opened, open the console with Ctrl` and type:
view.set_scratch(True)
then hit Enter, and close the console with Esc. You can now close the tab whenever you want without being prompted. Of course, you can manually save the contents if you wish.
If you would like to have all new buffers set to scratch by default, you'll need a plugin. Create a new Python file in Sublime with the following contents:
import sublime
import sublime_plugin
class SetNewScratchBuffer(sublime_plugin.EventListener):
def on_new(self, view):
view.set_scratch(True)
def on_save(self, view):
view.set_scratch(False)
Save the file as Packages/User/set_new_scratch_buffer.py where Packages is the folder opened when selecting Preferences -> Browse Packages... (~/.config/sublime-text-2/Packages on Linux with ST2). Once saved, it should automatically become active, although you can restart Sublime to make sure. Now, all new buffers created with CtrlN or File -> New File will automatically have the scratch attribute set. This will be disabled when the file is saved, so you don't accidentally destroy changes to an opened file.

Sublime TFS auto checkout: Unable to save

I am using Sublime Text 2 with the Sublime TFS plugin. I can check out files without a problem. If I try to save a checked-in file, Sublime TFS will automatically check the file out. However, before the checkout is complete (slow servers), Sublime Text shows an unable to save dialog. I can dismiss the dialog and save the file (because checkout is complete), but it is an annoyance.
Does anyone know of a solution? Perhaps I can change the timeout on a save before the dialog shows?
Found the solution. I changed the argument in thread.join() from 5 to 10 seconds inside the on_pre_save() function, located in sublime_tfs.py. See code below.
def on_pre_save(self, view):
if not hasattr(self, 'manager'):
self.manager = TfsManager()
if self.manager.auto_checkout_enabled:
path = view.file_name()
if not (path is None):
if is_readonly(path):
thread = TfsRunnerThread(path, self.manager.auto_checkout)
thread.start()
ThreadProgress(view, thread, "Checkout...", "Checkout success: %s" % path)
thread.join(10) # Changed from 5 to 10 seconds.
if thread.isAlive():
sublime.set_timeout(lambda: "Checkout failed. Too long operation")

Is there a way to convert Trac Wiki pages to HTML?

I see the suggestion of using Mylyn WikiText to convert wiki pages to html from this question except I'm not sure if its what I'm looking for from reading the front page of the site alone. I'll look into it further. Though I would prefer it being a Trac plug-in so I could initiate the conversion from within the wiki options but all the plugins at Trac-Hacks export single pages only whereas I want to dump all formatted pages in one go.
So is there an existing Trac plug-in or stand-alone application that'll meet my requirements? If not where would you point me to start looking at implementing that functionality myself?
You may find some useful information in the comments for this ticket on trac-hacks. One user reports using the wget utility to create a mirror copy of the wiki as if it was a normal website. Another user reports using the XmlRpc plugin to extract HTML versions of any given wiki page, but this method would probably require you to create a script to interface with the plugin. The poster didn't provide any example code, unfortunately, but the XmlRpc Plugin page includes a decent amount of documentation and samples to get you started.
If you have access to a command line on the server hosting Trac, you can use the trac-admin command like:
trac-admin /path/to/trac wiki export <wiki page name>
to retrieve a plain-text version of the specified wiki page. You would then have to parse the wiki syntax to HTML, but there are tools available to do that.
For our purposes, we wanted to export each of the wiki pages individually without the header/footer and other instance-specific content. For this purpose, the XML-RPC interface was a good fit. Here's the Python 3.6+ script I created for exporting the whole of the wiki into HTML files in the current directory. Note that this technique doesn't rewrite any hyperlinks, so they will resolve absolutely to the site.
import os
import xmlrpc.client
import getpass
import urllib.parse
def add_auth(url):
host = urllib.parse.urlparse(url).netloc
realm = os.environ.get('TRAC_REALM', host)
username = getpass.getuser()
try:
import keyring
password = keyring.get_password(realm, username)
except Exception:
password = getpass.getpass(f"password for {username}#{realm}: ")
if password:
url = url.replace('://', f'://{username}:{password}#')
return url
def main():
trac_url = add_auth(os.environ['TRAC_URL'])
rpc_url = urllib.parse.urljoin(trac_url, 'login/xmlrpc')
trac = xmlrpc.client.ServerProxy(rpc_url)
for page in trac.wiki.getAllPages():
filename = f'{page}.html'.lstrip('/')
dir = os.path.dirname(filename)
dir and os.makedirs(dir, exist_ok=True)
with open(filename, 'w') as f:
doc = trac.wiki.getPageHTML(page)
f.write(doc)
__name__ == '__main__' and main()
This script requires only Python 3.6, so download and save to a export-wiki.py file, then set the TRAC_URL environment variable and invoke the script. For example on Unix:
$ TRAC_URL=http://mytrac.mydomain.com python3.6 export-wiki.py
It will prompt for a password. If no password is required, just hit enter to bypass. If a different username is needed, also set the USER environment variable. Keyring support is also available but can be disregarded.