├── hackertyper.sublime-commands ├── .gitignore ├── LICENSE ├── hackertyper.py └── README.md /hackertyper.sublime-commands: -------------------------------------------------------------------------------- 1 | [ 2 | { "caption": "HackerTyper: Enable", "command": "hacker_typer", "args": { "enable": true } }, 3 | { "caption": "HackerTyper: Disable", "command": "hacker_typer", "args": { "enable": false } } 4 | ] -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Byte-compiled / optimized / DLL files 2 | __pycache__/ 3 | *.py[cod] 4 | 5 | # C extensions 6 | *.so 7 | 8 | # Distribution / packaging 9 | .Python 10 | env/ 11 | bin/ 12 | build/ 13 | develop-eggs/ 14 | dist/ 15 | eggs/ 16 | lib/ 17 | lib64/ 18 | parts/ 19 | sdist/ 20 | var/ 21 | *.egg-info/ 22 | .installed.cfg 23 | *.egg 24 | 25 | # Installer logs 26 | pip-log.txt 27 | pip-delete-this-directory.txt 28 | 29 | # Unit test / coverage reports 30 | htmlcov/ 31 | .tox/ 32 | .coverage 33 | .cache 34 | nosetests.xml 35 | coverage.xml 36 | 37 | # Translations 38 | *.mo 39 | 40 | # Mr Developer 41 | .mr.developer.cfg 42 | .project 43 | .pydevproject 44 | 45 | # Rope 46 | .ropeproject 47 | 48 | # Django stuff: 49 | *.log 50 | *.pot 51 | 52 | # Sphinx documentation 53 | docs/_build/ 54 | 55 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2014 Espen Hovlandsdal 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /hackertyper.py: -------------------------------------------------------------------------------- 1 | import os.path 2 | import sublime 3 | import sublime_plugin 4 | 5 | hacker_enabled = False 6 | 7 | 8 | class HackerTyperCommand(sublime_plugin.TextCommand): 9 | 10 | def run(self, edit, enable=False, content=False): 11 | global hacker_enabled 12 | hacker_enabled = enable 13 | 14 | if content is False: 15 | return 16 | 17 | # Replace contents 18 | self.view.replace(edit, sublime.Region(0, len(content)), content) 19 | 20 | 21 | class HackerTyper(sublime_plugin.EventListener): 22 | solution_exists = False 23 | hacker_buffer = "" 24 | 25 | def on_activated(self, view): 26 | # Don't check for solution files if the plugin is disabled 27 | if hacker_enabled is False: 28 | return 29 | 30 | # Check if the current file has a solution 31 | filename = view.file_name() 32 | if filename is None: 33 | return 34 | 35 | solution = filename + ".hackertyper" 36 | self.solution_exists = os.path.isfile(solution) 37 | 38 | # Give a feedback message if no solution was found 39 | # Clear the status bar if one was found 40 | if not self.solution_exists: 41 | err = "HackerTyper Error: " + os.path.basename(filename) 42 | err += ".hackertyper not found" 43 | return sublime.status_message(err) 44 | else: 45 | sublime.status_message("") 46 | 47 | # Read the entire solution text 48 | self.hacker_buffer = open(solution, encoding='utf-8').read() 49 | 50 | def on_modified_async(self, view): 51 | global hacker_enabled 52 | 53 | if hacker_enabled is False or self.solution_exists is False: 54 | return 55 | 56 | # Fetch correct part of the buffer 57 | bufSize = view.size() 58 | 59 | # Fall back if we're outrunning the original solution 60 | if bufSize > len(self.hacker_buffer): 61 | return 62 | 63 | newBuf = self.hacker_buffer[:bufSize] 64 | 65 | view.run_command("hacker_typer", {"enable": True, "content": newBuf}) 66 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # sublime-hacker-typer 2 | 3 | > [!NOTE] 4 | > Archived. No longer being maintained. 5 | 6 | Pretend you're an expert hacker and can type flawlessly. 7 | Inspired by http://hackertyper.com/ 8 | 9 | ## Summary 10 | 11 | Basically, the plugin (when enabled) looks for a file with the same name as the one you are editing, with an additional ".hackertyper"-extension. 12 | 13 | When it finds this file, it treats it as the "solution" for the file. Whenever you type something, it will try to read the same number of characters from the solution file and replace whatever you typed. 14 | 15 | ## Why!? 16 | 17 | Partly because it was fun. Partly because it can be useful when "live coding" something. Usually, when you're doing a talk and you're a little busy trying to make sense to your audience, you tend to do a lot of typos and silly mistakes. I still think it's a great way to engage the audience - but I'd rather skip all the mistakes ;-) 18 | 19 | ## Usage 20 | 21 | 1. Install the package through [Sublime Package Control](https://sublime.wbond.net/). Search for HackerTyper. 22 | 2. Enable the plugin through the command palette (Shift+Ctrl+P). "HackerTyper: Enable". 23 | 3. Create solution files alongside the files you want to pretend you're writing. So, to create an `index.html`-file based on a solution, create a `index.html.hackertyper`-file with the content you want to be typed out. 24 | 4. Open `index.html` and start writing. 25 | 26 | Protip: You might want to include `"file_exclude_patterns": ["*.hackertyper"]` in your project settings or user preferences to prevent the solution files from showing up in the sidebar etc. 27 | 28 | 29 | ## Issues 30 | 31 | - It operates on length of the file instead of characters pressed. This means if you press enter and sublime would insert some tabs/spaces, it will add as many characters. This doesn't match up to your keypresses and seems weird. There does not seem to be a "key down" event in Sublime, however, which would have fixed this. 32 | 33 | - The only reliable way of seeing if content has been added/removed seems to be the "modified" event. Since we cannot edit in an eventlistener, we need to run a command. This command changes the content of the editor, which triggers a modified event, creating a recursion loop. It is eventually stopped because of a max recursion depth limit, but this is obviously unwanted behaviour. Not sure how to work around this. 34 | 35 | ## License 36 | 37 | MIT-licensed. See LICENSE. 38 | --------------------------------------------------------------------------------