{"id":13595866,"url":"https://github.com/amoffat/snake","last_synced_at":"2025-09-27T10:30:41.772Z","repository":{"id":1017597,"uuid":"29103466","full_name":"amoffat/snake","owner":"amoffat","description":"Full Python Scripting in Vim","archived":true,"fork":false,"pushed_at":"2022-03-29T04:11:05.000Z","size":1196,"stargazers_count":1406,"open_issues_count":9,"forks_count":56,"subscribers_count":56,"default_branch":"master","last_synced_at":"2024-09-21T16:41:27.669Z","etag":null,"topics":[],"latest_commit_sha":null,"homepage":"","language":"Python","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":null,"status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/amoffat.png","metadata":{"files":{"readme":"README.md","changelog":"CHANGELOG.md","contributing":null,"funding":null,"license":null,"code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null}},"created_at":"2015-01-11T19:42:17.000Z","updated_at":"2024-05-29T10:51:29.000Z","dependencies_parsed_at":"2022-08-06T10:01:22.707Z","dependency_job_id":null,"html_url":"https://github.com/amoffat/snake","commit_stats":null,"previous_names":[],"tags_count":4,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/amoffat%2Fsnake","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/amoffat%2Fsnake/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/amoffat%2Fsnake/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/amoffat%2Fsnake/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/amoffat","download_url":"https://codeload.github.com/amoffat/snake/tar.gz/refs/heads/master","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":219871971,"owners_count":16554475,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":[],"created_at":"2024-08-01T16:01:59.653Z","updated_at":"2025-09-27T10:30:41.142Z","avatar_url":"https://github.com/amoffat.png","language":"Python","funding_links":[],"categories":["Python"],"sub_categories":[],"readme":"![snake logo](https://github.com/amoffat/snake/blob/master/logo.gif)\n\n[![Build\nStatus](https://travis-ci.org/amoffat/snake.svg?branch=master)](https://travis-ci.org/amoffat/snake)\n\nSnake (SNAAAAAAAAAKKKE) is a Python module for Vim that let's you use Python to\nits fullest extent to enhance Vim.\n\nHere's an example of a helper function, written in Python, using Snake, to\ntoggle the word under your cursor between snake-case and camel-case when you\npress `\u003cleader\u003ec`:\n\n```python\nimport snake\n\n@snake.key_map(\"\u003cleader\u003ec\")\ndef toggle_snake_case_camel_case():\n    \"\"\" take the word under the cursor and toggle it between snake-case and\n    camel-case \"\"\"\n\n    word = snake.get_word()\n\n    # is it snake case?\n    if \"_\" in word:\n        chunks = word.split(\"_\")\n        camel_case = chunks[0] + \"\".join([chunk.capitalize() for chunk in\n            chunks[1:]])\n        snake.replace_word(camel_case)\n\n    # is it camel case?\n    else:\n        # split our word on capital letters followed by non-capital letters\n        chunks = filter(None, re.split(\"([A-Z][^A-Z]*)\", word))\n        snake_case = \"_\".join([chunk.lower() for chunk in chunks])\n        snake.replace_word(snake_case)\n```\n\n![Metal Gear Solid Snake Success](http://i.imgur.com/ZFr3vXG.gif)\n\n## [Full API Reference](docs/api_reference.md)\n\n# Some other cool things you can do\n\n## Bind a function to a key\n\nWhen you press the key pattern in ``key_map``, the decorated function will run.\n\n```python\nimport snake\n\n@snake.key_map(\"\u003cleader\u003er\")\ndef reverse():\n    snake.replace_word(snake.get_word()[::-1])\n```\n\n## Use a function for an abbreviation\n\nA Python function can be expanded dynamically as you type an abbreviation in\ninsert mode.\n\n```python\nimport snake\nimport time\n\nsnake.abbrev(\"curtime\", time.ctime)\n```\n\n## Have a function run for a file type\n\nSometimes it is convenient to run some code when the buffer you open is of a\nspecific file type.\n\n```python\nimport snake\n\n@snake.when_buffer_is(\"python\")\ndef setup_python_folding(ctx):\n    ctx.set_option(\"foldmethod\", \"indent\")\n    ctx.set_option(\"foldnestmax\", 2)\n    ctx.key_map(\"\u003cspace\u003e\", \"za\")\n```\n\nA context object is passed into the function you wrap.  This context allows you\nto set options, let variables, and create abbreviations and keymaps that apply\nonly to the buffer you just opened, not globally.\n\n## Press arbitrary keys as if you typed them\n\n```python\nfrom snake import keys\n\ndef uppercase_second_word():\n    keys(\"gg\") # go to top of file, first character\n    keys(\"w\") # next word\n    keys(\"viw\") # select inner word\n    keys(\"~\") # uppercase it\n```\n\n# Installation\n\nYour Vim version must include [`+python`](http://vimdoc.sourceforge.net/htmldoc/various.html#+python) to use Snake. You can check with `:version`.\n\n## Vundle\n\nAdd the following line to your Vundle plugin block of your `.vimrc`:\n\n```\nPlugin 'amoffat/snake'\n```\n\nAnd the following lines to the end of the file:\n```\nif filereadable(expand(\"~/.vim/bundle/snake/plugin/snake.vim\"))\n    source ~/.vim/bundle/snake/plugin/snake.vim\nendif\n```\n\nRe-source your `.vimrc`.  Then `:PluginInstall`\n\n## Pathogen\n\nTODO\n\n## Neobundle\n\nAdd the following line to your Neobundle plugin block of your `.vimrc`:\n\n```\nNeoBundle 'amoffat/snake'\n```\n\nAnd the following lines to the end of the file:\n```\nif filereadable(expand(\"~/.vim/bundle/snake/plugin/snake.vim\"))\n    source ~/.vim/bundle/snake/plugin/snake.vim\nendif\n```\n\nRe-source your `.vimrc`. Then `NeoBundleInstall`\n\n# Where do I write my Snake code?\n\n`~/.vimrc.py` is intended to be the python equivalent of `~/.vimrc`.  Snake will\nload and evaluate it on startup.  It should contain all of your Snake\ninitialization code and do any imports of other Snake plugins.  If were so\ninclined, you could move all of your vim settings and options into `~/.vimrc.py`\nas well:\n\n```python\nfrom snake import multi_set_option, let, multi_command\n\nmulti_set_option(\n    \"nocompatible\",\n    \"exrc\",\n    \"secure\",\n\n    (\"background\", \"dark\"),\n    (\"textwidth\", 80),\n\n    (\"shiftwidth\", tab),\n    (\"softtabstop\", tab),\n    (\"tabstop\", tab),\n    \"expandtab\",\n)\n\nlet(\"mapleader\", \",\")\n\nmulti_command(\n    \"nohlsearch\",\n    \"syntax on\",\n)\n\nfrom snake.plugins import my_rad_plugin\n```\n\n# Design Philosophy\n\nVim is powerful, but its commands and key-bindings are built for seemingly every\nuse case imaginable.  It doesn't distinguish between commonly-used and\nrarely-used.  Snake should use that existing foundation of functionality to add\nstructure for commonly-needed operations.  For example, many vim users know that\n`yiw` yanks the word you're on into a register.  This is a common operation, and\nso it should be mapped to a simple function:\n\n```python\n@preserve_state()\ndef get_word():\n    keys(\"yiw\")\n    return get_register(\"0\")\n```\n\nNow instead of your plugin containing `execute \"normal! yiw\"`, it can contain\n`word = get_word()`\n\n\n# How do I write a plugin?\n\nIf your plugin is a package, create (or symlink) a directory inside\n`~/.vim/bundle` for your plugin.  Make this directory a Python package by\ncreating a `__init__.py`\n\nIf your plugin is a simple one-file module, just create or symlink that file\ninto your `~/.vim/bundle` directory.\n\nNext Add `from snake.plugins import \u003cyour_plugin\u003e` to `~/.vimrc.py`.  Finally,\nRe-source your `~/.vimrc`\n\nFor plugin API reference, check out [api\\_reference.md](docs/api_reference.md).\n\n# Can I use a virtualenv for my plugin?\n\nYes!  But it's crazy!\n\nJust include a `requirements.txt` file in your package directory that contains\nthe `pip freeze` output of all the dependencies you wish to include.  When your\nmodule is imported from `.vimrc.py`, a virtualenv will be automatically created\nfor your plugin if it does not exist, and your plugin dependencies automatically\ninstalled.\n\nVirtualenvs that are created automatically will use your virtualenv\\_wrapper\n`WORKON_HOME` environment variable, if one exists, otherwise `~/.virtualenvs`.\nAnd virtualenvs take the name `snake_plugin_\u003cyour_plugin_name\u003e`.\n\n## Gotchas\n\nYou may be wondering how snake can allow for different virtualenvs for different\nplugins within a single Python process.  There's a little magic going on, and as\nsuch, there are some gotchas.\n\nWhen a plugin with a virtualenv is imported, it is imported automatically within\nthat plugin's virtualenv.  Then the virtualenv is exited.  This process is\nrepeated for each plugin with a virtualenv.\n\nWhat this means is that all of your plugin's imports *must* occur at your\nplugin's import time:\n\nGOOD:\n```python\nfrom snake import *\nimport requests\n\ndef stuff():\n    return requests.get(\"http://google.com\").text\n```\n\nBAD:\n```python\nfrom snake import *\n\ndef stuff():\n    import requests\n    return requests.get(\"http://google.com\").text\n```\n\nThe difference here is that in the first example, your plugin will have a\nreference to the correct `requests` module, because it was imported while your\nplugin was being imported inside its virtualenv.  In the second example, when\n`stuff()` runs, it is no longer inside of your plugin's virtualenv, so when it\nimports `requests`, it will not get the correct module or any module at all.\n\nThere is also the problem of different plugins having different dependency\nversions.  For example, if Snake plugin `A` depends on `sh==1.10` and plugin `B`\ndepends on `sh==1.11`, whichever plugin gets loaded first in `.vimrc.py` will\nput *their* `sh` module into `sys.modules`.  Then, when the other plugin loads,\nit will attempt to load `sh`, see it is in `sys.modules`, and use that instead,\ninstead of looking in its virtualenv.\n\nAll of this obviously isn't great, and something better needs to be built to\nmore thoroughly separate virtualenvs from under a single Python process.  I\nthink what can happen is, for the `SnakePluginHook`, if a `fullname` has more\nthan 3 paths, drop into the virtualenv for the plugin and run `imp.find_module`.\nIf the module exists, return `self` as the loader.  Repeat the process in\n`load_module` except actually `imp.load_module`.  This way, the dependency\nshould be loaded into `sys.modules` prefixed by the full plugin name\n`snake.plugins.whatever.sh`.\n\n\n# Contributing\n\nRead [development.md](docs/development.md) for technical info.\n\n## Pull requests\n\nAlthough Snake is meant to make Vim more scriptable in Python, it is *not* meant\nto provide all the nuanced functionality of Vim.  PRs for new features will be\nscreened by the value-add of the feature weighed against the complexity added to\nthe api, with favor towards keeping the api simple.\n\n\n## Snake needs a vundle equivalent\n\nI would like to see an import hook that allows this in your `.vimrc.py`:\n\n```python\nfrom snake import *\n\nsomething_awesome = __import__(\"snake.plugins.tpope/something_awesome\")\n```\n\nWhere the import hook checks if the plugin exists in `~/.vim/snake`, and if it\ndoesn't, looks for a repo to clone at\n`https://github.com/tpope/something_awesome`\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Famoffat%2Fsnake","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Famoffat%2Fsnake","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Famoffat%2Fsnake/lists"}