@ -0,0 +1,12 @@ | |||
root = true | |||
[*] | |||
charset = utf-8 | |||
end_of_line = lf | |||
indent_style = space | |||
indent_size = 4 | |||
insert_final_newline = true | |||
trim_trailing_whitespace = true | |||
[*.md] | |||
indent_size = 2 |
@ -0,0 +1,41 @@ | |||
# Short and descriptive example bug report title | |||
## Summary | |||
... | |||
> Any other information you want to share that is relevant to the issue being reported. This might include the lines of code that you have identified as causing the bug, and potential solutions (and your opinions on their merits). | |||
## Expected behaviour | |||
... | |||
## Actual behaviour | |||
... | |||
## Steps to reproduce | |||
* This is the first step. | |||
1. one | |||
2. two | |||
3. three | |||
* This is the second step. | |||
* Further steps, etc. | |||
### Environment | |||
```text | |||
ProjectManager: | |||
version: 0.7.2 | |||
installed via Package Control: True | |||
Sublime Text: | |||
channel: stable | |||
version: 3126 | |||
platform: windows | |||
portable: yes | |||
architecture: x64 | |||
``` |
@ -0,0 +1,6 @@ | |||
*.cache | |||
*.log | |||
*.pyc | |||
*.pyo | |||
*.sublime-workspace | |||
.DS_Store |
@ -0,0 +1,22 @@ | |||
{ | |||
"@python": 3, | |||
"linters": | |||
{ | |||
"flake8": | |||
{ | |||
"ignore": "F401, F403", | |||
"max-line-length": 120 | |||
}, | |||
"pep257": | |||
{ | |||
"add-ignore": | |||
[ | |||
"D202" | |||
] | |||
}, | |||
"pep8": | |||
{ | |||
"max-line-length": 120 | |||
} | |||
} | |||
} |
@ -0,0 +1,117 @@ | |||
## CHANGELOG | |||
### Unreleased | |||
* add `.github/ISSUE_TEMPLATE.md` | |||
* add `.editorconfig` | |||
* add `.gitignore` | |||
* add `.sublimelinterrc` | |||
* add `CHANGELOG.md` | |||
* commands | |||
* open settings side-by-side | |||
* open key bindings side-by-side | |||
* menus | |||
* target default entries by id only | |||
* support custom main menus (in other languages) | |||
* open setttings side-by-side | |||
* open key bindings side-by-side | |||
* python source | |||
* split into files and load as module | |||
* put strings in single quotes | |||
* no need to `import codecs` | |||
* use `with` when accessing files | |||
* set newline to `\n` when writing | |||
* if dialog `answer is True` | |||
* open README and CHANGELOG in a read_only, scratch copy in a new tab | |||
* via main menu or command palette | |||
### [0.7.2](https://github.com/randy3k/ProjectManager/compare/0.7.1...0.7.2) | |||
* update README | |||
* add key bindings for windows and linux | |||
* use realpath for detecting window to close | |||
* better name the command as "Add New Project" | |||
### [0.7.1](https://github.com/randy3k/ProjectManager/compare/0.7.0...0.7.1) | |||
* fix #55 | |||
### [0.7.0](https://github.com/randy3k/ProjectManager/compare/0.6.11...0.7.0) | |||
* fix typo | |||
* remove show_open_files settings as the bug was fixed | |||
* use re.sub instead of replace to fix #54 | |||
* use relative link | |||
* rename as ProjectManager | |||
### [0.6.11](https://github.com/randy3k/ProjectManager/compare/0.6.10...0.6.11) | |||
* redundant caption | |||
* feature: remove dead projects | |||
* change default order of projects list | |||
### [0.6.10](https://github.com/randy3k/ProjectManager/compare/0.6.9...0.6.10) | |||
* cannonicalize projects directories | |||
* update menus and screenshots | |||
* use close_all instead | |||
### [0.6.9](https://github.com/randy3k/ProjectManager/compare/0.6.8...0.6.9) | |||
* close project by window or name | |||
### [0.6.8](https://github.com/randy3k/ProjectManager/compare/0.6.7...0.6.8) | |||
* use try-catch errors | |||
* only when library exists | |||
* no long check close_windows_when_empty | |||
* rename to get_info_from_project_file | |||
* cannonicalize paths to fix #47 | |||
### [0.6.7](https://github.com/randy3k/ProjectManager/compare/0.6.6...0.6.7) | |||
* use set_timeout instead of set_timeout_async | |||
* only check library file if it exists | |||
* rename functions for better readability | |||
* only close non-active window | |||
### [0.6.6](https://github.com/randy3k/ProjectManager/compare/0.6.5...0.6.6) | |||
* focus on the original view | |||
### [0.6.5](https://github.com/randy3k/ProjectManager/compare/0.6.4...0.6.5) | |||
* auto refresh folder list | |||
* various updates | |||
* update README | |||
* confirm to clear recent projects | |||
* remove refresh folder functionality | |||
* add emptylink in before / after code block | |||
* fix #2 | |||
### [0.6.4](https://github.com/randy3k/ProjectManager/compare/0.6.3...0.6.4) | |||
* show message when project list is empty | |||
### [0.6.3](https://github.com/randy3k/ProjectManager/compare/0.6.2...0.6.3) | |||
* fix window closing behaviour | |||
* fix which_project_dir bug again | |||
### [0.6.2](https://github.com/randy3k/ProjectManager/compare/0.6.1...0.6.2) | |||
* bootstrap manager run function | |||
* resolve symlink | |||
* rename function to expand_folder | |||
* fix which_project_dir | |||
### [0.6.1](https://github.com/randy3k/ProjectManager/compare/0.6.0...0.6.1) | |||
* add `get_project_files()` | |||
* add `get_project_info()` | |||
* don't use timeoout_async | |||
* pep8 fix | |||
### 0.6.0 | |||
* first release |
@ -1,70 +1,74 @@ | |||
# Project Manager for Sublime Text 3 | |||
# [`ProjectManager`](https://github.com/randy3k/ProjectManager) for [Sublime Text](https://www.sublimetext.com) | |||
<a href="https://packagecontrol.io/packages/ProjectManager"><img src="https://packagecontrol.herokuapp.com/downloads/ProjectManager.svg"></a> | |||
<a href="https://www.paypal.com/cgi-bin/webscr?cmd=_donations&business=Randy%2ecs%2elai%40gmail%2ecom&lc=US&item_name=Package&currency_code=USD&bn=PP%2dDonationsBF%3apaypal%2ddonate%2dyellow%2esvg%3aNonHosted" title="Donate to this project using Paypal"><img src="https://img.shields.io/badge/paypal-donate-blue.svg" /></a> | |||
<a href="https://gratipay.com/~randy3k/" title="Donate to this project using Gratipay"><img src="https://img.shields.io/badge/gratipay-donate-yellow.svg" /></a> | |||
[](https://github.com/randy3k/ProjectManager/blob/master/LICENSE.txt) | |||
[](https://packagecontrol.io/packages/ProjectManager) | |||
[](https://github.com/randy3k/ProjectManager/releases/latest) | |||
[](https://www.paypal.com/cgi-bin/webscr?cmd=_donations&business=Randy%2ecs%2elai%40gmail%2ecom&lc=US&item_name=Package¤cy_code=USD&bn=PP%2dDonationsBF%3apaypal%2ddonate%2dyellow%2esvg%3aNonHosted) | |||
[](https://gratipay.com/~randy3k/) | |||
Dont't have any idea what `*.sublime-project` and `*.sublime-workspace` are doing? Forget where the project files are? Don't worry, Project Manager will help organizing the project files by putting them in a centralized location. (It is inspired by Atom's [Project Manager](https://atom.io/packages/project-manager), but Atom's Project Manager is inspired by the built-in Sublime Text Project Manager, | |||
so there is a circular reasoning here). | |||
Dont't have any idea what `*.sublime-project` and `*.sublime-workspace` are doing? Forget where the project files are? Don't worry, Project Manager will help organizing the project files by putting them in a centralized location. (It is inspired by Atom's [Project Manager](https://atom.io/packages/project-manager), but Atom's Project Manager is inspired by the built-in Sublime Text Project Manager, so there is a circular reasoning here). | |||
 | |||
 | |||
## Requirements | |||
### Installation | |||
ProjectManager targets and is tested against the **latest Build** of Sublime Text. | |||
You can install Project Manager via Package Control. | |||
* [ST3 (stable)](https://www.sublimetext.com/3) | |||
* [ST3 (dev)](https://www.sublimetext.com/3dev) | |||
You can additionally add the following keybind in your user keybind settings file for "Open project in new window" | |||
## Installation | |||
``` | |||
{ | |||
"keys": ["super+ctrl+o"], // or ["ctrl+alt+o"] for Windows/Linux | |||
"command": "project_manager", "args": {"action": "new"} | |||
} | |||
``` | |||
Using **Package Control** is not required, but recommended as it keeps your packages (with their dependencies) up-to-date! | |||
### Installation via Package Control | |||
### Usage | |||
* [Install Package Control](https://packagecontrol.io/installation#st3) | |||
* Close and reopen Sublime Text after having installed Package Control. | |||
* Open the Command Palette (`Tools > Command Palette`). | |||
* Choose `Package Control: Install Package`. | |||
* Search for [`ProjectManager` on Package Control](https://packagecontrol.io/packages/ProjectManager) and select to install. | |||
To launch the Project Manager, you can either open it under the `Project` menu or via the command palette: `Project Manager: ...`. | |||
## Usage | |||
To quick switch between projects, use the hotkey <kbd>Ctrl</kbd>+<kbd>Cmd</kbd>+<kbd>P</kbd> (<kbd>Ctrl</kbd>+<kbd>Alt</kbd>+<kbd>P</kbd> for windows/linux). | |||
To launch ProjectManager, use the main menu (`Project > Project Manager`) or the command palette (`Project Manager: ...`). | |||
Project Manager also improves the shortcut <kbd>Ctrl</kbd>+<kbd>Shift</kbd>+<kbd>W</kbd> on Windows and Linux so that it will close the project when the window is closed. On OSX, it is the default behaviour. | |||
To quickly switch between projects, use the hotkey <kbd>Ctrl</kbd><kbd>⌘ Cmd</kbd><kbd>P</kbd> on macOS (<kbd>Ctrl</kbd><kbd>Alt</kbd><kbd>P</kbd> on Windows / Linux). | |||
ProjectManager also improves the shortcut <kbd>Ctrl</kbd><kbd>Shift</kbd><kbd>W</kbd> on Windows / Linux so that it will close the project when the window is closed. On OSX, this is the default behaviour. | |||
 | |||
 | |||
Options are self-explanatory, enjoy! | |||
Options are self-explained, enjoy! | |||
#### Create new project | |||
### Create new project | |||
Just drag some folders to Sublime Text and then "Add Project". The project files will be created in `Packages/User/Projects/`. | |||
#### Add existing projects to Project Manager | |||
### Add existing projects to Project Manager | |||
There are two ways to add existing projects to Project Manager. | |||
If you want to keep the project files (`.sublime-project` and `sublime-workspace`) in your project directory, | |||
There are two ways to add existing projects to Project Manager. If you want to keep the project files (`*.sublime-project` and `*.sublime-workspace`) in your project directory, | |||
- Open your project file `.sublime-project`, and then use the import option of Project Manager. This tells Project Manager where `.sublime-project` is located and Project Manager will know where to look when the project is opened. In other words, you can put the `.sublime-project` file in any places. | |||
- Open your project file `*.sublime-project`, and then use the import option of Project Manager. This tells Project Manager where `*.sublime-project` is located and Project Manager will know where to look when the project is opened. In other words, you can put the `*.sublime-project` file in any places. | |||
If you want Project Manager manages the project files | |||
- Move your `.sublime-project` and `.sublime-workspace` files in the project directory `Packages/User/Projects/`. You may need to update the project's folder information of the files. | |||
- Move your `*.sublime-project` and `*.sublime-workspace` files in the project directory `Packages/User/Projects/`. You may need to update the project's folder information of the files. | |||
#### Custom Projects directory | |||
### Custom Projects directory | |||
To use a different directory for your projects rather than `Packages/User/Projects/`, edit the following in package settings: Preferences -> Package Settings -> Project Manager | |||
To use a different directory for your projects rather than `Packages/User/Projects/`, edit the following in package settings: `Preferences > Package Settings > Project Manager` | |||
``` | |||
```json | |||
{ | |||
"projects_path": ["path/to/custom/projects_dir"], | |||
} | |||
``` | |||
## Source code | |||
[github.com/randy3k/ProjectManager](https://www.github.com/randy3k/ProjectManager) | |||
### License | |||
@ -1,17 +0,0 @@ | |||
import sublime_plugin | |||
class ProjectManagerCloseWindow(sublime_plugin.WindowCommand): | |||
def run(self): | |||
if self.window.project_file_name(): | |||
# if it is a project, close the project | |||
self.window.run_command('close_workspace') | |||
else: | |||
self.window.run_command('close_all') | |||
# exit if there are dirty views | |||
if any([v.is_dirty() for v in self.window.views()]): | |||
return | |||
# close the sidebar | |||
self.window.run_command('close_project') | |||
# close the window | |||
self.window.run_command('close_window') |
@ -1,528 +0,0 @@ | |||
import sublime | |||
import sublime_plugin | |||
import subprocess | |||
import os | |||
import codecs | |||
import platform | |||
import re | |||
def plugin_loaded(): | |||
t = sublime.load_settings("Project Manager.sublime-settings") | |||
s = sublime.load_settings("project_manager.sublime-settings") | |||
keys = [ | |||
"projects_path", | |||
"use_local_projects_dir", | |||
"show_open_files", | |||
"show_recent_projects_first" | |||
] | |||
d = {} | |||
for k in keys: | |||
if t.has(k) and not s.has(k): | |||
d.update({k: t.get(k)}) | |||
for key, value in d.items(): | |||
s.set(key, value) | |||
if d: | |||
sublime.save_settings("project_manager.sublime-settings") | |||
old_file = os.path.join(sublime.packages_path(), "User", "Project Manager.sublime-settings") | |||
if os.path.exists(old_file): | |||
os.unlink(old_file) | |||
class JsonFile: | |||
def __init__(self, fpath, encoding="utf-8"): | |||
self.encoding = encoding | |||
self.fpath = fpath | |||
def load(self, default=[]): | |||
self.fdir = os.path.dirname(self.fpath) | |||
if not os.path.isdir(self.fdir): | |||
os.makedirs(self.fdir) | |||
if os.path.exists(self.fpath): | |||
f = codecs.open(self.fpath, "r+", encoding=self.encoding) | |||
content = f.read() | |||
try: | |||
data = sublime.decode_value(content) | |||
except: | |||
sublime.message_dialog("%s is bad!" % self.fpath) | |||
raise | |||
if not data: | |||
data = default | |||
f.close() | |||
else: | |||
f = codecs.open(self.fpath, "w+", encoding=self.encoding) | |||
data = default | |||
f.close() | |||
return data | |||
def save(self, data, indent=4): | |||
self.fdir = os.path.dirname(self.fpath) | |||
if not os.path.isdir(self.fdir): | |||
os.makedirs(self.fdir) | |||
f = codecs.open(self.fpath, "w+", encoding=self.encoding) | |||
f.write(sublime.encode_value(data, True)) | |||
f.close() | |||
def remove(self): | |||
if os.path.exists(self.fpath): | |||
os.remove(self.fpath) | |||
def subl(args=[]): | |||
# learnt from SideBarEnhancements | |||
executable_path = sublime.executable_path() | |||
if sublime.platform() == 'osx': | |||
app_path = executable_path[:executable_path.rfind(".app/") + 5] | |||
executable_path = app_path + "Contents/SharedSupport/bin/subl" | |||
subprocess.Popen([executable_path] + args) | |||
if sublime.platform() == "windows": | |||
def fix_focus(): | |||
window = sublime.active_window() | |||
view = window.active_view() | |||
window.run_command('focus_neighboring_group') | |||
window.focus_view(view) | |||
sublime.set_timeout(fix_focus, 300) | |||
def expand_folder(folder, project_file): | |||
root = os.path.dirname(project_file) | |||
if not os.path.isabs(folder): | |||
folder = os.path.abspath(os.path.join(root, folder)) | |||
return folder | |||
def get_node(): | |||
if sublime.platform() == "osx": | |||
node = subprocess.check_output(["scutil", "--get", "ComputerName"]).decode().strip() | |||
else: | |||
node = platform.node().split(".")[0] | |||
return node | |||
def dont_close_windows_when_empty(func): | |||
def f(*args, **kwargs): | |||
preferences = sublime.load_settings("Preferences.sublime-settings") | |||
close_windows_when_empty = preferences.get("close_windows_when_empty") | |||
preferences.set("close_windows_when_empty", False) | |||
func(*args, **kwargs) | |||
if close_windows_when_empty: | |||
preferences.set("close_windows_when_empty", close_windows_when_empty) | |||
return f | |||
class Manager: | |||
def __init__(self, window): | |||
self.window = window | |||
settings_file = 'project_manager.sublime-settings' | |||
self.settings = sublime.load_settings(settings_file) | |||
default_projects_dir = os.path.join(sublime.packages_path(), "User", "Projects") | |||
self.projects_path = self.settings.get( | |||
"projects_path", [self.settings.get("projects_dir", default_projects_dir)]) | |||
self.projects_path = [ | |||
os.path.normpath(os.path.expanduser(d)) for d in self.projects_path] | |||
node = get_node() | |||
if self.settings.get("use_local_projects_dir", False): | |||
self.projects_path = \ | |||
[d + " - " + node for d in self.projects_path] + self.projects_path | |||
self.primary_dir = self.projects_path[0] | |||
self.projects_info = self.get_all_projects_info() | |||
def list_project_files(self, folder): | |||
pfiles = [] | |||
library = os.path.join(folder, "library.json") | |||
if os.path.exists(library): | |||
j = JsonFile(library) | |||
for f in j.load([]): | |||
if os.path.exists(f) and f not in pfiles: | |||
pfiles.append(os.path.normpath(f)) | |||
pfiles.sort() | |||
j.save(pfiles) | |||
for path, dirs, files in os.walk(folder, followlinks=True): | |||
for f in files: | |||
f = os.path.join(path, f) | |||
if f.endswith(".sublime-project") and f not in pfiles: | |||
pfiles.append(os.path.normpath(f)) | |||
# remove empty directories | |||
for d in dirs: | |||
d = os.path.join(path, d) | |||
if len(os.listdir(d)) == 0: | |||
os.rmdir(d) | |||
return pfiles | |||
def get_info_from_project_file(self, pfile): | |||
pdir = self.which_project_dir(pfile) | |||
if pdir: | |||
pname = re.sub("\.sublime-project$", "", os.path.relpath(pfile, pdir)) | |||
else: | |||
pname = re.sub("\.sublime-project$", "", os.path.basename(pfile)) | |||
pd = JsonFile(pfile).load() | |||
if pd and "folders" in pd and pd["folders"]: | |||
folder = pd["folders"][0].get("path", "") | |||
else: | |||
folder = "" | |||
star = False | |||
for w in sublime.windows(): | |||
if w.project_file_name() == pfile: | |||
star = True | |||
break | |||
return { | |||
pname: { | |||
"folder": expand_folder(folder, pfile), | |||
"file": pfile, | |||
"star": star | |||
} | |||
} | |||
def get_all_projects_info(self): | |||
ret = {} | |||
for pdir in self.projects_path: | |||
pfiles = self.list_project_files(pdir) | |||
for f in pfiles: | |||
ret.update(self.get_info_from_project_file(f)) | |||
return ret | |||
def which_project_dir(self, pfile): | |||
for pdir in self.projects_path: | |||
if (os.path.realpath(os.path.dirname(pfile))+os.path.sep).startswith( | |||
os.path.realpath(pdir)+os.path.sep): | |||
return pdir | |||
return None | |||
def display_projects(self): | |||
plist = [[key, key + "*" if value["star"] else key, value["folder"], value["file"]] | |||
for key, value in self.projects_info.items()] | |||
plist = sorted(plist) | |||
if self.settings.get("show_recent_projects_first", True): | |||
j = JsonFile(os.path.join(self.primary_dir, "recent.json")) | |||
recent = j.load([]) | |||
plist = sorted(plist, key=lambda p: recent.index(p[3]) if p[3] in recent else -1, | |||
reverse=True) | |||
count = 0 | |||
for i in range(len(plist)): | |||
if plist[i][0] is not plist[i][1]: | |||
plist.insert(count, plist.pop(i)) | |||
count = count + 1 | |||
return [item[0] for item in plist], [[item[1], item[2]] for item in plist] | |||
def project_file_name(self, project): | |||
return self.projects_info[project]["file"] | |||
def project_workspace(self, project): | |||
return re.sub("\.sublime-project$", ".sublime-workspace", self.project_file_name(project)) | |||
def update_recent(self, project): | |||
j = JsonFile(os.path.join(self.primary_dir, "recent.json")) | |||
recent = j.load([]) | |||
pname = self.project_file_name(project) | |||
if pname not in recent: | |||
recent.append(pname) | |||
else: | |||
recent.append(recent.pop(recent.index(pname))) | |||
# only keep the most recent 50 records | |||
if len(recent) > 50: | |||
recent = recent[(50-len(recent)):len(recent)] | |||
j.save(recent) | |||
def clear_recent_projects(self): | |||
def clear_callback(): | |||
ok = sublime.ok_cancel_dialog("Clear Recent Projects?") | |||
if ok: | |||
j = JsonFile(os.path.join(self.primary_dir, "recent.json")) | |||
j.remove() | |||
sublime.set_timeout(clear_callback, 100) | |||
def get_project_data(self, project): | |||
return JsonFile(self.project_file_name(project)).load() | |||
def check_project(self, project): | |||
wsfile = self.project_workspace(project) | |||
j = JsonFile(wsfile) | |||
if not os.path.exists(wsfile): | |||
j.save({}) | |||
elif self.settings.has("show_open_files"): | |||
show_open_files = self.settings.get("show_open_files", False) | |||
data = j.load({}) | |||
data["show_open_files"] = show_open_files | |||
df = data.get("distraction_free", {}) | |||
df["show_open_files"] = show_open_files | |||
data["distraction_free"] = df | |||
j.save(data) | |||
@dont_close_windows_when_empty | |||
def close_project_by_window(self, window): | |||
window.run_command("close_workspace") | |||
def close_project_by_name(self, project): | |||
pfile = os.path.realpath(self.project_file_name(project)) | |||
for w in sublime.windows(): | |||
if w.project_file_name(): | |||
if os.path.realpath(w.project_file_name()) == pfile: | |||
self.close_project_by_window(w) | |||
if w.id() != sublime.active_window().id(): | |||
w.run_command("close_window") | |||
return True | |||
return False | |||
def add_project(self): | |||
@dont_close_windows_when_empty | |||
def close_all_files(): | |||
self.window.run_command("close_all") | |||
def add_callback(project): | |||
pd = self.window.project_data() | |||
f = os.path.join(self.primary_dir, "%s.sublime-project" % project) | |||
if pd: | |||
JsonFile(f).save(pd) | |||
else: | |||
JsonFile(f).save({}) | |||
JsonFile(re.sub("\.sublime-project$", ".sublime-workspace", f)).save({}) | |||
self.close_project_by_window(self.window) | |||
self.window.run_command("close_project") | |||
close_all_files() | |||
# reload projects info | |||
self.__init__(self.window) | |||
self.switch_project(project) | |||
def show_input_panel(): | |||
project = "New Project" | |||
pd = self.window.project_data() | |||
pf = self.window.project_file_name() | |||
try: | |||
path = pd["folders"][0]["path"] | |||
if pf: | |||
project = os.path.basename(expand_folder(path, pf)) | |||
else: | |||
project = os.path.basename(path) | |||
except: | |||
pass | |||
v = self.window.show_input_panel("Project name:", project, add_callback, None, None) | |||
v.run_command("select_all") | |||
sublime.set_timeout(show_input_panel, 100) | |||
def import_sublime_project(self): | |||
pfile = self.window.project_file_name() | |||
if not pfile: | |||
sublime.message_dialog("Project file not found!") | |||
return | |||
if self.which_project_dir(pfile): | |||
sublime.message_dialog("This project was created by Project Manager!") | |||
return | |||
ok = sublime.ok_cancel_dialog("Import %s?" % os.path.basename(pfile)) | |||
if ok: | |||
j = JsonFile(os.path.join(self.primary_dir, "library.json")) | |||
data = j.load([]) | |||
if pfile not in data: | |||
data.append(pfile) | |||
j.save(data) | |||
def append_project(self, project): | |||
self.update_recent(project) | |||
pd = self.get_project_data(project) | |||
paths = [expand_folder(f.get("path"), self.project_file_name(project)) | |||
for f in pd.get("folders")] | |||
subl(["-a"] + paths) | |||
def switch_project(self, project): | |||
self.update_recent(project) | |||
self.check_project(project) | |||
self.close_project_by_window(self.window) | |||
self.close_project_by_name(project) | |||
subl([self.project_file_name(project)]) | |||
def open_in_new_window(self, project): | |||
self.update_recent(project) | |||
self.check_project(project) | |||
self.close_project_by_name(project) | |||
subl(["-n", self.project_file_name(project)]) | |||
def _remove_project(self, project): | |||
ok = sublime.ok_cancel_dialog("Remove \"%s\" from Project Manager?" % project) | |||
if ok: | |||
pfile = self.project_file_name(project) | |||
if self.which_project_dir(pfile): | |||
self.close_project_by_name(project) | |||
os.unlink(self.project_file_name(project)) | |||
os.unlink(self.project_workspace(project)) | |||
else: | |||
for pdir in self.projects_path: | |||
j = JsonFile(os.path.join(pdir, "library.json")) | |||
data = j.load([]) | |||
if pfile in data: | |||
data.remove(pfile) | |||
j.save(data) | |||
sublime.status_message("Project \"%s\" is removed." % project) | |||
def remove_project(self, project): | |||
sublime.set_timeout(lambda: self._remove_project(project), 100) | |||
def clean_dead_projects(self): | |||
projects_to_remove = [] | |||
for pname, pi in self.projects_info.items(): | |||
folder = pi["folder"] | |||
if not os.path.exists(folder): | |||
projects_to_remove.append(pname) | |||
def remove_projects_iteratively(): | |||
pname = projects_to_remove[0] | |||
self._remove_project(pname) | |||
projects_to_remove.remove(pname) | |||
if len(projects_to_remove) > 0: | |||
sublime.set_timeout(remove_projects_iteratively, 100) | |||
if len(projects_to_remove) > 0: | |||
sublime.set_timeout(remove_projects_iteratively, 100) | |||
else: | |||
sublime.message_dialog("No Dead Projects.") | |||
def edit_project(self, project): | |||
def on_open(): | |||
self.window.open_file(self.project_file_name(project)) | |||
sublime.set_timeout_async(on_open, 100) | |||
def rename_project(self, project): | |||
def rename_callback(new_project): | |||
if project == new_project: | |||
return | |||
pfile = self.project_file_name(project) | |||
wsfile = self.project_workspace(project) | |||
pdir = self.which_project_dir(pfile) | |||
if not pdir: | |||
pdir = os.path.dirname(pfile) | |||
new_pfile = os.path.join(pdir, "%s.sublime-project" % new_project) | |||
new_wsfile = re.sub("\.sublime-project$", ".sublime-workspace", new_pfile) | |||
reopen = self.close_project_by_name(project) | |||
os.rename(pfile, new_pfile) | |||
os.rename(wsfile, new_wsfile) | |||
j = JsonFile(new_wsfile) | |||
data = j.load({}) | |||
if "project" in data: | |||
data["project"] = "%s.sublime-project" % os.path.basename(new_project) | |||
j.save(data) | |||
if not self.which_project_dir(pfile): | |||
for pdir in self.projects_path: | |||
library = os.path.join(pdir, "library.json") | |||
if os.path.exists(library): | |||
j = JsonFile(library) | |||
data = j.load([]) | |||
if pfile in data: | |||
data.remove(pfile) | |||
data.append(new_pfile) | |||
j.save(data) | |||
if reopen: | |||
# reload projects info | |||
self.__init__(self.window) | |||
self.open_in_new_window(new_project) | |||
def show_input_panel(): | |||
v = self.window.show_input_panel("New project name:", | |||
project, rename_callback, None, None) | |||
v.run_command("select_all") | |||
sublime.set_timeout(show_input_panel, 100) | |||
def cancellable(func): | |||
def _ret(self, action): | |||
if action >= 0: | |||
func(self, action) | |||
elif action < 0 and self.caller == "manager": | |||
sublime.set_timeout(self.run, 10) | |||
return _ret | |||
class ProjectManager(sublime_plugin.WindowCommand): | |||
def show_quick_panel(self, items, on_done): | |||
sublime.set_timeout( | |||
lambda: self.window.show_quick_panel(items, on_done), | |||
10) | |||
def run(self, action=None, caller=None): | |||
self.manager = Manager(self.window) | |||
if action is None: | |||
self.show_options() | |||
elif action == "add_project": | |||
self.manager.add_project() | |||
elif action == "import_sublime_project": | |||
self.manager.import_sublime_project() | |||
elif action == "clear_recent_projects": | |||
self.manager.clear_recent_projects() | |||
elif action == "remove_dead_projects": | |||
self.manager.clean_dead_projects() | |||
else: | |||
self.caller = caller | |||
callback = eval("self.on_" + action) | |||
self.projects, display = self.manager.display_projects() | |||
if not self.projects: | |||
sublime.message_dialog("Project list is empty.") | |||
return | |||
self.show_quick_panel(display, callback) | |||
def show_options(self): | |||
items = [ | |||
["Open Project", "Open project in the current window"], | |||
["Open Project in New Window", "Open project in a new window"], | |||
["Append Project", "Append project to current window"], | |||
["Edit Project", "Edit project settings"], | |||
['Rename Project', "Rename project"], | |||
["Remove Project", "Remove from Project Manager"], | |||
["Add New Project", "Add current folders to Project Manager"], | |||
["Import Project", "Import current .sublime-project file"], | |||
["Clear Recent Projects", "Clear Recent Projects"], | |||
["Remove Dead Projects", "Remove Dead Projects"] | |||
] | |||
def callback(a): | |||
if a < 0: | |||
return | |||
elif a <= 5: | |||
actions = ["switch", "new", "append", "edit", "rename", "remove"] | |||
self.run(action=actions[a], caller="manager") | |||
elif a == 6: | |||
self.run(action="add_project") | |||
elif a == 7: | |||
self.run(action="import_sublime_project") | |||
elif a == 8: | |||
self.run(action="clear_recent_projects") | |||
elif a == 9: | |||
self.run(action="remove_dead_projects") | |||
self.show_quick_panel(items, callback) | |||
@cancellable | |||
def on_new(self, action): | |||
self.manager.open_in_new_window(self.projects[action]) | |||
@cancellable | |||
def on_switch(self, action): | |||
self.manager.switch_project(self.projects[action]) | |||
@cancellable | |||
def on_append(self, action): | |||
self.manager.append_project(self.projects[action]) | |||
@cancellable | |||
def on_remove(self, action): | |||
self.manager.remove_project(self.projects[action]) | |||
@cancellable | |||
def on_edit(self, action): | |||
self.manager.edit_project(self.projects[action]) | |||
@cancellable | |||
def on_rename(self, action): | |||
self.manager.rename_project(self.projects[action]) |
@ -0,0 +1,5 @@ | |||
#!/usr/bin/env python | |||
# coding: utf-8 | |||
from .src import * |
@ -0,0 +1 @@ | |||
__pkg_name__ = 'ProjectManager' |
@ -0,0 +1,25 @@ | |||
import sublime | |||
import sublime_plugin | |||
import os | |||
def plugin_loaded(): | |||
t = sublime.load_settings('Project Manager.sublime-settings') | |||
s = sublime.load_settings('project_manager.sublime-settings') | |||
keys = [ | |||
'projects_path', | |||
'use_local_projects_dir', | |||
'show_open_files', | |||
'show_recent_projects_first' | |||
] | |||
d = {} | |||
for k in keys: | |||
if t.has(k) and not s.has(k): | |||
d.update({k: t.get(k)}) | |||
for key, value in d.items(): | |||
s.set(key, value) | |||
if d: | |||
sublime.save_settings('project_manager.sublime-settings') | |||
old_file = os.path.join(sublime.packages_path(), 'User', 'Project Manager.sublime-settings') | |||
if os.path.exists(old_file): | |||
os.remove(old_file) |
@ -0,0 +1,38 @@ | |||
import sublime | |||
import os | |||
class JsonFile: | |||
def __init__(self, fpath, encoding='utf-8'): | |||
self.encoding = encoding | |||
self.fpath = fpath | |||
def load(self, default=[]): | |||
self.fdir = os.path.dirname(self.fpath) | |||
if not os.path.isdir(self.fdir): | |||
os.makedirs(self.fdir) | |||
if os.path.exists(self.fpath): | |||
with open(self.fpath, mode='r', encoding=self.encoding) as f: | |||
content = f.read() | |||
try: | |||
data = sublime.decode_value(content) | |||
except: | |||
sublime.message_dialog('%s is bad!' % self.fpath) | |||
raise | |||
if not data: | |||
data = default | |||
else: | |||
with open(self.fpath, mode='w', encoding=self.encoding, newline='\n') as f: | |||
data = default | |||
f.write(data) | |||
return data | |||
def save(self, data, indent=4): | |||
self.fdir = os.path.dirname(self.fpath) | |||
if not os.path.isdir(self.fdir): | |||
os.makedirs(self.fdir) | |||
with open(self.fpath, mode='w', encoding=self.encoding, newline='\n') as f: | |||
f.write(sublime.encode_value(data, True)) | |||
def remove(self): | |||
if os.path.exists(self.fpath): | |||
os.remove(self.fpath) |
@ -0,0 +1,390 @@ | |||
import sublime | |||
import sublime_plugin | |||
import subprocess | |||
import os | |||
import platform | |||
import re | |||
from .json_file import JsonFile | |||
def subl(args=[]): | |||
# learnt from SideBarEnhancements | |||
executable_path = sublime.executable_path() | |||
if sublime.platform() == 'osx': | |||
app_path = executable_path[:executable_path.rfind('.app/') + 5] | |||
executable_path = app_path + 'Contents/SharedSupport/bin/subl' | |||
subprocess.Popen([executable_path] + args) | |||
if sublime.platform() == 'windows': | |||
def fix_focus(): | |||
window = sublime.active_window() | |||
view = window.active_view() | |||
window.run_command('focus_neighboring_group') | |||
window.focus_view(view) | |||
sublime.set_timeout(fix_focus, | |||
300) | |||
def expand_folder(folder, project_file): | |||
root = os.path.dirname(project_file) | |||
if not os.path.isabs(folder): | |||
folder = os.path.abspath(os.path.join(root, folder)) | |||
return folder | |||
def get_node(): | |||
if sublime.platform() == 'osx': | |||
node = subprocess.check_output(['scutil', '--get', 'ComputerName']).decode().strip() | |||
else: | |||
node = platform.node().split('.')[0] | |||
return node | |||
def dont_close_windows_when_empty(func): | |||
def f(*args, **kwargs): | |||
s = sublime.load_settings('Preferences.sublime-settings') | |||
close_windows_when_empty = s.get('close_windows_when_empty') | |||
s.set('close_windows_when_empty', False) | |||
func(*args, **kwargs) | |||
if close_windows_when_empty: | |||
s.set('close_windows_when_empty', close_windows_when_empty) | |||
return f | |||
class Manager: | |||
def __init__(self, window): | |||
self.window = window | |||
s = 'project_manager.sublime-settings' | |||
self.settings = sublime.load_settings(s) | |||
default_projects_dir = os.path.join(sublime.packages_path(), | |||
'User', | |||
'Projects') | |||
self.projects_path = self.settings.get( | |||
'projects_path', [self.settings.get('projects_dir', default_projects_dir)]) | |||
self.projects_path = [ | |||
os.path.normpath(os.path.expanduser(d)) for d in self.projects_path] | |||
node = get_node() | |||
if self.settings.get('use_local_projects_dir', False): | |||
self.projects_path = \ | |||
[d + ' - ' + node for d in self.projects_path] + self.projects_path | |||
self.primary_dir = self.projects_path[0] | |||
self.projects_info = self.get_all_projects_info() | |||
def list_project_files(self, folder): | |||
pfiles = [] | |||
library = os.path.join(folder, 'library.json') | |||
if os.path.exists(library): | |||
j = JsonFile(library) | |||
for f in j.load([]): | |||
if os.path.exists(f) and f not in pfiles: | |||
pfiles.append(os.path.normpath(f)) | |||
pfiles.sort() | |||
j.save(pfiles) | |||
for path, dirs, files in os.walk(folder, followlinks=True): | |||
for f in files: | |||
f = os.path.join(path, f) | |||
if f.endswith('.sublime-project') and f not in pfiles: | |||
pfiles.append(os.path.normpath(f)) | |||
# remove empty directories | |||
for d in dirs: | |||
d = os.path.join(path, d) | |||
if len(os.listdir(d)) == 0: | |||
os.rmdir(d) | |||
return pfiles | |||
def get_info_from_project_file(self, pfile): | |||
pdir = self.which_project_dir(pfile) | |||
if pdir: | |||
pname = re.sub('\.sublime-project$', | |||
'', | |||
os.path.relpath(pfile, pdir)) | |||
else: | |||
pname = re.sub('\.sublime-project$', | |||
'', | |||
os.path.basename(pfile)) | |||
pd = JsonFile(pfile).load() | |||
if pd and 'folders' in pd and pd['folders']: | |||
folder = pd['folders'][0].get('path', '') | |||
else: | |||
folder = '' | |||
star = False | |||
for w in sublime.windows(): | |||
if w.project_file_name() == pfile: | |||
star = True | |||
break | |||
return { | |||
pname: { | |||
'folder': expand_folder(folder, pfile), | |||
'file': pfile, | |||
'star': star | |||
} | |||
} | |||
def get_all_projects_info(self): | |||
ret = {} | |||
for pdir in self.projects_path: | |||
pfiles = self.list_project_files(pdir) | |||
for f in pfiles: | |||
ret.update(self.get_info_from_project_file(f)) | |||
return ret | |||
def which_project_dir(self, pfile): | |||
for pdir in self.projects_path: | |||
if (os.path.realpath(os.path.dirname(pfile))+os.path.sep).startswith( | |||
os.path.realpath(pdir)+os.path.sep): | |||
return pdir | |||
return None | |||
def display_projects(self): | |||
plist = [[key, key + '*' if value['star'] else key, value['folder'], value['file']] | |||
for key, value in self.projects_info.items()] | |||
plist = sorted(plist) | |||
if self.settings.get('show_recent_projects_first', True): | |||
j = JsonFile(os.path.join(self.primary_dir, 'recent.json')) | |||
recent = j.load([]) | |||
plist = sorted(plist, | |||
key=lambda p: recent.index(p[3]) if p[3] in recent else -1, | |||
reverse=True) | |||
count = 0 | |||
for i in range(len(plist)): | |||
if plist[i][0] is not plist[i][1]: | |||
plist.insert(count, plist.pop(i)) | |||
count = count + 1 | |||
return [item[0] for item in plist], [[item[1], item[2]] for item in plist] | |||
def project_file_name(self, project): | |||
return self.projects_info[project]['file'] | |||
def project_workspace(self, project): | |||
return re.sub('\.sublime-project$', | |||
'.sublime-workspace', | |||
self.project_file_name(project)) | |||
def update_recent(self, project): | |||
j = JsonFile(os.path.join(self.primary_dir, 'recent.json')) | |||
recent = j.load([]) | |||
pname = self.project_file_name(project) | |||
if pname not in recent: | |||
recent.append(pname) | |||
else: | |||
recent.append(recent.pop(recent.index(pname))) | |||
# only keep the most recent 50 records | |||
if len(recent) > 50: | |||
recent = recent[(50-len(recent)):len(recent)] | |||
j.save(recent) | |||
def clear_recent_projects(self): | |||
def clear_callback(): | |||
answer = sublime.ok_cancel_dialog('Clear Recent Projects?') | |||
if answer is True: | |||
j = JsonFile(os.path.join(self.primary_dir, 'recent.json')) | |||
j.remove() | |||
sublime.set_timeout(clear_callback, 100) | |||
def get_project_data(self, project): | |||
return JsonFile(self.project_file_name(project)).load() | |||
def check_project(self, project): | |||
wsfile = self.project_workspace(project) | |||
j = JsonFile(wsfile) | |||
if not os.path.exists(wsfile): | |||
j.save({}) | |||
elif self.settings.has('show_open_files'): | |||
show_open_files = self.settings.get('show_open_files', False) | |||
data = j.load({}) | |||
data['show_open_files'] = show_open_files | |||
df = data.get('distraction_free', {}) | |||
df['show_open_files'] = show_open_files | |||
data['distraction_free'] = df | |||
j.save(data) | |||
@dont_close_windows_when_empty | |||
def close_project_by_window(self, window): | |||
window.run_command('close_workspace') | |||
def close_project_by_name(self, project): | |||
pfile = os.path.realpath(self.project_file_name(project)) | |||
for w in sublime.windows(): | |||
if w.project_file_name(): | |||
if os.path.realpath(w.project_file_name()) == pfile: | |||
self.close_project_by_window(w) | |||
if w.id() != sublime.active_window().id(): | |||
w.run_command('close_window') | |||
return True | |||
return False | |||
def add_project(self): | |||
@dont_close_windows_when_empty | |||
def close_all_files(): | |||
self.window.run_command('close_all') | |||
def add_callback(project): | |||
pd = self.window.project_data() | |||
f = os.path.join(self.primary_dir, '%s.sublime-project' % project) | |||
if pd: | |||
JsonFile(f).save(pd) | |||
else: | |||
JsonFile(f).save({}) | |||
JsonFile(re.sub('\.sublime-project$', '.sublime-workspace', f)).save({}) | |||
self.close_project_by_window(self.window) | |||
self.window.run_command('close_project') | |||
close_all_files() | |||
# reload projects info | |||
self.__init__(self.window) | |||
self.switch_project(project) | |||
def show_input_panel(): | |||
project = 'New Project' | |||
pd = self.window.project_data() | |||
pf = self.window.project_file_name() | |||
try: | |||
path = pd['folders'][0]['path'] | |||
if pf: | |||
project = os.path.basename(expand_folder(path, pf)) | |||
else: | |||
project = os.path.basename(path) | |||
except: | |||
pass | |||
v = self.window.show_input_panel('Project name:', | |||
project, | |||
add_callback, | |||
None, | |||
None) | |||
v.run_command('select_all') | |||
sublime.set_timeout(show_input_panel, 100) | |||
def import_sublime_project(self): | |||
pfile = self.window.project_file_name() | |||
if not pfile: | |||
sublime.message_dialog('Project file not found!') | |||
return | |||
if self.which_project_dir(pfile): | |||
sublime.message_dialog('This project was created by Project Manager!') | |||
return | |||
answer = sublime.ok_cancel_dialog('Import %s?' % os.path.basename(pfile)) | |||
if answer is True: | |||
j = JsonFile(os.path.join(self.primary_dir, 'library.json')) | |||
data = j.load([]) | |||
if pfile not in data: | |||
data.append(pfile) | |||
j.save(data) | |||
def append_project(self, project): | |||
self.update_recent(project) | |||
pd = self.get_project_data(project) | |||
paths = [expand_folder(f.get('path'), self.project_file_name(project)) | |||
for f in pd.get('folders')] | |||
subl(['-a'] + paths) | |||
def switch_project(self, project): | |||
self.update_recent(project) | |||
self.check_project(project) | |||
self.close_project_by_window(self.window) | |||
self.close_project_by_name(project) | |||
subl([self.project_file_name(project)]) | |||
def open_in_new_window(self, project): | |||
self.update_recent(project) | |||
self.check_project(project) | |||
self.close_project_by_name(project) | |||
subl(['-n', self.project_file_name(project)]) | |||
def _remove_project(self, project): | |||
answer = sublime.ok_cancel_dialog('Remove "%s" from Project Manager?' % project) | |||
if answer is True: | |||
pfile = self.project_file_name(project) | |||
if self.which_project_dir(pfile): | |||
self.close_project_by_name(project) | |||
os.remove(self.project_file_name(project)) | |||
os.remove(self.project_workspace(project)) | |||
else: | |||
for pdir in self.projects_path: | |||
j = JsonFile(os.path.join(pdir, 'library.json')) | |||
data = j.load([]) | |||
if pfile in data: | |||
data.remove(pfile) | |||
j.save(data) | |||
sublime.status_message('Project "%s" is removed.' % project) | |||
def remove_project(self, project): | |||
sublime.set_timeout(lambda: self._remove_project(project), 100) | |||
def clean_dead_projects(self): | |||
projects_to_remove = [] | |||
for pname, pi in self.projects_info.items(): | |||
folder = pi['folder'] | |||
if not os.path.exists(folder): | |||
projects_to_remove.append(pname) | |||
def remove_projects_iteratively(): | |||
pname = projects_to_remove[0] | |||
self._remove_project(pname) | |||
projects_to_remove.remove(pname) | |||
if len(projects_to_remove) > 0: | |||
sublime.set_timeout(remove_projects_iteratively, 100) | |||
if len(projects_to_remove) > 0: | |||
sublime.set_timeout(remove_projects_iteratively, 100) | |||
else: | |||
sublime.message_dialog('No Dead Projects.') | |||
def edit_project(self, project): | |||
def on_open(): | |||
self.window.open_file(self.project_file_name(project)) | |||
sublime.set_timeout_async(on_open, 100) | |||
def rename_project(self, project): | |||
def rename_callback(new_project): | |||
if project == new_project: | |||
return | |||
pfile = self.project_file_name(project) | |||
wsfile = self.project_workspace(project) | |||
pdir = self.which_project_dir(pfile) | |||
if not pdir: | |||
pdir = os.path.dirname(pfile) | |||
new_pfile = os.path.join(pdir, '%s.sublime-project' % new_project) | |||
new_wsfile = re.sub('\.sublime-project$', '.sublime-workspace', new_pfile) | |||
reopen = self.close_project_by_name(project) | |||
os.rename(pfile, new_pfile) | |||
os.rename(wsfile, new_wsfile) | |||
j = JsonFile(new_wsfile) | |||
data = j.load({}) | |||
if 'project' in data: | |||
data['project'] = '%s.sublime-project' % os.path.basename(new_project) | |||
j.save(data) | |||
if not self.which_project_dir(pfile): | |||
for pdir in self.projects_path: | |||
library = os.path.join(pdir, 'library.json') | |||
if os.path.exists(library): | |||
j = JsonFile(library) | |||
data = j.load([]) | |||
if pfile in data: | |||
data.remove(pfile) | |||
data.append(new_pfile) | |||
j.save(data) | |||
if reopen: | |||
# reload projects info | |||
self.__init__(self.window) | |||
self.open_in_new_window(new_project) | |||
def show_input_panel(): | |||
v = self.window.show_input_panel('New project name:', | |||
project, | |||
rename_callback, | |||
None, | |||
None) | |||
v.run_command('select_all') | |||
sublime.set_timeout(show_input_panel, 100) |
@ -0,0 +1,28 @@ | |||
import sublime | |||
import sublime_plugin | |||
from . import __pkg_name__ | |||
class PmReadmeCommand(sublime_plugin.TextCommand): | |||
def run(self, edit): | |||
v = self.view.window().new_file() | |||
v.set_name(__pkg_name__ + ': Readme') | |||
v.settings().set('gutter', False) | |||
v.insert(edit, 0, sublime.load_resource('Packages/' + __pkg_name__ + '/README.md')) | |||
v.set_syntax_file('Packages/Markdown/Markdown.sublime-syntax') | |||
v.set_read_only(True) | |||
v.set_scratch(True) | |||
class PmChangelogCommand(sublime_plugin.TextCommand): | |||
def run(self, edit): | |||
v = self.view.window().new_file() | |||
v.set_name(__pkg_name__ + ': Changelog') | |||
v.settings().set('gutter', False) | |||
v.insert(edit, 0, sublime.load_resource('Packages/' + __pkg_name__ + '/CHANGELOG.md')) | |||
v.set_syntax_file('Packages/Markdown/Markdown.sublime-syntax') | |||
v.set_read_only(True) | |||
v.set_scratch(True) |
@ -0,0 +1,112 @@ | |||
import sublime | |||
import sublime_plugin | |||
from .project_manager import Manager | |||
def cancellable(func): | |||
def _ret(self, action): | |||
if action >= 0: | |||
func(self, action) | |||
elif action < 0 and self.caller == 'manager': | |||
sublime.set_timeout(self.run, 10) | |||
return _ret | |||
class ProjectManagerCloseWindow(sublime_plugin.WindowCommand): | |||
def run(self): | |||
if self.window.project_file_name(): | |||
# if it is a project, close the project | |||
self.window.run_command('close_workspace') | |||
else: | |||
self.window.run_command('close_all') | |||
# exit if there are dirty views | |||
if any([v.is_dirty() for v in self.window.views()]): | |||
return | |||
# close the sidebar | |||
self.window.run_command('close_project') | |||
# close the window | |||
self.window.run_command('close_window') | |||
class ProjectManager(sublime_plugin.WindowCommand): | |||
def show_quick_panel(self, items, on_done): | |||
sublime.set_timeout( | |||
lambda: self.window.show_quick_panel(items, on_done), | |||
10) | |||
def run(self, action=None, caller=None): | |||
self.manager = Manager(self.window) | |||
if action is None: | |||
self.show_options() | |||
elif action == 'add_project': | |||
self.manager.add_project() | |||
elif action == 'import_sublime_project': | |||
self.manager.import_sublime_project() | |||
elif action == 'clear_recent_projects': | |||
self.manager.clear_recent_projects() | |||
elif action == 'remove_dead_projects': | |||
self.manager.clean_dead_projects() | |||
else: | |||
self.caller = caller | |||
callback = eval('self.on_' + action) | |||
self.projects, display = self.manager.display_projects() | |||
if not self.projects: | |||
sublime.message_dialog('Project list is empty.') | |||
return | |||
self.show_quick_panel(display, callback) | |||
def show_options(self): | |||
items = [ | |||
['Open Project', 'Open project in the current window'], | |||
['Open Project in New Window', 'Open project in a new window'], | |||
['Append Project', 'Append project to current window'], | |||
['Edit Project', 'Edit project settings'], | |||
['Rename Project', 'Rename project'], | |||
['Remove Project', 'Remove from Project Manager'], | |||
['Add New Project', 'Add current folders to Project Manager'], | |||
['Import Project', 'Import current .sublime-project file'], | |||
['Clear Recent Projects', 'Clear Recent Projects'], | |||
['Remove Dead Projects', 'Remove Dead Projects'] | |||
] | |||
def callback(a): | |||
if a < 0: | |||
return | |||
elif a <= 5: | |||
actions = ['switch', 'new', 'append', 'edit', 'rename', 'remove'] | |||
self.run(action=actions[a], caller='manager') | |||
elif a == 6: | |||
self.run(action='add_project') | |||