├── .gitignore ├── .vscode └── settings.json ├── LICENSE ├── README.md ├── dev_notes.md ├── setup.py └── snippet ├── __init__.py └── plugin.py /.gitignore: -------------------------------------------------------------------------------- 1 | .idea/ 2 | env/ 3 | dist/ 4 | build/ 5 | __pycache__/ 6 | .pytest_cache/ 7 | *.egg-info/ 8 | 9 | !.gitkeep 10 | 11 | -------------------------------------------------------------------------------- /.vscode/settings.json: -------------------------------------------------------------------------------- 1 | { 2 | "python.linting.enabled": true, 3 | "python.pythonPath": "/Users/mprivat/anaconda3/bin/python", 4 | "python.formatting.provider": "yapf", 5 | "python.linting.pylintEnabled": true 6 | } -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) Availity 2016-present 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # mkdocs-snippet-plugin 2 | 3 | An mkdocs plugin that injects snippets from a file in a git repository. 4 | 5 | ## Installation 6 | 7 | > **Note:** This package requires MkDocs version 0.17 or higher. 8 | 9 | Install the package with pip: 10 | 11 | ```bash 12 | pip install mkdocs-snippet-plugin 13 | ``` 14 | 15 | Enable the plugin in your `mkdocs.yml`: 16 | 17 | ```yaml 18 | plugins: 19 | - snippet: 20 | base_path: docs 21 | ``` 22 | 23 | The `base_path` entry should point to the root of your documentation site, usually defaulted to `docs`. 24 | 25 | ## How to use it 26 | 27 | If you have a markdown file in a remote Git repostory, and you want to extract a sections from it, add the following to your documentation markdown in mkdocs: 28 | 29 | ``` 30 | {{ snippet('git@github.com:mprivat/mkdocs-snippet-plugin.git', 'README.md', '## Installation') }} 31 | ``` 32 | 33 | It will download the file you specify from the Git URL, extract the section you ask for (including its subsections) and inject that into your mkdocs file at render time. 34 | 35 | If the remote file has references to images, those will also be downloaded and placed in a `_gen` folder in the mkdocs hierarchy. You will probably want to include `**/gen_` in your `.gitignore` file so you don't put those into your git repository unless you want them there. 36 | -------------------------------------------------------------------------------- /dev_notes.md: -------------------------------------------------------------------------------- 1 | # To develop locally, install in edit mode: 2 | 3 | ``` 4 | pip install -e /path/to/mkdocs-snippet-plugin/ 5 | ``` 6 | 7 | # Dependencies 8 | 9 | ``` 10 | pip install gitpython 11 | ``` 12 | -------------------------------------------------------------------------------- /setup.py: -------------------------------------------------------------------------------- 1 | import os 2 | from setuptools import setup, find_packages 3 | 4 | 5 | def read(fname): 6 | return open(os.path.join(os.path.dirname(__file__), fname)).read() 7 | 8 | 9 | setup( 10 | name='mkdocs-snippet-plugin', 11 | version='1.0.2', 12 | description= 13 | 'An mkdocs plugin that injects snippets from a file in a git repository', 14 | long_description=read('README.md'), 15 | long_description_content_type='text/markdown', 16 | keywords='mkdocs python markdown snippet', 17 | url='https://github.com/mprivat/mkdocs-snippet-plugin', 18 | author='Michael Privat', 19 | author_email='Michael.Privat@availity.com', 20 | license='MIT', 21 | python_requires='>=2.7.9,!=3.0.*,!=3.1.*,!=3.2.*,!=3.3.*', 22 | install_requires=['mkdocs>=0.17', 'gitpython'], 23 | classifiers=[ 24 | 'Development Status :: 5 - Production/Stable', 25 | 'Intended Audience :: Developers', 26 | 'Intended Audience :: Information Technology', 27 | 'License :: OSI Approved :: MIT License', 28 | 'Programming Language :: Python', 29 | 'Programming Language :: Python :: 2', 30 | 'Programming Language :: Python :: 2.7', 31 | 'Programming Language :: Python :: 3', 32 | 'Programming Language :: Python :: 3.4', 33 | 'Programming Language :: Python :: 3.5', 34 | 'Programming Language :: Python :: 3.6', 35 | 'Programming Language :: Python :: 3.7' 36 | ], 37 | packages=find_packages(exclude=['*.tests']), 38 | entry_points={ 39 | 'mkdocs.plugins': ['snippet = snippet.plugin:SnippetPlugin'] 40 | }) 41 | -------------------------------------------------------------------------------- /snippet/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mprivat/mkdocs-snippet-plugin/9db015b108bae18a984e684136ba03bdd4cdba67/snippet/__init__.py -------------------------------------------------------------------------------- /snippet/plugin.py: -------------------------------------------------------------------------------- 1 | from mkdocs.plugins import BasePlugin 2 | 3 | from jinja2 import Template 4 | from git import Repo 5 | import uuid 6 | import shutil 7 | import re 8 | import os 9 | import mkdocs 10 | import sys 11 | 12 | 13 | class SnippetPlugin(BasePlugin): 14 | 15 | if sys.version_info[0] == 3: 16 | string_types = str 17 | else: 18 | string_types = basestring 19 | 20 | config_scheme = (('base_path', 21 | mkdocs.config.config_options.Type( 22 | string_types, default=None)), ) 23 | 24 | page = None 25 | repos = dict() 26 | 27 | def copy_markdown_images(self, tmpRoot, markdown): 28 | # root = os.path.dirname(os.path.dirname(self.page.url)) 29 | root = self.page.url 30 | 31 | paths = [] 32 | 33 | p = re.compile("!\[.*\]\((.*)\)") 34 | it = p.finditer(markdown) 35 | for match in it: 36 | path = match.group(1) 37 | paths.append(path) 38 | 39 | destinationPath = os.path.realpath(self.config['base_path'] + "/" + 40 | root + "/gen_/" + path) 41 | 42 | if not os.path.isfile(destinationPath): 43 | print("Copying image: " + path + " to " + destinationPath) 44 | 45 | os.makedirs(os.path.dirname(destinationPath), exist_ok=True) 46 | shutil.copyfile(tmpRoot + "/" + path, destinationPath) 47 | 48 | for path in paths: 49 | markdown = markdown.replace(path, "gen_/" + path) 50 | 51 | return markdown 52 | 53 | def markdown_snippet(self, git_url, file_path, section_name): 54 | p = re.compile("^#+ ") 55 | m = p.search(section_name) 56 | if m: 57 | section_level = m.span()[1] - 1 58 | 59 | root = "" 60 | if self.repos.get(git_url) is None: 61 | root = "/tmp/" + uuid.uuid4().hex 62 | self.repos[git_url] = root 63 | Repo.clone_from(git_url, root) 64 | else: 65 | root = self.repos[git_url] 66 | 67 | content = "" 68 | with open(root + '/' + file_path, 'r') as myfile: 69 | content = myfile.read() 70 | 71 | p = re.compile("^" + section_name + "$", re.MULTILINE) 72 | start = p.search(content) 73 | start_index = start.span()[1] 74 | 75 | p = re.compile("^#{1," + str(section_level) + "} ", re.MULTILINE) 76 | 77 | result = "" 78 | end = p.search(content[start_index:]) 79 | if end: 80 | end_index = end.span()[0] 81 | result = content[start_index:end_index + start_index] 82 | else: 83 | result = content[start_index:] 84 | 85 | # If there are any images, find them, copy them 86 | result = self.copy_markdown_images(root, result) 87 | return result 88 | else: 89 | return "Markdown section doesn't exist in source" 90 | 91 | def snippet(self, git_url, file_path, section_name): 92 | if file_path.endswith('.md'): 93 | return self.markdown_snippet(git_url, file_path, section_name) 94 | else: 95 | return "File format not supported" 96 | 97 | def on_page_markdown(self, markdown, page, config, **kwargs): 98 | self.page = page 99 | md_template = Template(markdown) 100 | return md_template.render(snippet=self.snippet) 101 | 102 | def on_post_build(self, config): 103 | for key in self.repos: 104 | shutil.rmtree(self.repos[key]) 105 | self.repos.clear() 106 | --------------------------------------------------------------------------------