From e8bb783abb1aa5501ac9e9272a696712956d44ef Mon Sep 17 00:00:00 2001 From: Michael Shepanski Date: Thu, 14 Apr 2022 22:55:48 -0400 Subject: [PATCH 1/3] Auto discover projects. --- project_manager.py | 78 +++++++++++++++++++++++++++++++- project_manager.sublime-settings | 22 ++++++++- 2 files changed, 98 insertions(+), 2 deletions(-) diff --git a/project_manager.py b/project_manager.py index dba01db..cb08c2a 100644 --- a/project_manager.py +++ b/project_manager.py @@ -5,11 +5,12 @@ import platform import re import copy - +import json from .json_file import JsonFile SETTINGS_FILENAME = 'project_manager.sublime-settings' +REPODIRS = ('.git', '.svn', '.hg') pm_settings = None @@ -135,6 +136,31 @@ def f(*args, **kwargs): return f +def find_repos(basedir, max_level=1, ignores=None): + """ os.walk with max depth. https://stackoverflow.com/a/234329 """ + ignores = [i.lower() for i in (ignores or [])] + basedir = basedir.rstrip(os.path.sep) + assert os.path.isdir(basedir) + num_sep = basedir.count(os.path.sep) + for root, dirs, files in os.walk(basedir): + for dirname in dirs: + if not _is_ignored(root, ignores) and dirname in REPODIRS: + yield root, dirname + # yield root, dirs, files + num_sep_this = root.count(os.path.sep) + if num_sep + max_level <= num_sep_this: + del dirs[:] + + +def _is_ignored(dirname, ignores): + parts = dirname.split(os.path.sep) + for ignore in ignores: + for part in parts: + if ignore == part.lower(): + return True + return False + + class ProjectsInfo: _instance = None @@ -205,6 +231,7 @@ def refresh_projects(self): raise Exception("Directory \"{}\" does not exists.".format(self._primary_dir)) self._info = self._get_all_projects_info() + self._auto_discover_repos() def _get_all_projects_info(self): all_projects_info = {} @@ -265,6 +292,55 @@ def _load_sublime_project_files(self, folder): os.rmdir(d) return pfiles + def _auto_discover_repos(self): + try: + user_discover_dirs = pm_settings.get('auto_discover_base_directories') + + # Get a list of discover_dirs to search for projects + node = computer_name() + if isinstance(user_discover_dirs, dict): + if node in user_discover_dirs: + user_discover_dirs = user_discover_dirs[node] + else: + user_discover_dirs = [] + if isinstance(user_discover_dirs, str): + user_discover_dirs = [user_discover_dirs] + + discover_dirs = [] + for folder in user_discover_dirs: + p = expand_path(folder) + p = p.replace("$hostname", node) + discover_dirs.append(p) + + # Convert existing project info to dict for easy lookup + existing = {info['folder']:key for key,info in self._info.items()} + + # Find new repo directories + max_depth = pm_settings.get('auto_discover_max_recursion_depth', 1) + ignores = pm_settings.get('auto_discover_ignored_folders', []) + for basedir in discover_dirs: + for folder, repodir in find_repos(basedir, max_depth, ignores): + if folder not in existing: + project_name = os.path.basename(folder) + self._create_new_project(project_name, folder) + except Exception as err: + print('Error auto discovering repos: {}' % err) + + def _create_new_project(self, project_name, folder): + data = {"folders": [{ + "binary_file_patterns": [], + "file_exclude_patterns": [], + "folder_exclude_patterns": [], + "name": project_name, + "path": folder, + }]} + + filename = '{}.sublime-project'.format(project_name) + filepath = os.path.join(self.primary_dir(), filename) + print('Creating {} -> {}'.format(filename, folder)) + with open(filepath, 'w') as handle: + json.dump(data, handle, indent=2) + class Manager: def __init__(self, window): diff --git a/project_manager.sublime-settings b/project_manager.sublime-settings index c7c1391..4fd03e8 100644 --- a/project_manager.sublime-settings +++ b/project_manager.sublime-settings @@ -36,5 +36,25 @@ // How the projects in the project list should be formatted. Supported variables // are "project_name" and "active_project_indicator". For inactive projects // "active_project_indicator" will be an empty string. - "project_display_format": "{project_name}{active_project_indicator}" + "project_display_format": "{project_name}{active_project_indicator}", + + // Auto Discover Projects + // Any folders listed auto_discover_base_directories will be recursively + // searched for git, hg, or svn projects and a new .sublime-project will + // be created if one does not already exist. Format for this value is + // similar to projects above anc can be a string, list of strings or a + // dict containing {"hostname": ["path"]}. The variable `$hostname` + // will expand to the computer name in any paths. + "auto_discover_base_directories": {}, + + // Folder names to ignore when searching for projects. + "auto_discover_ignored_folders": [ + "node_modules", + "out", + "typings", + "test" + ], + + // Max depth to recurse when searching for projects. + "auto_discover_max_recursion_depth": 2 } From 2dd2a3946c90b35b9c2c28779a156fb0e6865a17 Mon Sep 17 00:00:00 2001 From: Michael Shepanski Date: Thu, 14 Apr 2022 23:08:22 -0400 Subject: [PATCH 2/3] Ignore bad paths. Better print messages. --- project_manager.py | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/project_manager.py b/project_manager.py index cb08c2a..1a27550 100644 --- a/project_manager.py +++ b/project_manager.py @@ -319,12 +319,14 @@ def _auto_discover_repos(self): max_depth = pm_settings.get('auto_discover_max_recursion_depth', 1) ignores = pm_settings.get('auto_discover_ignored_folders', []) for basedir in discover_dirs: + if not os.path.isdir(basedir): + print('ProjectManager: {} is not a valid path'.format(basedir)) for folder, repodir in find_repos(basedir, max_depth, ignores): if folder not in existing: project_name = os.path.basename(folder) self._create_new_project(project_name, folder) except Exception as err: - print('Error auto discovering repos: {}' % err) + print(err) def _create_new_project(self, project_name, folder): data = {"folders": [{ @@ -337,7 +339,7 @@ def _create_new_project(self, project_name, folder): filename = '{}.sublime-project'.format(project_name) filepath = os.path.join(self.primary_dir(), filename) - print('Creating {} -> {}'.format(filename, folder)) + print('ProjectManager: Creating {} -> {}'.format(filename, folder)) with open(filepath, 'w') as handle: json.dump(data, handle, indent=2) From b1f029dd34b2982c534a22e33d82de19e8913cff Mon Sep 17 00:00:00 2001 From: Michael Shepanski Date: Fri, 15 Apr 2022 12:40:47 -0400 Subject: [PATCH 3/3] Use JsonFile instead if json. --- project_manager.py | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/project_manager.py b/project_manager.py index 1a27550..d143296 100644 --- a/project_manager.py +++ b/project_manager.py @@ -5,7 +5,6 @@ import platform import re import copy -import json from .json_file import JsonFile @@ -340,8 +339,7 @@ def _create_new_project(self, project_name, folder): filename = '{}.sublime-project'.format(project_name) filepath = os.path.join(self.primary_dir(), filename) print('ProjectManager: Creating {} -> {}'.format(filename, folder)) - with open(filepath, 'w') as handle: - json.dump(data, handle, indent=2) + JsonFile(filepath).save(data) class Manager: