From 0407fb4fdb2d177a7d9f19d9e60c2a51ba9640ed Mon Sep 17 00:00:00 2001 From: Aleh Kashnikau Date: Sun, 6 Sep 2015 16:56:38 +0300 Subject: [PATCH] moved padawan.php to composer global package --- .gitmodules | 3 - Padawan.sublime-settings | 2 +- README.md | 29 ++-- install.sh | 8 - padawan.php | 1 - padawan.py | 347 ++++++++++++++++++++++----------------- plugin.py | 15 +- 7 files changed, 221 insertions(+), 184 deletions(-) delete mode 100644 .gitmodules delete mode 100755 install.sh delete mode 160000 padawan.php diff --git a/.gitmodules b/.gitmodules deleted file mode 100644 index f125811..0000000 --- a/.gitmodules +++ /dev/null @@ -1,3 +0,0 @@ -[submodule "padawan.php"] - path = padawan.php - url = https://github.com/mkusher/padawan.php.git diff --git a/Padawan.sublime-settings b/Padawan.sublime-settings index 5f343d1..126dfa8 100644 --- a/Padawan.sublime-settings +++ b/Padawan.sublime-settings @@ -1,4 +1,4 @@ { - "padawan_composer_command": "composer", + "padawan_composer": "composer", "padawan_timeout": 0.5 } \ No newline at end of file diff --git a/README.md b/README.md index 5b21f06..3dce04a 100644 --- a/README.md +++ b/README.md @@ -10,36 +10,38 @@ This plugin includes: - Commands for index generation and index saving - Commands for starting, stopping and restarting the server +# Installation + +1. Make sure you've installed [padawan.php server]( +https://github.com/mkusher/padawan.php#how-to-use) +2. Install this plugin via [Package control](https://packagecontrol.io) +or clone this repo to `/path/to/your/sublime-text-3/Packages/` + # Demo video Click the image below to watch a short video on what Padawan.sublime can already do. -[![ScreenShot](http://i1.ytimg.com/vi/qpLJD24DYcU/maxresdefault.jpg)](https://www.youtube.com/watch?v=qpLJD24DYcU) +[![ScreenShot](http://i1.ytimg.com/vi/qpLJD24DYcU/maxresdefault.jpg) +](https://www.youtube.com/watch?v=qpLJD24DYcU) # Requirements Padawan.php requires PHP 5.5+ -# Installation - -Clone this repo to `/path/to/your/sublime-text-3/Packages/` -and then run `sh install.sh` - # Running To get smart autocompletion all you need to do is the following easy steps: -1. Install padawan.sublime plugin -2. Open your php composer project -3. Run the `Padawan: Generate Index` from the command palette -4. Run the `Padawan: Start Server` from the command palette after index +1. Open your php composer project +2. Run the `Padawan: Generate Index` from the command palette +3. Run the `Padawan: Start Server` from the command palette after index generation has stopped -5. Enjoy smart completion +4. Enjoy smart completion # Plugins(Extensions) -You can extend Padawan.php by installing different plugins. +You can extend Padawan.php by installing plugins. See [Plugins List](https://github.com/mkusher/padawan.php/wiki/Plugins-list) for more info. @@ -49,4 +51,5 @@ Use `Padawan: Add plugin` and type plugin name, for example `mkusher/padawan-di` ## Removing -Use `Padawan: Remove plugin` and choose one of the installed plugin from list +Use `Padawan: Remove plugin` and choose one of the installed plugin from +the list. diff --git a/install.sh b/install.sh deleted file mode 100755 index 775a8bb..0000000 --- a/install.sh +++ /dev/null @@ -1,8 +0,0 @@ -#!/usr/bin/env bash - -DIR=$( cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd ) -cd $DIR -git submodule update --init --recursive -php -r "readfile('https://getcomposer.org/installer');" | php -cd $DIR/padawan.php -$DIR/composer.phar install diff --git a/padawan.php b/padawan.php deleted file mode 160000 index 181b45b..0000000 --- a/padawan.php +++ /dev/null @@ -1 +0,0 @@ -Subproject commit 181b45b37d01c44056247a55e124233a2f44cb2d diff --git a/padawan.py b/padawan.py index c8a1145..1ea768e 100644 --- a/padawan.py +++ b/padawan.py @@ -5,68 +5,96 @@ import subprocess import re -settings = sublime.load_settings("Padawan.sublime-settings") + +def get_setting(name, default=None): + project_data = sublime.active_window().project_data() + + if (project_data and 'padawan' in project_data and + name in project_data['padawan']): + return project_data['padawan'][name] + + return sublime.load_settings('Padawan.sublime-settings').get(name, default) + + server_addr = "http://127.0.0.1:15155" -timeout = 0.5 -padawanPath = path.dirname(__file__) -composer = settings.get("padawan_composer") -if not composer: - composer = 'php ' + path.join(padawanPath, 'composer.phar') -server_path = path.join(padawanPath, 'padawan.php') +cli = 'padawan' +server_command = 'padawan-server' -class IndexGenerator: +class Server: - def Generate(self, view, projectRoot): - generatorCommand = server_path + '/bin/cli' - stream = subprocess.Popen( - 'cd ' + projectRoot + ' && ' + generatorCommand + ' generate', + def start(self): + subprocess.Popen( + server_command, shell=True, stdout=subprocess.PIPE, stderr=subprocess.STDOUT ) - self.Notify(view, stream) - - def Notify(self, view, stream): - retcode = stream.poll() - if retcode is not None: - self.drawBars(view, 100) - return - line = stream.stdout.readline().decode('ascii') - errorMatch = re.search('Error: (.*)', line) - if errorMatch is not None: - retcode = 1 - print(errorMatch.group(1).replace("'", "''")) - return - - generator = self - def Notifier(): - return generator.Notify(view, stream) + def stop(self): + try: + self.sendRequest('kill', {}) + return True + except Exception: + return False + + def restart(self): + if self.stop(): + self.start() + + def sendRequest(self, command, params, data=''): + timeout = get_setting("padawan_timeout", 0.5) + addr = server_addr + "/"+command+"?" + urllib.parse.urlencode(params) + response = urllib.request.urlopen( + addr, + data.encode("utf8"), + timeout + ) + result = json.loads(response.read().decode("utf8")) + if "error" in result: + raise ValueError(result["error"]) + return result + - match = re.search('Progress: ([0-9]+)', line) - if not match or match is None: - sublime.set_timeout(Notifier, 0.005) +class Editor: - progress = int(match.group(1)) - self.drawBars(view, progress) + def getView(self): + return sublime.active_window().active_view() - sublime.set_timeout(Notifier, 0.005) + def log(self, message): + print(message) - def drawBars(self, view, progress): + def notify(self, message): + self.getView().set_status("PadawanStatus", message) + + def progress(self, progress): bars = int(progress / 5) - barsStr = '' + bars_str = '' for i in range(20): if i < bars: - barsStr += '=' + bars_str += '=' else: - barsStr += ' ' - barsStr = '[' + barsStr + ']' + bars_str += ' ' + bars_str = '[' + bars_str + ']' + message = "Progress {0} {1}%".format(bars_str, str(progress)) + + self.getView().set_status("PadawanProgress", message) + return + + def error(self, error): + self.notify(error) - view.set_status( - "PadawanIndexGeneration", - "Progress "+barsStr+' '+str(progress)+"%" - ) + def callAfter(self, timeout, callback): + def Notifier(): + if callback(): + sublime.set_timeout(Notifier, timeout) + sublime.set_timeout(Notifier, timeout) + +server = Server() +editor = Editor() +pathError = '''padawan command is not found in your $PATH. Please\ + make sure you installed padawan.php package and\ + configured your $PATH''' class PadawanClient: @@ -75,11 +103,11 @@ def GetCompletion(self, filepath, line_num, column_num, contents): curPath = self.GetProjectRoot(filepath) params = { - 'filepath': filepath.replace(curPath, ""), - 'line': line_num, - 'column': column_num, - 'path': curPath - } + 'filepath': filepath.replace(curPath, ""), + 'line': line_num, + 'column': column_num, + 'path': curPath + } result = self.DoRequest('complete', params, contents) if not result: @@ -92,133 +120,146 @@ def SaveIndex(self, filepath): def DoRequest(self, command, params, data=''): try: - return self.SendRequest(command, params, data) + return server.sendRequest(command, params, data) except urllib.request.URLError: - sublime.status_message("Padawan is not running") + editor.error("Padawan.php is not running") except Exception as e: - print('Error occured {0}'.format(e)) + editor.error("Error occured {0}".format(e.message)) return False - def SendRequest(self, command, params, data=''): - addr = server_addr + "/"+command+"?" + urllib.parse.urlencode(params) - response = urllib.request.urlopen( - addr, - data.encode("utf8"), - timeout - ) - completions = json.loads(response.read().decode("utf8")) - if "error" in completions: - raise ValueError(completions["error"]) - return completions + def AddPlugin(self, plugin): + composer = get_setting("padawan_composer", "composer") + composerCommand = composer + ' global require ' - def StartServer(self): - command = '{0}/bin/server.php > {0}/../logs/server.log'.format( - server_path - ) - subprocess.Popen( - command, - shell=True, - stdout=subprocess.PIPE, - stderr=subprocess.STDOUT - ) - - def StopServer(self): - try: - self.SendRequest('kill', {}) - return True - except Exception: - return False - - def RestartServer(self): - if self.StopServer(): - self.StartServer() - - def AddPlugin(self, view, plugin): - composerCommand = composer + ' require ' - generatorCommand = server_path + '/bin/cli' - - view.set_status("PadawanPlugin", "Started plugin installation") - command = 'cd {0} && {1} {3} && {2} plugin add {3}'.format( - self.PadawanPHPPath(), - composerCommand, - generatorCommand, - plugin - ) + command = '{0} {2} && {1} plugin add {2}'.format( + composerCommand, + cli, + plugin + ) stream = subprocess.Popen( - command, - shell=True, - stdout=subprocess.PIPE, - stderr=subprocess.STDOUT - ) - client = self + command, + shell=True, + stdout=subprocess.PIPE, + stderr=subprocess.STDOUT + ) - def Notifier(): + def OnAdd(retcode): + if not retcode: + server.restart() + editor.notify("Plugin installed") + else: + if retcode == 127: + editor.error(pathError) + editor.error("Plugin installation failed") + + def LogAdding(): retcode = stream.poll() + if retcode is not None: + return OnAdd(retcode) - line = stream.stdout.readline() - print(line) + line = stream.stdout.readline().decode("ascii") + editor.log(line) + return True + editor.callAfter(1e-4, LogAdding) - if retcode is None: - sublime.set_timeout(Notifier, 0.05) - return + def RemovePlugin(self, plugin): + composer = get_setting("padawan_composer", "composer") + composerCommand = composer + ' global remove' - if not retcode: - client.RestartServer() - view.set_status("PadawanPlugin", "Plugin installed") - else: - view.set_status("PadawanPlugin", "Plugin installation failed") + command = '{0} {1}'.format( + composerCommand, + plugin + ) - sublime.set_timeout(Notifier, 0.05) + stream = subprocess.Popen( + command, + shell=True, + stdout=subprocess.PIPE, + stderr=subprocess.STDOUT + ) + + def onRemoved(): + subprocess.Popen( + '{0}'.format( + cli + ' plugin remove ' + plugin + ), + shell=True, + stdout=subprocess.PIPE, + stderr=subprocess.STDOUT + ).wait() + self.RestartServer() + return editor.notify("Plugin removed") + + def LogRemoving(): + retcode = stream.poll() + if retcode is not None: + return onRemoved() - def GetInstalledPlugins(self): - plugins_path = path.join(server_path, "plugins.json") - if not path.exists(plugins_path): - return [] - return json.load(open(plugins_path, 'r')) + line = stream.stdout.readline().decode("ascii") + editor.log(line) + return True - def RemovePlugin(self, view, plugin): - composerCommand = composer + ' remove' - generatorCommand = server_path + '/bin/cli' + editor.callAfter(1e-4, LogRemoving) - command = 'cd {0} && {1} {2}'.format( - self.PadawanPHPPath(), - composerCommand, - plugin - ) + def Generate(self, filepath): + curPath = self.GetProjectRoot(filepath) + stream = subprocess.Popen( + 'cd ' + curPath + ' && ' + cli + ' generate', + shell=True, + stdout=subprocess.PIPE, + stderr=subprocess.STDOUT + ) + + def onGenerationEnd(retcode): + if retcode > 0: + if retcode == 127: + editor.error(pathError) + else: + editor.error("Error occured, code: {0}".format( + str(retcode) + )) + return + server.restart() + editor.progress(100) + editor.notify("Index generated") - subprocess.Popen( - command, - shell=True, - stdout=subprocess.PIPE, - stderr=subprocess.STDOUT - ) + def ProcessGenerationPoll(): + retcode = stream.poll() + if retcode is not None: + return onGenerationEnd(retcode) + line = stream.stdout.readline().decode("utf8") + errorMatch = re.search('Error: (.*)', line) + if errorMatch is not None: + retcode = 1 + editor.error("{0}".format( + errorMatch.group(1).replace("'", "''") + )) + return + match = re.search('Progress: ([0-9]+)', line) + if match is None: + return True + progress = int(match.group(1)) + editor.progress(progress) + return True - subprocess.Popen( - 'cd {0} && {1}'.format( - self.PadawanPHPPath(), - generatorCommand + ' plugin remove ' + plugin - ), - shell=True, - stdout=subprocess.PIPE, - stderr=subprocess.STDOUT - ).wait() + editor.callAfter(1e-4, ProcessGenerationPoll) - self.RestartServer() - view.set_status("PadawanPlugin", "Plugin removed") + def StartServer(self): + server.start() - def Generate(self, filepath, view): - curPath = self.GetProjectRoot(filepath) - generator = IndexGenerator() - generator.Generate(view, curPath) - self.RestartServer() + def StopServer(self): + server.stop() + + def RestartServer(self): + server.restart() def GetProjectRoot(self, filepath): curPath = path.dirname(filepath) while curPath != '/' and not path.exists( - path.join(curPath, 'composer.json') - ): + path.join(curPath, 'composer.json') + ): curPath = path.dirname(curPath) if curPath == '/': @@ -226,8 +267,4 @@ def GetProjectRoot(self, filepath): return curPath - def PadawanPHPPath(self): - return padawanPath + '/padawan.php/' - - client = PadawanClient() diff --git a/plugin.py b/plugin.py index c2d4162..63fe617 100644 --- a/plugin.py +++ b/plugin.py @@ -17,7 +17,7 @@ def run(self, edit): fname = self.view.file_name() if fname is None: return None - client.Generate(fname, self.view) + client.Generate(fname) class PadawanStartServerCommand(sublime_plugin.TextCommand): @@ -44,9 +44,11 @@ def run(self, edit): def success(name): if not name or not isinstance(name, str): return - client.AddPlugin(self.view, name) + client.AddPlugin(name) + def on_change(name): return + def on_cancel(): return @@ -63,13 +65,14 @@ class PadawanPluginRemoveCommand(sublime_plugin.TextCommand): def run(self, edit): items = client.GetInstalledPlugins() + def success(index): if index >= len(items): return name = items[index] if not name: return - client.RemovePlugin(self.view, name) + client.RemovePlugin(name) sublime.active_window().show_quick_panel( items, @@ -110,6 +113,12 @@ def on_modified_async(self, view): curChar = view.substr(sublime.Region(cursor-4, cursor)) if curChar == 'use ': return self.run_completion(view) + if curChar == 'new ': + return self.run_completion(view) + if cursor > 9: + curChar = view.substr(sublime.Region(cursor-10, cursor)) + if curChar == 'namespace ': + return self.run_completion(view) cursor -= 1 def on_query_completions(self, view, prefix, locations):