mirror of
https://github.com/nix-community/nixvim.git
synced 2026-08-29 10:20:52 -05:00
351 lines
10 KiB
Python
Executable File
351 lines
10 KiB
Python
Executable File
#!/usr/bin/env nix-shell
|
|
#!nix-shell -i python3 -p python3
|
|
|
|
import os
|
|
import re
|
|
from argparse import ArgumentParser
|
|
from collections.abc import Callable, Sequence
|
|
from typing import cast
|
|
|
|
# Template for default.nix (colorscheme)
|
|
colorscheme_nix_template = """{{ lib, ... }}:
|
|
lib.nixvim.plugins.mkNeovimPlugin {{
|
|
name = "{name}";
|
|
moduleName = "LUA_MODULE_NAME"; # TODO replace (or remove entirely if it is the same as `name`)
|
|
package = "{package}";
|
|
|
|
isColorscheme = true;
|
|
colorscheme = "COLORSCHEME_NAME"; # TODO replace (or set to null if it has multiple colorschemes or doesn't need to set colorscheme, or remove completely if same as name)
|
|
|
|
{maintainer_todo}maintainers = [ lib.maintainers.{maintainer} ];
|
|
|
|
# TODO provide an example for the `settings` option (or remove entirely if there is no useful example)
|
|
# NOTE you can use `lib.literalExpression` or `lib.literalMD` if needed
|
|
settingsExample = {{
|
|
foo = 42;
|
|
bar.__raw = "function() print('hello') end";
|
|
}};
|
|
}}
|
|
"""
|
|
|
|
# Template for test file (plugin)
|
|
test_nix_template = """{{
|
|
empty = {{
|
|
plugins.{name}.enable = true;
|
|
}};
|
|
|
|
defaults = {{
|
|
plugins.{name} = {{
|
|
enable = true;
|
|
settings = {{
|
|
foo = 42;
|
|
bar.__raw = "function() print('hello') end";
|
|
}};
|
|
}};
|
|
}};
|
|
}}
|
|
"""
|
|
|
|
# Template for test file (colorscheme)
|
|
colorscheme_test_nix_template = """{{
|
|
empty = {{
|
|
colorscheme = "COLORSCHEME_NAME"; # TODO replace (or remove completely if doesn't need to set colorscheme)
|
|
colorschemes.{name}.enable = true;
|
|
}};
|
|
|
|
defaults = {{
|
|
colorscheme = "COLORSCHEME_NAME"; # TODO replace (or remove completely if doesn't need to set colorscheme)
|
|
|
|
colorschemes.{name} = {{
|
|
enable = true;
|
|
settings = {{
|
|
foo = 42;
|
|
bar.__raw = "function() print('hello') end";
|
|
}};
|
|
}};
|
|
}};
|
|
}}
|
|
"""
|
|
|
|
|
|
ModuleFileCreator = Callable[[str, str, str, str, str, bool, bool], None]
|
|
|
|
|
|
def to_kebab_case(input_string: str) -> str:
|
|
"""
|
|
Convert a string to kebab-case.
|
|
|
|
Args:
|
|
input_string (str): The input string to convert.
|
|
|
|
Returns:
|
|
str: The converted kebab-case string.
|
|
"""
|
|
# Replace non-alphanumeric characters with hyphens
|
|
input_string = re.sub(r"[\W_]+", "-", input_string).lower()
|
|
|
|
return input_string.strip("-")
|
|
|
|
|
|
def strip_nvim(input_string: str) -> str:
|
|
"""
|
|
Remove 'nvim' prefix or suffix from a string.
|
|
|
|
Args:
|
|
input_string (str): The input string to process.
|
|
|
|
Returns:
|
|
str: The string with 'nvim' removed from start or end.
|
|
"""
|
|
# Remove leading and trailing standalone 'nvim'
|
|
input_string = re.sub(r"(^nvim-|-nvim$|^nvim$)", "", input_string)
|
|
|
|
return input_string.strip("-")
|
|
|
|
|
|
def load_template(root_dir: str, relative_path: str) -> str:
|
|
with open(os.path.join(root_dir, relative_path), encoding="utf-8") as f:
|
|
return f.read()
|
|
|
|
|
|
def replace_once(template: str, old: str, new: str) -> str:
|
|
if old not in template:
|
|
raise ValueError(f"Template placeholder not found: {old}")
|
|
return template.replace(old, new, 1)
|
|
|
|
|
|
def render_plugin_template(
|
|
template: str,
|
|
name: str,
|
|
package: str,
|
|
maintainer: str,
|
|
is_default_maintainer: bool = False,
|
|
) -> str:
|
|
maintainer_todo = (
|
|
" # TODO replace with your name\n" if is_default_maintainer else ""
|
|
)
|
|
|
|
content = replace_once(template, 'name = "my-plugin";', f'name = "{name}";')
|
|
content = replace_once(
|
|
content,
|
|
'moduleName = "my-plugin";',
|
|
f'moduleName = "{name}";',
|
|
)
|
|
content = replace_once(
|
|
content,
|
|
'package = "my-plugin-nvim"; # TODO replace',
|
|
f'package = "{package}";',
|
|
)
|
|
return replace_once(
|
|
content,
|
|
" # TODO replace with your name\n maintainers = [ lib.maintainers.MyName ];",
|
|
f"{maintainer_todo} maintainers = [ lib.maintainers.{maintainer} ];",
|
|
)
|
|
|
|
|
|
def create_nix_file(
|
|
file_path: str,
|
|
template: str,
|
|
name: str,
|
|
package: str,
|
|
maintainer: str,
|
|
is_default_maintainer: bool = False,
|
|
dry_run: bool = False,
|
|
) -> None:
|
|
"""
|
|
Create a nix file from a template.
|
|
|
|
Args:
|
|
file_path (str): The path to the file to create.
|
|
template (str): The template string to use for the file content.
|
|
name (str): The name of the plugin.
|
|
package (str): The package name of the plugin.
|
|
maintainer (str): The maintainer name from lib.maintainers.
|
|
is_default_maintainer (bool): Whether the maintainer is the default value.
|
|
dry_run (bool): If True, only print what would be written instead of actually writing.
|
|
"""
|
|
# Add a TODO comment if using the default maintainer
|
|
maintainer_todo = (
|
|
"# TODO replace with your name \n " if is_default_maintainer else ""
|
|
)
|
|
|
|
content = template.format(
|
|
name=name,
|
|
package=package,
|
|
maintainer=maintainer,
|
|
maintainer_todo=maintainer_todo,
|
|
)
|
|
write_to_file(file_path, content, dry_run)
|
|
|
|
|
|
def create_plugin_file(
|
|
file_path: str,
|
|
template: str,
|
|
name: str,
|
|
package: str,
|
|
maintainer: str,
|
|
is_default_maintainer: bool = False,
|
|
dry_run: bool = False,
|
|
) -> None:
|
|
"""
|
|
Create a plugin nix file from plugins/TEMPLATE.nix.
|
|
"""
|
|
content = render_plugin_template(
|
|
template,
|
|
name,
|
|
package,
|
|
maintainer,
|
|
is_default_maintainer,
|
|
)
|
|
write_to_file(file_path, content, dry_run)
|
|
|
|
|
|
def create_test_file(
|
|
file_path: str,
|
|
template: str,
|
|
name: str,
|
|
dry_run: bool = False,
|
|
) -> None:
|
|
"""
|
|
Create a test file from a template.
|
|
|
|
Args:
|
|
file_path (str): The path to the file to create.
|
|
template (str): The template string to use for the file content.
|
|
name (str): The name of the plugin.
|
|
dry_run (bool): If True, only print what would be written instead of actually writing.
|
|
"""
|
|
content = template.format(name=name)
|
|
write_to_file(file_path, content, dry_run)
|
|
|
|
|
|
def write_to_file(file_path: str, content: str, dry_run: bool = False) -> None:
|
|
"""
|
|
Makes sure directories exist and write content to a file.
|
|
|
|
Args:
|
|
file_path (str): The path to the file to write.
|
|
content (str): The content to write to the file.
|
|
dry_run (bool): If True, only print what would be written instead of actually writing.
|
|
"""
|
|
if dry_run:
|
|
print(f"Would write to {file_path}:")
|
|
print("=" * 40)
|
|
print(content)
|
|
print("=" * 40)
|
|
return
|
|
|
|
os.makedirs(os.path.dirname(file_path), exist_ok=True)
|
|
with open(file_path, "w", encoding="utf-8") as f:
|
|
_ = f.write(content)
|
|
|
|
|
|
def find_project_root(root_identifiers: Sequence[str]) -> str:
|
|
current_path = os.getcwd()
|
|
while True:
|
|
if all(
|
|
os.path.exists(os.path.join(current_path, root_identifier))
|
|
for root_identifier in root_identifiers
|
|
):
|
|
return current_path
|
|
parent_path = os.path.dirname(current_path)
|
|
if parent_path == current_path:
|
|
raise FileNotFoundError(
|
|
f"Could not find project root containing: {', '.join(root_identifiers)}"
|
|
)
|
|
os.chdir("..")
|
|
current_path = os.getcwd()
|
|
|
|
|
|
# TODO: support interactive unmanaged args
|
|
def main() -> None:
|
|
"""
|
|
Main function to generate default.nix and test files for a new plugin or colorscheme.
|
|
"""
|
|
DEFAULT_MAINTAINER = "YOUR_NAME"
|
|
|
|
parser = ArgumentParser(
|
|
description="Generate default.nix and test files for a new plugin or colorscheme"
|
|
)
|
|
_ = parser.add_argument(
|
|
"originalName", type=str, help="Original name of the new plugin or colorscheme"
|
|
)
|
|
_ = parser.add_argument(
|
|
"--package",
|
|
"-p",
|
|
type=str,
|
|
help="Package name of the new plugin (defaults to normalized version of originalName)",
|
|
)
|
|
_ = parser.add_argument(
|
|
"--maintainer",
|
|
"-m",
|
|
type=str,
|
|
help="Maintainer name (from lib.maintainers)",
|
|
default=DEFAULT_MAINTAINER,
|
|
)
|
|
_ = parser.add_argument(
|
|
"--colorscheme",
|
|
"-c",
|
|
action="store_true",
|
|
help="Create a colorscheme instead of a plugin",
|
|
)
|
|
_ = parser.add_argument(
|
|
"--dry-run",
|
|
"-d",
|
|
action="store_true",
|
|
help="Show what would be written without actually creating files",
|
|
)
|
|
args = parser.parse_args()
|
|
original_name = cast(str, args.originalName)
|
|
package_arg = cast(str | None, args.package)
|
|
maintainer = cast(str, args.maintainer)
|
|
is_colorscheme = cast(bool, args.colorscheme)
|
|
dry_run = cast(bool, args.dry_run)
|
|
|
|
# Calculate name - convert to kebab case and strip nvim
|
|
name = strip_nvim(to_kebab_case(original_name))
|
|
|
|
# Use provided package name or default to normalized original name
|
|
package = package_arg if package_arg else to_kebab_case(original_name)
|
|
|
|
# Check if user provided a maintainer or we're using the default
|
|
is_default_maintainer = maintainer == DEFAULT_MAINTAINER
|
|
|
|
# Define paths
|
|
root_identifiers = ["flake.nix", "plugins/TEMPLATE.nix"]
|
|
root_dir = find_project_root(root_identifiers)
|
|
|
|
if is_colorscheme:
|
|
plugin_template = colorscheme_nix_template
|
|
test_template = colorscheme_test_nix_template
|
|
plugin_path = f"{root_dir}/colorschemes/{name}/default.nix"
|
|
test_path = f"{root_dir}/tests/test-sources/colorschemes/{name}/default.nix"
|
|
create_module_file: ModuleFileCreator = create_nix_file
|
|
else:
|
|
plugin_template = load_template(root_dir, "plugins/TEMPLATE.nix")
|
|
test_template = test_nix_template
|
|
plugin_path = f"{root_dir}/plugins/by-name/{name}/default.nix"
|
|
test_path = f"{root_dir}/tests/test-sources/plugins/by-name/{name}/default.nix"
|
|
create_module_file = create_plugin_file
|
|
|
|
# Create files
|
|
create_module_file(
|
|
plugin_path,
|
|
plugin_template,
|
|
name,
|
|
package,
|
|
maintainer,
|
|
is_default_maintainer,
|
|
dry_run,
|
|
)
|
|
create_test_file(
|
|
test_path,
|
|
test_template,
|
|
name,
|
|
dry_run,
|
|
)
|
|
|
|
|
|
if __name__ == "__main__":
|
|
main()
|