15. Packages

Packages are simply a set of Resource Files used by Sublime Text, gathered into a single directory named after the Package.

A Package Resource is simply a Resource File that is part of a Package and therefore in a Package directory.

Another (more compact) form they can come in (e.g. the shipped Packages and most user-installed Packages are in this form) is in a .sublime-package file, which is simply a .zip file containing all the files of that package, preserving the Package’s directory structure.

Compressed or not, any package file can be viewed using:

Tools > Command Palette... > View Package File

Files retrieved from a compressed file will be read-only, but may be saved as editable files using.

File > Save As...

(They remain read-only until they are closed and then re-opened again from where they were saved.)

Be advised: if there is a .sublime-package, as well as a loose file in a <data_path>/Packages/<pkg_name>/ directory, the contents of the loose file will override the ENTIRE file stored in the .sublime-package file. See Overriding below for more details.

Sublime Text ships with over 50 Packages pre-installed, and many, many user-created Packages are available through several channels, the easiest of which is using the Package Control Package, which provides very easy management of the Packages you have installed with any given Sublime-Text installation.

15.1. Sublime Text Packages vs Python Packages

As it turns out, the name “Package” in Sublime Text’s Python context is indeed aptly chosen: Sublime Text indeed “considers” a Sublime Text Package as a Python Package, despite its not having a __init__.py file in it, which is normally what makes a Python Package a Package.

Rationale: From your Sublime Text Package, you can do relative importing, which is ONLY legal from within a Python Package, and generates an exception when it is used outside of a Python Package.

from . import sibling_module

This is logical actually, since the importlib package (comes with Python, and is used “under the covers” as the implementation of the import statement) allows directly importing of Python modules as packages whether they have a __init__.py file in them or not. So I suspect that is the mechanism being used by Sublime Text for “bringing in” individual Python plugins as part of a package named after the parent directory, and is probably also why the Sublime Data/User/ directory is sometimes referred to as the “User Package”.

And why shouldn’t Sublime Text use that powerful mechanism? It’s Python’s way of making new functionality available safely under its own namespace.

Indeed, API Environments Overview describes the environment Plugins are run in, and the 3rd paragraph hints strongly that it indeed treats a Sublime Text Package as a Python Package.

15.2. Locations

The concept of a “Packages Directory” is really 5 possible locations for any given Package Resource File.

Default Package:

Loaded first; Default.sublime-package [1]

shipped Packages:

Loaded next (alphabetically by Package name); <pkg_name>.sublime-package files shipped with Sublime Text are stored here. (Do not change this directory’s contents.) [1]

installed Packages:

Loaded next (alphabetically by Package name); Packages installed by user are stored here when they are designed to remain in .sublime-package format. [1]

  • Represented location: <data_path>/Packages/<pkg_name>/

  • Actual location: <data_path>/Installed Packages/ (not editable)

unpacked Packages:

Loaded next (alphabetically by Package name); Packages installed by the user when designed to be unpacked (loose files) by Package Control, as well as user-created Packages can be stored here.

  • Represented location: <data_path>/Packages/<pkg_name>/

  • Actual location: same (on disk, Resource files editable)

(See note below.)

User Package:

Loaded last; <data_path>/User/. Customizations unique to each user are stored here. Path representation: Packages/User/.

  • Represented location: <data_path>/Packages/User/

  • Actual location: same (on disk, Resource files editable)

If you are a power user, your customizations will go in the latter 2 directories.

The User Package can be used to develop new packages while they are in experimental stages. Once in a more complete state, they can be moved to <data_path>/Packages/<pkg_name>/.

15.3. Overriding

The above loading sequence is how “overrides” or customized behavior are implemented. Note carefully that when the Package (including the User Package) has a file like Default (Windows).sublime-keymap or Main.sublime-menu, it does not replace the whole Keymap or Main Menu. In these cases, the overriding happens at a finer level of granularity: these files contain JSON data structures (e.g. a list or tree structure [respectively] of dictionary objects), and the value of each entry in the data structure overwrites the already-loaded value for the entry with the same key and hierarchy location in the data structure. So you can think of the contents of both files as being “merged”, where due to the loading sequence outlined above, the values loaded from the Packages lower in the list overwrite the same values from Packages higher in the list. It is this sequence that governs what overrides what.

Note that the above-described “merge” override behavior is what happens between the Package (including the User Package) and the Sublime Text application, and is restricted to just some (not all) of the Resource Files that contain Sublime-relaxed-JSON format data:

  • .sublime-commands

  • .sublime-completions

  • .sublime-keymap

  • .sublime-menu

  • .sublime-mousemap

  • .sublime-settings

But this does not apply to any of the other types of Resource Files:

  • .sublime-build

  • .sublime-color-scheme

  • .tmTheme

  • .sublime-macro

  • .tmPreferences

  • .py

  • .sublime-snippet

  • .sublime-syntax

  • .tmLanguage

  • .sublime-theme

15.3.1. Overriding Whole Files from a Zipped Package

Once you understand the “merge-type” overriding described above, it is important to understand that there is another type of overriding (whole-file overriding) that is triggered when:

  • the Package being overridden is in archive form (i.e. with .sublime-package file extension, which will be either an installed Package or a shipped Package, and

  • the override file is placed in an unpacked Package directory (i.e. in <data_path>/Packages/<pkg_name>/ with an identical relative path and name).

In this case, Sublime Text literally ignores that same file from the .sublime-package file and replaces it with loose file in the unpacked Package directory.

In this case, the loose-file directory <data_path>/Packages/<pkg_name>/ is called an “Override Package”.

Illustrated:

<data_path>/Packages/Installed Packages/<pkg_name>.sublime-package, or
<executable_path>/Packages/<pkg_name>.sublime-package              -----+
                                                                        | loose file with
                                                                        | same name and
                                                                        | same relative
                                                                        | subdirectory
<data_path>/Packages/<pkg_name>/...    <--------------------------------+

When the Override Package directory is empty, it doesn’t change anything. But for each occurrence of a file with the exact same relative subdirectory and filename as in the .sublime-package file, Sublime Edit ignores the .sublime-package file and uses the loose file in the Override Package instead.

An interesting aspect of Override Packages is that Python plugins therein are able to use relative import paths for accessing other modules in the corresponding .sublime-package file as if they were part of it.

Example: You want to add functionality to the Python Package’s def (function) Snippet. Its default content is provided in <executable_path>/Packages/Python.sublime-package.

The way to find the Snippet file that is involved, make a copy of the Python.sublime-package file in a safe, empty directory, change its extension to .zip and then extract the contents out of the file. Scan the package Snippet files for

<tabTrigger>def</tabTrigger> or just >def<

and you will find it in the ./Snippets/ subdirectory as function.sublime-snippet.

Make a copy of that file and place it in

<data_path>/Packages/Python/Snippets/

then modify it to suit you. Sublime Text reloads it as soon as you save it so the turn-around time on modifications-to-testing is remarkably short.

Note

The following two Packages provide a convenient way to create an override of a single Package file. The Commands they provide that do this are:

  • PackageResourceViewer: Open Resource

  • OverrideAudit: Create Override

Both of these commands:

  • open the file in an editable form, with an “apparent” path <data_path>/Packages/<pkg_name>/<resource_file>, even though that file (and directory) may not exist yet, and

  • use the Ctrl+S (Save file) operation to create the file if it is not already there, as well as create the new <data_path>/Packages/<pkg_name>/ directory if it did not already exist.

Note

Some packages are configured to be installed as loose files in the <data_path>/Packages/<pkg_name>/. When this is the case, once they are installed, the only instance of that Package is as loose files in that directory. When this is the case, editing of those Package Resource files (when they are not created by yourself) is not recommended because those files get replaced when the Package is updated. If you wish to override only certain files in an unpacked Package, you can create a .sublime-package file out of it by zipping its contents, preserving the directory structure under the

<data_path>/Packages/<pkg_name>/

directory, naming the zipped file

<pkg_name>.sublime-package

and moving the newly-created file into the

<data_path>/Installed Packages/

directory. Then in the original (unpacked) directory (which now becomes an Override Package because of the existence of the <pkg_name>.sublime-package file where Sublime Text will load it), keep only the files you wish to override.

Caution

A precaution about Override Packages: if/when the overridden file(s) in the corresponding .sublime-package is updated, you will not be notified.

The OverrideAudit package provides monitoring of override files and will notify you when the file it overrides has been updated.

15.4. Creating New Packages

You create a new package by simply creating a directory under <data_path>/Packages/.

This directory is conveniently accessible via Preferences > Browse Packages....

15.4.1. A Package’s Python Version

As of Sublime Text build 4200, the default Python version used by a package is 3.3. However, Python 3.8 is available. To get your Package to use Python 3.8, place a file in the top directory of the package containing just 3 bytes: “3.8”.

MyPackage/
    .python-version

15.5. Managing Package Complexity

As functionality is added to Packages being developed, they can become quite complex. (Sometimes they have complex designs to start with.) For increased maintainability (via modularity) it can be useful to move related groups of related functionality into their own Python modules.

Simpler Packages can factor out related functionality into their own sibling (top-level) Python modules which make use of each others’ facilities via the from . import sibling_module statement.

More complex Packages sometimes factor out related functionality as Subpackages for the same reason that Python supports Subpackages within Packages: modularity and maintainability—each Subpackage has its own sphere of responsibility. The method of doing this is exactly the same as implementing Python Subpackages. Usually developers will want to limit doing this to when it makes sense to do so: when the subpackage is relatively self-contained, providing its own data and/or services to the main Package, and (preferably) only depending upon external Packages that ship with Python. (What you don’t want is to have 2 or more modules that are circularly dependent upon one another. Doing that generates an exception while the Package is being loaded, and will thus prevent your Package from being loaded successfully.)

There is an important caveat to using Subpackages in a Sublime Text Package if you have the Package Control Package installed (which is true for virtually all Sublime Text installations). The reason for this is that the Package Control Package automatically updates installed Packages while Sublime Text is running, and when it does this, it does not re-start the Python Interpreter, but instead uses importlib.reload() to reload the top-level Python modules (.py files) in each installed Package that gets updated. If an updated Package has Subpackages that might have been updated, those Subpackages do not get automatically reloaded (with their possibly-updated logic) until Sublime Text restarts. For Packages that will never change again, this is not an issue. However, for packages that get updated periodically, when the update happens, until Sublime Text is restarted, there is a risk of such Packages being in an inconsistent state and possibly broken unless the Package itself addresses this caveat internally.

Here is how to do it.

Let’s say your Package is called ProComment and is structured like this:

ProComment/
    procomment.py         <-- central Plugin
    lib/
        __init__.py
        debug.py
        utils.py
    src/
        __init__.py
        core.py
        contexts.py
        func_parsing.py
        commands/
            __init__.py
            insert_comment.py
            insert_datestamp.py
            insert_timestamp.py

Your central Plugin is named procomment.py and you have 3 Subpackages: lib, src/ and src/commands/. In your central Plugin, implement something like this:

""" procomment.py """
import importlib
import sys
import os
from typing import Tuple


# =========================================================================
# Data
# =========================================================================

_cfg_compressed_pkg_ext = '.sublime-package'

# Use name of parent directory as `package_name`.
module_path, _ = os.path.splitext(os.path.realpath(__file__))
parent_dir, submodule_name = os.path.split(module_path)
_, package_name = os.path.split(parent_dir)
if package_name.endswith(_cfg_compressed_pkg_ext):
    package_name = package_name[:-len(_cfg_compressed_pkg_ext)]
del _
this_module_name = f'{package_name}.{submodule_name}'
_reload_indent_level = -1

# Can't use `debugging = is_debugging(DebugBit.IMPORTING)` here because
# the import required to support it causes a circular import.
debugging = True
if debugging:
    print(f'{this_module_name}  >>> module execution')


# =========================================================================
# Load / Reload
# =========================================================================

def reload(dotted_subpkg: str, submodules: Tuple[str, ...] = ()):
    """
    Reload each module in `submodules` only if previously loaded.  This is a
    precondition of calling ``importlib.reload()`` but is also for efficiency:

    - if Sublime Text is just starting, nothing important happens here (because
      the cached modules will not have been added to ``sys.modules`` yet), and
      and the various ``import`` statements do the loading in the usual way;

    - if ``Package Control`` is updating this Package (or the central Plugin
      was just saved during development), then this function recursively
      reloads each loaded module, and the ``import`` statements then do
      nothing since each target module will already be in ``sys.modules``.

    Note:  The below works on the basis that ``<sublime_data>/Packages``
           directory was placed in ``sys.path`` by Sublime Text.  So the
           module names being constructed below have to look like this:

               MyPackage.subdir.module
               MyPackage.subdir.subdir.module
               etc.

    :param dotted_subpkg:  dotted directory portion of module names that
                             will be found in the keys of ``sys.modules``.
                             Example:  'MyPackage.src.commands'
    :param submodules:     tuple of submodule names
    """
    global _reload_indent_level
    _reload_indent_level += 1
    indent = '  ' * _reload_indent_level
    if debugging:
        if _reload_indent_level == 0:
            print('vvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvv')
        print(f'{indent}reload():  >>> {dotted_subpkg=} {submodules=}')

    if not submodules:
        # Called from top-level Plugin.
        module_name = dotted_subpkg
        if module_name in sys.modules:
            if debugging:
                print(f'{indent}Reloading({module_name})')
            importlib.reload(sys.modules[module_name])
    else:
        # Called from subpackage.
        for submodule in submodules:
            module_name = f'{dotted_subpkg}.{submodule}'
            if module_name in sys.modules:
                if debugging:
                    print(f'{indent}Reloading({module_name})')
                importlib.reload(sys.modules[module_name])

    if debugging:
        print(f'{indent}reload():  <<<')
        if _reload_indent_level == 0:
            print('^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^')

    _reload_indent_level -= 1


reload(package_name + '.lib')  # Recurse into .lib/ subpackage.
reload(package_name + '.src')  # Recurse into .src/ subpackage.

from .lib import *
from .src import *


# =========================================================================
# Events
# =========================================================================

def plugin_loaded():
    core.on_plugin_loaded()


def plugin_unloaded():
    core.on_plugin_unloaded()


if debugging:
    print(f'{this_module_name}  <<<')

Then in each __init__.py file for each Subpackage:

""" lib/__init__.py """
from ..procomment import reload
from .debug import IntFlag, DebugBit, is_debugging
from .utils import sublime_submodule_name

submodule_name = sublime_submodule_name(__file__, 2)
debugging = is_debugging(DebugBit.IMPORTING)
if debugging:
    print(f'{submodule_name}  >>> module execution')

reload(submodule_name, ('debug', 'utils'))

from . import utils

__all__ = [
    'debug',
    'utils'
]

if debugging:
    print(f'{submodule_name}  <<<')
""" src/__init__.py """
from ..procomment import reload
from ..lib.debug import IntFlag, DebugBit, is_debugging
from ..lib.utils import sublime_submodule_name

submodule_name = sublime_submodule_name(__file__, 2)
debugging = is_debugging(DebugBit.IMPORTING)
if debugging:
    print(f'{submodule_name}  >>> module execution')

reload(submodule_name, ('core', 'contexts', 'func_parsing'))
reload(submodule_name + '.commands')  # Recurse into .commands/ subpackage.

from . import core
from .contexts import *
from .commands import *

__all__ = [
    'core',
    'func_parsing',

    # events/contexts
    "ProCommentContextEventListener",

    # commands/*
    "ProCommentInsertDatestampCommand",
    "ProCommentInsertTimestampCommand",
    'ProCommentInsertCommentCommand',
]

if debugging:
    print(f'{submodule_name}  <<<')
""" src/commands/__init__.py """
from ...procomment import reload
from ...lib.debug import IntFlag, DebugBit, is_debugging
from ...lib.utils import sublime_submodule_name

subpackage_name = sublime_submodule_name(__file__, 3)
debugging = is_debugging(DebugBit.IMPORTING)
if debugging:
    print(f'  {subpackage_name}  >>> module execution')

reload(subpackage_name, ('insert_datestamp', 'insert_timestamp', 'insert_comment'))

from .insert_comment import ProCommentInsertCommentCommand
from .insert_datestamp import ProCommentInsertDatestampCommand
from .insert_timestamp import ProCommentInsertTimestampCommand

__all__ = [
    # Comment Commands
    'ProCommentInsertCommentCommand',

    # Datestamp/Timestamp Commands
    'ProCommentInsertDatestampCommand',
    'ProCommentInsertTimestampCommand',
]

if debugging:
    print(f'  {subpackage_name}  <<<')

where sublime_submodule_name is defined in .lib/utils.py as:

_cfg_compressed_pkg_ext = '.sublime-package'


def sublime_submodule_name(file_path: str, subpackage_depth: int):
    """
    Convert `file_path` to the indicated imported Python Module name.  Example
    given `subpackage_depth` == 2:

    file_path: == '/path/to/Sublime Text/Packages/ProComment/src/__init__.py'
    Result: 'ProComment.src'

    file_path: == '/path/to/Sublime Text/Packages/ProComment.sublime-package/src/__init__.py'
    Result: 'ProComment.src'

    file_path: == '/path/to/Sublime Text/Packages/ProComment/src/core.py'
    Result: 'ProComment.src.core'

    :param file_path:         `__file__` for each module calling this function.
    :param subpackage_depth:  Number of directories below `Sublime Text/Packages/`
    """
    module_list = []
    working_path, _ = os.path.splitext(file_path)

    for i in range(subpackage_depth + 1):
        remaining, submodule = os.path.split(working_path)
        include = True

        if submodule == '__init__':
            include = False
        elif i == subpackage_depth and submodule.endswith(_cfg_compressed_pkg_ext):
            submodule = submodule[:-len(_cfg_compressed_pkg_ext)]

        if include:
            module_list.append(submodule)

        working_path = remaining

    return '.'.join(reversed(module_list))

and produces a return value that looks like: ProComment.src.commands based on the physical path contained in __file__—exactly what the reload() function needs.

Note carefully that src/__init__.py explicitly recursed into the src/commands/ directory like this—after all its modules at that level were reloaded:

reload(subpackage_name + '.commands')  # Recurse into .commands/ subpackage.

This pattern then causes a “cascade” of all the modules being reloaded: when procomment.py gets either loaded (for the first time), reloaded (by Package Control, or saved during development).

The end result of this pattern is:

  • When Sublime Text starts, all Subpackages get loaded via a cascade of import statements.

  • When the Package Control Package updates your Package and reloads the central Plugin (or when you save your central Plugin during development), all the modules in each Subpackage get reloaded via a cascade of reload() calls.

If you would like to verify/trace that visually, you can see the cascade of importing and reloading in the debug output (from the code above) into the Console Panel when Sublime Text is first started. This is when the loading is mostly being done by import statements:

reloading plugin ProComment.procomment
ProComment.procomment  >>> module execution
vvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvv
reload():  >>> dotted_subpkg='ProComment.lib' submodules=()
reload():  <<<
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
vvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvv
reload():  >>> dotted_subpkg='ProComment.src' submodules=()
reload():  <<<
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
ProComment.lib  >>> module execution
vvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvv
reload():  >>> dotted_subpkg='ProComment.lib' submodules=('debug', 'utils')
Reloading(ProComment.lib.debug)
Reloading(ProComment.lib.utils)
reload():  <<<
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
ProComment.lib  <<<
ProComment.src  >>> module execution
vvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvv
reload():  >>> dotted_subpkg='ProComment.src' submodules=('core', 'contexts', 'func_parsing')
reload():  <<<
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
vvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvv
reload():  >>> dotted_subpkg='ProComment.src.commands' submodules=()
reload():  <<<
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  ProComment.src.commands  >>> module execution
vvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvv
reload():  >>> dotted_subpkg='ProComment.src.commands' submodules=('insert_datestamp', 'insert_timestamp', 'insert_comment')
reload():  <<<
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  ProComment.src.commands  <<<
ProComment.src  <<<
ProComment.procomment  <<<

And this is the debug output each time procomment.py is saved during development, or when the Package Control Package updates the package while Sublime Text is running). This is when the loading is being done entirely by the cascade of calls to the reload() function:

reloading plugin ProComment.procomment
ProComment:  Plugin unloaded at 28-Feb-2026 13:26
ProComment.procomment  >>> module execution
vvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvv
reload():  >>> dotted_subpkg='ProComment.lib' submodules=()
Reloading(ProComment.lib)
ProComment.lib  >>> module execution
  reload():  >>> dotted_subpkg='ProComment.lib' submodules=('debug', 'utils')
  Reloading(ProComment.lib.debug)
  Reloading(ProComment.lib.utils)
  reload():  <<<
ProComment.lib  <<<
reload():  <<<
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
vvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvv
reload():  >>> dotted_subpkg='ProComment.src' submodules=()
Reloading(ProComment.src)
ProComment.src  >>> module execution
  reload():  >>> dotted_subpkg='ProComment.src' submodules=('core', 'contexts', 'func_parsing')
  Reloading(ProComment.src.core)
  Reloading(ProComment.src.contexts)
  Reloading(ProComment.src.func_parsing)
  reload():  <<<
  reload():  >>> dotted_subpkg='ProComment.src.commands' submodules=()
  Reloading(ProComment.src.commands)
  ProComment.src.commands  >>> module execution
    reload():  >>> dotted_subpkg='ProComment.src.commands' submodules=('insert_datestamp', 'insert_timestamp', 'insert_comment')
    Reloading(ProComment.src.commands.insert_datestamp)
    Reloading(ProComment.src.commands.insert_timestamp)
    Reloading(ProComment.src.commands.insert_comment)
    reload():  <<<
  ProComment.src.commands  <<<
  reload():  <<<
ProComment.src  <<<
reload():  <<<
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
ProComment.procomment  <<<
Debugging: [0x2002]
  - LOAD_UNLOAD:  0x0002
  - IMPORTING  :  0x2000
ProComment:  Initialized at 28-Feb-2026 13:26.

When you turn debugging output off, and all of the above happens silently and happily in the background.

Note

The lines:

Debugging: [0x2002]
  - LOAD_UNLOAD:  0x0002
  - IMPORTING  :  0x2000

is from the lib/debug.py module as a result of having at least 1 debugging bit turned on at the time the Console output was captured.

The line:

ProComment:  Initialized at 23-Feb-2026 11:53.

at the end is a result of having the LOAD_UNLOAD debugging bit turned on at the time the Console output was captured.

15.6. Troubleshooting Packages

Because Sublime Text is so customizable, it is possible for 3rd-party Packages and/or local customization to interfere with one another, or cause other problems. You might see this, for example, as Python exceptions that don’t make sense in the Console Panel, or certain functionality isn’t behaving as you expect.

15.6.1. Safe Mode

As of Sublime Text 4, you can now launch Sublime Text in Safe Mode by using this option on the command line to launch it.

subl –safe-mode

Sublime Text initially displays this dialog box to describe the run-time environment:

Screenshot showing Safe Mode Announcement

Fig. 15.6.1.1 Screenshot showing Safe Mode Announcement

When launched this way, Sublime Text uses an alternate Data Directory thus disabling all 3rd-party Packages and local customizations, as well as internally not loading any previous sessions (i.e. from any .sublime-workspace files). This will help you to verify whether the behavior is, or is not, coming from Sublime Text itself or one of its shipped Packages.

The alternate Data Directory used is:

Windows:

%APPDATA%/Sublime |nbsp| Text (Safe Mode)/

Windows Portable Version:

path/to/application/Data (Safe Mode)/

Linux:

~/.config/sublime-text-safe-mode/

MacOS:

~/Library/Application Support/Sublime |nbsp| Text (Safe Mode)/

If the behavior is still being exhibited, it is most likely from a corrupted shipped Package file and can be remedied by re-installing Sublime Text.

If the behavior disappears, then you know it was coming from somewhere in the Data Directory.

A nice thing about Safe Mode is that you can install experimental customizations or packages you think might have caused problems, and it will not affect Sublime Text’s ability to start and behave normally because:

  • such packages will be installed in the alternate Data Directory, thus not impacting normal sessions, and

  • each time Sublime Text starts in Safe Mode, it deletes any content in the Safe-Mode Data Directory, so it “doesn’t hurt” if a package installed there did something it wasn’t supposed to.

Caution: don’t store anything important in the Safe-Mode Data Directory!

15.6.2. Diagnosing Trouble from the Data Directory

If you have reached the conclusion that the trouble you are experiencing has come from the Data Directory (3rd-Party Packages and/or local customization), you can discover the source of the problem by following these steps. Knowing when the problem started is also an asset, because the cause will most likely have occurred just before the problem started.

  • Close Sublime Text if it is running.

  • Rename the Data Directory to another name to keep it as a backup and reference about what Packages you installed and what customizations you made.

  • Re-start Sublime Text.

When Sublime Text starts, it will create a fresh new Data Directory.

Note: In subsequent steps, it is recommended to keep the contents of the renamed (backup) of the problematic Data Directory unaltered for sake of preserving the evidence. This is so that if your first attempt at isolating the problem isn’t successful, you can repeat it (using smaller or different steps) as many times as needed until you have isolated the problem.

15.6.2.1. Diagnosis by Isolating Packages

Now you can re-install 3rd-Party Packages you had in place (and were working correctly) well before the problem started. Test to verify whether the problem is occurring after this step.

  • If the problem is not occurring at this point, you can continue to diagnose by re-adding each subsequent Package one by one, until the behavior returns, at which point you will know what Package to exclude or disable.

  • On the other hand, if the problem *is occurring*, you know the problem is somewhere in that group of Packages, and you can take steps to further isolate the source by reverting and repeating this step with only half of the Packages, and keep dividing the problematic group until you have isolated the source.

15.6.2.2. Diagnosis by Isolating Customizations

If you have reached this point and the problem has not returned, now you can add your own customizations back in, one at a time, until the problem resurfaces. If/when you encounter the problem again, you will know where to investigate further to remedy, or, as the case may be, what not to do.

15.7. Further Reading

The more you know about Python’s Import System, the more you will understand some of the power of Sublime Text Packages and their facilities brought to bear by Python itself (e.g. their ability use modular subpackages to cleverly manage Package complexity, and thereby make a Package more understandable, and thereby more maintainable).