Sublime Blog

Sublime Text IDE

June 09, 2021

The Sublime Text editor announced its major v4 release recently, many years after v3. In the last post about low latency experiences I thought I would look into how Sublime does as an IDE for Python or Rust.

VSCode and Intellij (generic Intellij or Clion for Rust and Pycharm for Python) are the main IDE tools for these languages. Sublime text, like Vim, provides a joyfully fast and low latency experience and is very good at setting up efficient movement and text slicing/dicing short cuts.

My experience is that Sublime does fine in place of Intellij or VSCode but there is more boilerplate to setup a project that I can well imagine people not wanting to learn about when something more convenient is available. Sublime does not have a good debugger for Python or Rust. The sublime debugger package is a work in progress experience, at least on Windows. It is setup much like VSCode is for debugging, so VSCode’s debugging documentation will apply. For now, I’d be happy to jump into Pycharm or VSCode on the rare occasions I want to debug.

Inlay hints is a nice to have that VSCode and Intellij provide for Rust but is missing from the current version of the Sublime LSP package. It’s currently not standardised in the LSP spec.

Finally, Intellij/PyCharm has a wide array of refactorings. VSCode and Sublime can certainly highlight and then rename symbols and VSCode has the minor refactorings “extract method/variable”. Things like the “Move” refactor could be worth firing up Intellij/PyCharm for when doing large scale refactoring.

Typical Python Project

  • Black (code formatter)
  • Mypy (type checker)
  • Flake8 (linter)
  • Pytest
  • Virtualenv (via Pipenv to isolate project dependencies from other projects)

For VSCode we need to install the Python extension and then for each project the .vscode/settings.json looks something like the following settings, after which it’s all good to go:

{
    "python.pythonPath": "C:\\Users\\MyUser\\.virtualenvs\\My-Project-6GvSyrI3\\Scripts\\python.exe",
    "python.linting.pylintEnabled": false,
    "python.formatting.provider": "black",
    "python.testing.pytestEnabled": true,
    "python.linting.flake8Enabled": true,
    "python.linting.mypyEnabled": true,
    "python.testing.pytestArgs": [
        "tests"
    ],
    "python.testing.unittestEnabled": false,
    "python.testing.nosetestsEnabled": false
}

For Sublime there are also somethings we need to do once (package setup) and other steps that are per-project. Use package control to get these packages:

I still tend to run tests using pytest in the terminal as using Cmder integrated with Windows Terminal is nice. On Linux it’s easy to manage terminals and extra windows using the i3 window manager. There is a pytest runner/viewer on package control though.

I have limited global LSP preferences (command: Preferences LSP Settings) that are agnostic of the particular LSP server:

"lsp_format_on_save": true

The lsp_format_on_save setting is only revelant to Rust projects as the Pyright LSP server does not do formatting yet. We use the sublack package for that instead. The sublack preferences cannot be reached via the command pallette so use menu “Preferences -> Package Settings -> sublack” and set it to format on save:

{
    "black_on_save": true
}

For sublime linter customise settings if desired (command -> “Preferences: Sublime Linter Settings”).

We use the flake8 linter and mypy linter. Our Python LSP server of choice, Pyright, does not have builtin mypy/flake8 support. There is a different Python LSP server with flake8 support, but it is based on Jedi/Rope and I’ve had a better experience with the Microsoft Pyright typechecker which is used in Pylance and the VSCode Python extension. I’ve also used the Anaconda sublime package for Python (and Rust) extensively. This supports the mypy typechecker and flake8 directly. It also uses Jedi for completions and again, I think Pyright is a better experience. Additionally, tools that are based around the LSP make it easy for multiple editors to benefit from a single LSP server being updated, so seem to have more work on them. Maybe there will be a Pylance LSP server that sublime can use at some point. The global default config for these linters should be fine. We don’t worry about a global installation of mypy/flake8 as we are normally creating projects and using specific versions in that project for build reproducibility.

Python Project Specific Setup

A typical sublime project file (use the command pallete or menu to create the project then edit it) now looks something like this:

{
    "folders":
    [
        {
            "path": ".",
        }
    ],
    "settings":
    {
        "python_interpreter": "C:/Users/MyUser/.virtualenvs/My-Project-6GvSyrI3/Scripts/python.exe",
        "SublimeLinter.linters.flake8.python": "C:/Users/MyUser/.virtualenvs/My-Project-6GvSyrI3/Scripts/python.exe",
        "SublimeLinter.linters.mypy.python": "C:/Users/MyUser/.virtualenvs/My-Project-6GvSyrI3/Scripts/python.exe",
        "PyTest": {
            "pytest": "C:/Users/MyUser/.virtualenvs/My-Project-6GvSyrI3/Scripts/pytest",
            "mode": "manual"
        },
        "sublack.black_command": "C:/Users/MyUser/.virtualenvs/My-Project-6GvSyrI3/Scripts/black"
    }
}

The LSP pyright server can be enabled globally or per project from the command pallette. The awkward part of using Pyright is that a config file is required when it is used inside a virtualenv, which is almost always the case, so in the project root we have pyrightconfig.json file that again needs the virtualenv path.

{
    "venvPath": "C:/Users/MyUser/.virtualenvs/",
    "venv": "My-Project-6GvSyrI3",
    "pythonVersion": "3.8",
    "exclude": [
        "**/node_modules",
        "**/__pycache__",
        "**/.pytest_cache",
        "**/.mypy_cache",
        "**/.hypothesis"
    ],
    "useLibraryCodeForTypes": true,
    "typeCheckingMode": "off"
}

I prefer to leave most of the type warnings to mypy, in part because it’s used in project continous integration builds, but you can put the typeCheckingMode to “on” - see pyright configuration.

Typical Rust Project

Get the Rust Enhanced sublime package from package control. It’s fine without customisation.

Use rustup to get the rust source code (probably already installed with rust) - rustup component add rust-src.

The rest is straight forward, follow the sublime LSP instructions for Rust.

In the sublime “Preferences: LSP Settings”, in addition to the "lsp_format_on_save": true option mentioned above we add

    "clients": {
        "rust-analyzer": {
            "enabled": true,
            "command": ["rust-analyzer"],
            "selector": "source.rust"
        }
    }

The rust-analyzer.exe binary (or just rust-analyzer on Linux) needs manually updating. UPDATE: the LSP rust analyzer sublime package now handles this.

Rust Project Specific Setup

With those global settings there shouldn’t be need for project specific settings. The server provides formatting on save and linting.

LSP Keyboard Shortcuts

Some shortcuts will make the LSP features easier to use.

{"keys": ["ctrl+6"], "command": "lsp_symbol_references", "context": [{"key": "setting.lsp_active"}]},
{"keys": ["ctrl+6"], "command": "hide_panel", "args": {"cancel": true}, "context": [{"key": "panel_visible", "operator": "equal", "operand": true }]},
{"keys": ["ctrl+shift+6"], "command": "lsp_code_lens", "context": [{"key": "setting.lsp_active"}]},
{"keys": ["ctrl+7"], "command": "lsp_symbol_implementation", "context": [{"key": "setting.lsp_active"}]},
{"keys": ["ctrl+8"], "command": "lsp_symbol_type_definition", "context": [{"key": "setting.lsp_active"}]},
{"keys": ["ctrl+9"], "command": "lsp_symbol_declaration", "context": [{"key": "setting.lsp_active"}]},
{"keys": ["ctrl+n"], "command": "lsp_hover", "context": [{"key": "setting.lsp_active"}]},
{"keys": ["ctrl+n"], "command": "hide_popup", "context": [{ "key": "popup_visible", "operator": "equal", "operand": true}]},
{"keys": ["ctrl+alt+n"], "command": "new_file"},
{"keys": ["ctrl+r"], "command": "lsp_document_symbols", "context": [{"key": "setting.lsp_active"}]},
{"keys": ["ctrl+shift+r"], "command": "lsp_workspace_symbols", "context": [{"key": "setting.lsp_active"}]},
{"keys": ["ctrl+,"], "command": "lsp_symbol_definition", "context": [{"key": "setting.lsp_active"}]},
{"keys": ["ctrl+."], "command": "jump_back"},

We use LSP for goto commands instead of the builtin goto features. These shortcuts also put the new_file command on a different key so lsp_hover is easily available (which is like quick documentation and more). Adjust for your setup.

Overall

Definitely a bit of a hassle to setup, but nice once done. All customisations/settings for the dev environment and editor should probably be under source control or saved somehow such as with the Sync Settings Sublime package.

Over the years I’ve seen a number of websites suggesting which Sublime packages to use that make the editor more like an IDE. My current large list is:

[
    "A File Icon",
    "Advanced CSV",
    "AdvancedNewFile",
    "AlignTab",
    "ApacheConf",
    "AutoFileName",
    "AutoHotkey",
    "BracketHighlighter",
    "Browser Refresh",
    "Case Conversion",
    "ColorPicker",
    "Colorsublime",
    "ConvertToUTF8",
    "CSS Unminifier",
    "CUDA C++",
    "Debugger",
    "Dockerfile Syntax Highlighting",
    "EasyDiff",
    "EditorConfig",
    "Elixir",
    "Expand Selection to Whitespace",
    "F#",
    "FileBrowser",
    "FindKeyConflicts",
    "GDScript (Godot Engine)",
    "GitGutter",
    "GitHub Flavored Markdown Preview",
    "Gradle_Language",
    "Hasher",
    "HexViewer",
    "Highlight Build Errors",
    "HTML-CSS-JS Prettify",
    "i3 wm",
    "INI",
    "Inno Setup",
    "iOpener",
    "Jinja2",
    "JSX",
    "Julia",
    "Load file to REPL",
    "LSP",
    "LSP-json",
    "LSP-pyright",
    "LSP-rust-analyzer",
    "Markdown Extended",
    "MarkdownEditing",
    "MarkdownPreview",
    "Materialize",
    "Modific",
    "Monokai Extended",
    "MultiEditUtils",
    "NASM x86 Assembly",
    "nginx",
    "Package Control",
    "Path Tools",
    "PowerShell",
    "Protocol Buffer Syntax",
    "Puppet",
    "PureScript",
    "PyTest",
    "Python 3",
    "Random Everything",
    "requirementstxt",
    "Rust Enhanced",
    "Sass",
    "Select Quoted",
    "SelectUntil",
    "Shell Turtlestein",
    "SideBarEnhancements",
    "sublack",
    "SublimeLinter",
    "SublimeLinter-contrib-mypy",
    "SublimeLinter-flake8",
    "SublimeREPL",
    "Sync Settings",
    "Terminal",
    "TOML",
    "Trimmer",
    "TypeScript Syntax",
    "UnicodeMath",
    "Wrap Plus",
    "XSL",
]

Lots of those are just syntax highlighting for different languages.

These days there’s a youtube channel OdatNurd - Sublime Text Tutorials, not just articles, to help you get the right personal setup. I have to say, it’s definitely easier than Emacs (and presumably Vim, though modal editing seems more the challenge than the configuration). I spent 5 years using the text editor or operating system that is Emacs and read most of the entire Emacs Wiki at one point. The amount of customisation and configuration code felt too much eventually and Emacs was (still is?) single threaded, which can make for some annoying hangs. There maybe some out of the box emacs “distros” that require little configuration, but I’ve long since stopped looking.


Notes from a software engineer with two decades working in various industries - games, poker and gambling, music streaming and telecommunications. Likes fast code and functional programming. Based in the UK.

github mark
© 2024