1

Here is the code:

Sublime plugin:

File 1: open_in_default_program.py:

# https://github.com/SublimeTextIssues/Core/issues/2368

import webbrowser
import sublime_plugin

class OpenInDefaultProgramCommand(sublime_plugin.TextCommand):
    def run(self, edit):
        if self.view.file_name():
            webbrowser.open_new_tab("file://" + self.view.file_name())

    def is_visible(self):
        return self.view.file_name() is not None and (
            self.view.file_name()[-5:] == ".html" or
            self.view.file_name()[-3:] == ".md" or
            self.view.file_name()[-4:] == ".ahk")

File 2: Context.sublime-menu:

[
    { "command": "open_in_default_program" },
]

AutoHotkey test file:

Test.ahk:

MsgBox Something

My question:

It works for HTML and Markdown files. It also works for AutoHotkey files - but how? From what I see, it uses browser. AutoHotkey files can't be opened in browser - but actually they are perfectly could be launched with this plugin. Why it works?

Here is another plugin for opening files in default application, but it's much more complex: https://github.com/SublimeText/OpenDefaultApplication/blob/master/OpenDefault.py

user90726
  • 939
  • 1
  • 8
  • 28
  • Side-note: `self.view.file_name()[-5:] == ".html" or self.view.file_name()[-3:] == ".md" or self.view.file_name()[-4:] == ".ahk"` is a really verbose/brittle (thanks to hard-coded slice indices) way to spell `self.view.file_name.endswith((".html", ".md", ".ahk"))` (`endswith` accepts `tuple` of possible endings and returns `True` if any of them terminate the string, and it avoids brittle stuff like slicing by hardcoded indices). – ShadowRanger Jul 30 '18 at 12:17
  • @ShadowRanger As I understand, this compact form will not work in ST? (Doesn't work for me, at least). – user90726 Jul 30 '18 at 12:47
  • SublimeText is plain Python, right? `endswith` [is a plain Python method on `str`](https://docs.python.org/3/library/stdtypes.html#str.endswith); should work everywhere. – ShadowRanger Jul 30 '18 at 14:24
  • @ShadowRanger Yes, actually it works, but it was necessary to add `()` after `file_name`. – user90726 Jul 30 '18 at 19:26
  • Also, as I discovered, it is a good idea to add `.lower()` after `.file_name()`. So, the code will work for uppercase extensions too. – user90726 Jul 30 '18 at 19:57
  • Ah, my mistake; left off the parens by accident. Glad you figured it out. – ShadowRanger Jul 31 '18 at 20:19

1 Answers1

4

This is mentioned in the documentation for webbrowser.open:

Note that on some platforms, trying to open a filename using this function, may work and start the operating system’s associated program. However, this is neither supported nor portable.

The reason for this is that some browsers, when given a file they don't know how to handle, will automatically open it in the default program for that file. For example, on Windows, Internet Explorer is basically the same program as Windows Explorer,1 so asking Internet Explorer to open a file it doesn't know how to handle has basically the same effect as double-clicking that file in Windows Explorer.

Of course other browsers might do nothing, or copy the file to your Downloads directory, or pop up a dialog asking you what you want to do with this file. That's why the docs say "this is neither supported nor portable".


It's also worth noting that, like many of the stdlib modules, the docs for webbrowser have a link to the source code at the top, and the source code is pretty straightforward, simple Python code. You can see that ultimately, it's just using the subprocess module to call something like (depending on your detected browser, and possibly with some browser-specific options to tell it "don't start a whole new browser, tell the existing browser window to open a new tab"):

iexplore.exe file://path/to/your/file

You can easily work out exactly what command it's running and experiment running the same command in your shell/command prompt.


The more complex plugin shows the way to do this as portably as possible:

  • On Windows, you can call os.startfile.
  • On other platforms, you run a command-line tool. (The plugin seems to work out the right tool at install time, store it in a settings file, and look it up in that file.)
    • On macOS, it's open.
    • On FreeDesktop systems, including most modern Linux distros, it's xdg-open.

Those three options are usually enough to cover 99% of your users, and almost all of the remaining users will be people who know what they're doing and can figure out what to put in your settings file. (Unless, of course, you're developing for mobile, in which case you'll want to write special handlers for iOS and Android.)


1. This isn't really true anymore in modern Windows, but it's close enough to illustrate the point.

abarnert
  • 354,177
  • 51
  • 601
  • 671