diff --git a/.vim/ftplugin/rust.vim b/.vim/ftplugin/rust.vim index e845b95..dd9a1fd 100644 --- a/.vim/ftplugin/rust.vim +++ b/.vim/ftplugin/rust.vim @@ -1,7 +1,10 @@ -if !has('nvim') - nnoremap :ALEGoToDefinition -endif +" f5 runs the current project +nmap :!clear:!cargo run -q +nmap :!clear:!cargo run -nnoremap :Cargo run +" ctrl-f5 lets you run the current program but prompts you for the cli args +nmap :!clear:!cargo run -- -let g:rustfmt_autosave = 1 +nmap :!clear:!cargo run +nmap :!clear:!cargo check +nmap :!clear:!cargo test diff --git a/config.ini b/config.ini index 33bcff4..df35adb 100644 --- a/config.ini +++ b/config.ini @@ -1,34 +1,46 @@ # items in the [home] section are copied into the home directory with the same # relative location as they appear in the dotfiles repo [home] -files= - # sync vim plugin directories but not all of .vim - .vim/ftplugin - .vim/pack - - # individual files to sync - .bash_profile - .bashrc - .screenrc - .tmux.conf - .vimrc - .config/htop/htoprc - .config/lazydocker/config.yml - -# items in map.posix define a place where config files will be placed on posix -# systems relative to the user's home directory -[map.posix] -nvim/init.vim=.config/nvim/init.vim -scripts/winmode=bin/winmode - -# items in the map.windows section defines a place where config files will be -# placed on windows, relative to the user's home directory -[map.windows] -.vimrc=AppData/Local/nvim/init.vim -Vundle.vim=AppData/Local/nvim/bundle/Vundle.vim -.vim/ftplugin=AppData/Local/nvim/ftplugin -packer.nvim=AppData/Local/nvim-data/site/pack/packer/start/packer.nvim -nvim/lua=AppData/Local/nvim/lua -cargo-config.toml=.cargo/config.toml - -[cargo:gitui] +files: + # sync vim plugin directories but not all of .vim + .vim/ftplugin + .vim/pack + + # individual files to sync + .bash_profile + .bashrc + .vimrc + + cargo-config.toml > .cargo/config.toml + +[home posix] +when: not host.is_windows + +files: + .screenrc + .tmux.conf + .config/lazydocker/config.yml + .config/htop/htoprc + + nvim/init.vim > .config/nvim/init.vim + +[home wsl] +when: host.is_wsl + +files: + scripts/winmode > bin/winmode + +[link-files AppData] +when: host.is_windows + +target_root: ~/AppData/Local + +files: + nvim/lua + + .vimrc > nvim/init.vim + Vundle.vim > nvim/bundle/Vundle.vim + .vim/ftplugin > nvim/ftplugin + packer.nvim > nvim-data/site/pack/packer/start/packer.nvim + +# [cargo:gitui] diff --git a/install b/install index c5d932c..7ec7e8d 100755 --- a/install +++ b/install @@ -3,6 +3,6 @@ installs preferences """ -from prefs import Installer +from prefs.installer import Installer installer = Installer() installer.run() diff --git a/prefs/__init__.py b/prefs/__init__.py deleted file mode 100644 index 2b81710..0000000 --- a/prefs/__init__.py +++ /dev/null @@ -1,5 +0,0 @@ -""" -prefs is a preferences manager -""" - -from .installer import Installer diff --git a/prefs/home.py b/prefs/home.py index 3702654..bd80805 100644 --- a/prefs/home.py +++ b/prefs/home.py @@ -1,33 +1,32 @@ -class Home: - def __init__(self, label = None): - self.label = label +from . import host +from .linker import Linker - @classmethod - def from_name(cls, name): - """ - Builds a Home struct from a given name - """ - if name.startswith('home'): - label = name.removeprefix('home').strip() - return cls(label) - return None +class Home: + resource_name = 'home' - def parse_section(self, section): - mapping = { - 'files': self.parse_files - } - for key in section: - if fn := mapping.get(key): - val = section[key] - fn(val) - else: - raise KeyError(f"Home has no such config key: {key}") + def __init__(self, label, section): + self.label = label or None + self.parse_files(section.get('files', '')) def parse_files(self, text): + self.files = [] lines = [s for s in text.splitlines() if s] - print(f" parse_files: {lines}") + for line in lines: + parts = line.split(">", 2) + if len(parts) == 1: + pair = (parts[0].strip(), parts[0].strip()) + else: + pair = (parts[0].strip(), parts[1].strip()) + self.files.append(pair) def __repr__(self): if self.label: return f'' return '' + + def run(self): + linker = Linker(host.dotfiles_root, host.home) + for pair in self.files: + source_path = host.dotfiles_root / pair[0] + target_path = host.home / pair[1] + linker.link(source_path, target_path) diff --git a/prefs/host.py b/prefs/host.py index b381dfc..362fa4b 100644 --- a/prefs/host.py +++ b/prefs/host.py @@ -10,7 +10,7 @@ import sys import pathlib from functools import cached_property -class _Host: +class Host: """ hacking the python module system a little to make the module look like a singleton object so that it can have properties @@ -54,5 +54,11 @@ class _Host: here = pathlib.Path(os.path.realpath(__file__)) return here.parent.parent + @property + def home(self): + """ + the home directory + """ + return pathlib.Path.home() -sys.modules[__name__] = _Host() +sys.modules[__name__] = Host() diff --git a/prefs/installer.py b/prefs/installer.py index b26ab99..416042c 100644 --- a/prefs/installer.py +++ b/prefs/installer.py @@ -26,105 +26,18 @@ class Installer: parser.optionxform = str parser.read_file(config_file) return parser - self._config = parser def run(self): - """ - runs the install process - """ - if self.options.x: - self.run_x() - return - - if host.is_windows and not host.is_admin: - print("You are not admin: admin is required on Windows") - sys.exit(1) - - config = self.config - for section_name in config.sections(): - try: - T = self.parse_section_name(section_name) - except ValueError as e: - print(f"error reading section: {e}") - continue - section = T.from_section(section_name, **config[section_name]) - print(f"Section: {section}") - section.run() - - print("linking in home files") - home = self.options.config['home'] - home_files = filter(None, home['files'].splitlines()) - for fname in home_files: - print(f"\n{fname}") - path = pathlib.Path(fname) - self.map_file(path, path) - - if host.is_linux: - self.map_section('map.posix') - self.map_section('map.windows') - - if host.is_windows: - self.map_section('map.windows') - - def run_x(self): - for section_name in self.config.sections(): - print("sections:") - print(f" {section_name}") - if r := Resource.from_name(section_name): - print(f" {r}") - r.parse_section(self.config[section_name]) - - @cached_property - def targets(self): - """ - defines all of the places where preferences files will be installed - """ - if host.is_linux: - return [targets.Linux()] - - if host.is_windows: - return [targets.Windows()] - - return [] - - def map_file(self, source_path, target_path): - if not source_path.is_absolute(): - source_path = host.dotfiles_root / source_path - - print(f"source path: {source_path}") - print(f"source drive: {source_path.drive}") - if not source_path.exists(): - print("skip: no such file\n") - return - - for target in self.targets: - target.map_file(source_path, target_path) - - @property - def config_path(self): - # pylint: disable=missing-function-docstring - return self.options.config - - # @cached_property - # def config(self): - # """ - # the contents of our configuration file - # """ - # with open(self.config_path, 'r', encoding='utf-8') as config_fp: - # log.debug("loading config from path %s", self.config_path) - # return json.load(config_fp) - - def map_section(self, section_name): - section = self.options.config[section_name] - for source_name in section: - target_name = section[source_name] - source_path = pathlib.Path(source_name) - target_path = pathlib.Path(target_name) - print(f"Map {source_path} to {target_path}") - self.map_file(source_path, target_path) - - def parse_section_name(self, section_name): - if section_name == "home": - return sections.Home - raise ValueError(f"unprocessable section name: {section_name}") - + resources = [] + for name in self.config.sections(): + section = self.config[name] + if self.when(section): + r = Resource.from_section(name, section) + resources.append(r) + for r in resources: + r.run() + + def when(self, section): + if clause := section.get('when', None): + return eval(clause) + return True diff --git a/prefs/linker.py b/prefs/linker.py new file mode 100644 index 0000000..19b9435 --- /dev/null +++ b/prefs/linker.py @@ -0,0 +1,56 @@ +import pathlib + +from . import host + +class Linker: + """ + Linker links files from soome source to some target + """ + def __init__(self, source_root, target_root): + self.source_root = source_root + self.target_root = target_root + + def link(self, source_path, target_path): + if not source_path.is_absolute(): + source_path = self.source_root / source_path + if not target_path.is_absolute(): + target_path = self.target_root / target_path + + if not target_path.parent.exists(): + print("creating missing parent directories for target") + parent_dir = target_path.parent + parent_dir.mkdir(parents=True) + + print(f"{source_path} -> {target_path}") + +class LinkFiles: + resource_name = 'link-files' + + def __init__(self, label, section): + self.label = label + self.target_root = pathlib.Path(section['target_root']).expanduser() + self.source_root = host.dotfiles_root + self.parse_files(section.get('files', '')) + + def parse_files(self, text): + self.files = [] + lines = [s for s in text.splitlines() if s] + for line in lines: + parts = line.split(">", 2) + if len(parts) == 1: + pair = (parts[0].strip(), parts[0].strip()) + else: + pair = (parts[0].strip(), parts[1].strip()) + self.files.append(pair) + + def __repr__(self): + if self.label: + return f'' + return '' + + def run(self): + linker = Linker(host.dotfiles_root, self.target_root) + for pair in self.files: + source_path = pathlib.Path(pair[0]) + target_path = pathlib.Path(pair[1]) + linker.link(source_path, target_path) diff --git a/prefs/resource.py b/prefs/resource.py index 69da78a..d932f16 100644 --- a/prefs/resource.py +++ b/prefs/resource.py @@ -1,7 +1,8 @@ from .home import Home +from .linker import LinkFiles class Resource: - resource_types = [Home] + resource_types = [Home, LinkFiles] @classmethod def from_name(cls, name): @@ -9,9 +10,17 @@ class Resource: from_name is to be implemented by resource classes """ for T in cls.resource_types: - if r := T.from_name(name): - return r - return None + if T.resource_name == name: + return T + raise ValueError(f"No section type has name {name}") - def when(self, when_text): - pass + @classmethod + def from_section(cls, name, section): + parts = name.split(' ') + name = parts[0] + try: + label = parts[1] + except IndexError: + label = None + T = cls.from_name(name) + return T(label, section)