├── .gitignore ├── Default (OSX).sublime-keymap ├── shell-eval.gif ├── Default (Linux).sublime-keymap ├── Default (Windows).sublime-keymap ├── Default.sublime-commands ├── Main.sublime-menu ├── README.md └── evaluate.py /.gitignore: -------------------------------------------------------------------------------- 1 | evaluate.pyc 2 | evaluate.py.bak -------------------------------------------------------------------------------- /Default (OSX).sublime-keymap: -------------------------------------------------------------------------------- 1 | [ 2 | { "keys": ["super+shift+e"], "command": "evaluate" } 3 | ] -------------------------------------------------------------------------------- /shell-eval.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jbrooksuk/Sublime-Evaluate/HEAD/shell-eval.gif -------------------------------------------------------------------------------- /Default (Linux).sublime-keymap: -------------------------------------------------------------------------------- 1 | [ 2 | { "keys": ["ctrl+shift+e"], "command": "evaluate" } 3 | ] -------------------------------------------------------------------------------- /Default (Windows).sublime-keymap: -------------------------------------------------------------------------------- 1 | [ 2 | { "keys": ["ctrl+shift+e"], "command": "evaluate" } 3 | ] -------------------------------------------------------------------------------- /Default.sublime-commands: -------------------------------------------------------------------------------- 1 | [ 2 | { 3 | "caption": "Evaluate: Evaluate Selections", 4 | "command": "evaluate" 5 | } 6 | ] -------------------------------------------------------------------------------- /Main.sublime-menu: -------------------------------------------------------------------------------- 1 | [ 2 | { 3 | "id": "edit", 4 | "children": 5 | [ 6 | {"id": "wrap"}, 7 | {"command": "evaluate"} 8 | ] 9 | } 10 | ] -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Sublime Evaluate [![Gittip](http://badgr.co/gittip/jbrooksuk.png)](https://www.gittip.com/jbrooksuk/) 2 | A powerful plugin which returns the value of selected regions. 3 | 4 | # Evaluation Environment 5 | Sublime Evaluate is able to evaluate the `math` and `datetime` import as part of its enviroment. This opens up access to a whole range of Python functions such as: 6 | 7 | ```python 8 | math.atan2(80, 40) 9 | 10 | math.pi * 60 11 | 12 | datetime.date(2013,4,2) # Returns a formatted date 13 | 14 | datetime.date.today() 15 | ``` 16 | 17 | We can also perform morecomplex expressions such as: 18 | 19 | ```python 20 | (math.pi * 2) / math.pi * 0.5 21 | ``` 22 | 23 | # Evaluate Shell Script 24 | Sublime Evaluate also supports evaluating code as shell script, Just add a `!` at the beginning: 25 | 26 | ```bash 27 | ! echo hello # > hello 28 | 29 | ! ! true; echo $? # > 1 30 | 31 | # Even multilines 32 | ! for i in `seq 10`; do 33 | echo I got a $i 34 | done 35 | 36 | ``` 37 | 38 | #### Current Working Dir 39 | Current working dir is the Sublime install dir. You can get it with a `!pwd`. 40 | 41 | #### Stderr 42 | Stderr is captured as well as stdout. 43 | 44 | #### Timeout 45 | Currently a 5s timeout is set. You code will be terminated when it runs more than 5s. 46 | 47 | #### OS/Sublime Support 48 | Though the it is supposed to work on all OSs / Sublime 2/3, this feature is tested on macOS + Sublime 3. 49 | 50 | Please kindly report to us if you find an issue. We'd be happy to fix it. 51 | 52 | #### Demo 53 | ![](shell-eval.gif) 54 | 55 | # License 56 | MIT - [http://jbrooksuk.mit-license.org](http://jbrooksuk.mit-license.org) 57 | -------------------------------------------------------------------------------- /evaluate.py: -------------------------------------------------------------------------------- 1 | from __future__ import division 2 | import sublime 3 | import sublime_plugin 4 | import threading 5 | import math 6 | import datetime 7 | import subprocess 8 | 9 | sublime_version = 2 10 | 11 | if not sublime.version() or int(sublime.version()) > 3000: 12 | sublime_version = 3 13 | 14 | class EvaluateCommand(sublime_plugin.TextCommand): 15 | def run(self, edit): 16 | sels = self.view.sel() 17 | 18 | threads = [] 19 | for sel in sels: 20 | to_eval = self.view.substr(sel) 21 | thread = EvaluateCall(sel, to_eval, 5) 22 | threads.append(thread) 23 | thread.start() 24 | 25 | self.view.sel().clear() 26 | if sublime_version == 2: 27 | edit = self.view.begin_edit('evaluate') 28 | 29 | self.handle_threads(edit, threads) 30 | 31 | if sublime_version == 2: 32 | self.view.end_edit(edit) 33 | 34 | def handle_threads(self, edit, threads, offset=0): 35 | for t in threads: 36 | t.join(5) # TODO: use bulk join maybe 37 | offset = self.replace(edit, t, offset) 38 | 39 | selections = len(self.view.sel()) 40 | sublime.status_message('Evaluated %s selection%s!' % (selections, '' if selections == 1 else 's')) 41 | 42 | def replace(self, edit, thread, offset): 43 | sel = thread.sel 44 | original = thread.original 45 | result = thread.result 46 | 47 | if offset: 48 | sel = sublime.Region(sel.begin() + offset, sel.end() + offset) 49 | 50 | prefix = original 51 | main = str(result) 52 | self.view.replace(edit, sel, main) 53 | 54 | end_point = (sel.begin() + len(prefix) + len(main)) - len(original) 55 | self.view.sel().add(sublime.Region(end_point, end_point)) 56 | 57 | return offset + len(main) - len(original) 58 | 59 | 60 | class EvaluateCall(threading.Thread): 61 | def __init__(self, sel, to_eval, timeout): 62 | self.sel = sel 63 | self.original = to_eval 64 | self.timeout = timeout 65 | self.result = self.original # Default result 66 | threading.Thread.__init__(self) 67 | 68 | def run(self): 69 | '''Evaluate `self.original`, save to `self.result`. 70 | If `self.timeout` reached, the run fails and nothing change. 71 | 72 | If `self.original` starts with '!', after trimming leading spaces, 73 | it is evaluated as Shell code. (The same convention as ipython). 74 | 75 | Otherwise, it is evaluated as Python code.''' 76 | 77 | if self.original.lstrip().startswith('!'): 78 | # Remove the first bang 79 | shell_code = self.original.lstrip()[1:] 80 | try: 81 | p = subprocess.Popen(shell_code, 82 | shell=True, 83 | stdout=subprocess.PIPE, 84 | stderr=subprocess.STDOUT) 85 | # stderr goes to stdout 86 | out, _ = p.communicate(timeout=self.timeout) 87 | # ad-hoc Python 2/3 str/bytes handling 88 | if type(out) == bytes: 89 | out = out.decode() 90 | 91 | # Remove the last newline 92 | if out.endswith('\n'): 93 | out = out[:-1] 94 | 95 | self.result = out 96 | except (Exception): 97 | # TODO: print error to console 98 | pass 99 | else: 100 | try: 101 | tmp_global = { 102 | "math": math, 103 | "datetime": datetime 104 | } 105 | code = compile(self.original, '', 'eval') 106 | self.result = eval(code, tmp_global) 107 | except (ValueError, SyntaxError): 108 | pass 109 | --------------------------------------------------------------------------------