We are currently solving the following issue within our repository. Here is the issue text:
--- BEGIN ISSUE ---
current_page.ancestors only contains direct ancestor and not the full path of the page
I'm using the mkdocs theme and tried to enhance it with a breadcrumb trail. The page navigation is created automatically by mkdocs (I don't use the pages confguration since I have almost 300 pages).
I copied and adapted the `breadcrumbs.html` file from the readthedocs theme and integrated it in `content.html`:

```
<ol class="breadcrumb">
<li><a href="{{ homepage_url }}">Docs</a></li>
{% if current_page %}
  {% for doc in current_page.ancestors %}
    {% if doc.link %}
      <li><a href="{{ doc.link|e }}">{{ doc.title }}</a></li>
    {% else %}
      <li>{{ doc.title }}</li>
    {% endif %}
  {% endfor %}
{% endif %}
{% if current_page %}<li>{{ current_page.title }}</li>{% endif %}
</ol>
```

My file path (starting from the `docs_dir`)  is:
`beheerteam/diensten/algemeen/ActiveDirectory.md`
The generated breadcrumb trail is:
`Docs/algemeen/ActiveDirectory`

`algemeen` is the only part that originates from the loop `for doc in current_page.ancestors`.

Maybe this is a stupid question or it is just not possible, but I couldn't find i in the documentation and I'm just starting with mkdocs and couldn't understand the source on how this works.


--- END ISSUE ---

Below are some code segments, each from a relevant file. One or more of these files may contain bugs.
--- BEGIN FILES ---
Path: `mkdocs/nav.py`
Content:
```
1 # coding: utf-8
2 
3 """
4 Deals with generating the site-wide navigation.
5 
6 This consists of building a set of interlinked page and header objects.
7 """
8 
9 from __future__ import unicode_literals
10 import datetime
11 import logging
12 import os
13 
14 from mkdocs import utils, exceptions
15 
16 log = logging.getLogger(__name__)
17 
18 
19 def filename_to_title(filename):
20     """
21     Automatically generate a default title, given a filename.
22     """
23     if utils.is_homepage(filename):
24         return 'Home'
25 
26     return utils.filename_to_title(filename)
27 
28 
29 class SiteNavigation(object):
30     def __init__(self, pages_config, use_directory_urls=True):
31         self.url_context = URLContext()
32         self.file_context = FileContext()
33         self.nav_items, self.pages = _generate_site_navigation(
34             pages_config, self.url_context, use_directory_urls)
35         self.homepage = self.pages[0] if self.pages else None
36         self.use_directory_urls = use_directory_urls
37 
38     def __str__(self):
39         return ''.join([str(item) for item in self])
40 
41     def __iter__(self):
42         return iter(self.nav_items)
43 
44     def walk_pages(self):
45         """
46         Returns each page in the site in turn.
47 
48         Additionally this sets the active status of the pages and headers,
49         in the site navigation, so that the rendered navbar can correctly
50         highlight the currently active page and/or header item.
51         """
52         page = self.homepage
53         page.set_active()
54         self.url_context.set_current_url(page.abs_url)
55         self.file_context.set_current_path(page.input_path)
56         yield page
57         while page.next_page:
58             page.set_active(False)
59             page = page.next_page
60             page.set_active()
61             self.url_context.set_current_url(page.abs_url)
62             self.file_context.set_current_path(page.input_path)
63             yield page
64         page.set_active(False)
65 
66     @property
67     def source_files(self):
68         if not hasattr(self, '_source_files'):
69             self._source_files = set([page.input_path for page in self.pages])
70         return self._source_files
71 
72 
73 class URLContext(object):
74     """
75     The URLContext is used to ensure that we can generate the appropriate
76     relative URLs to other pages from any given page in the site.
77 
78     We use relative URLs so that static sites can be deployed to any location
79     without having to specify what the path component on the host will be
80     if the documentation is not hosted at the root path.
81     """
82 
83     def __init__(self):
84         self.base_path = '/'
85 
86     def set_current_url(self, current_url):
87         self.base_path = os.path.dirname(current_url)
88 
89     def make_relative(self, url):
90         """
91         Given a URL path return it as a relative URL,
92         given the context of the current page.
93         """
94         suffix = '/' if (url.endswith('/') and len(url) > 1) else ''
95         # Workaround for bug on `os.path.relpath()` in Python 2.6
96         if self.base_path == '/':
97             if url == '/':
98                 # Workaround for static assets
99                 return '.'
100             return url.lstrip('/')
101         # Under Python 2.6, relative_path adds an extra '/' at the end.
102         relative_path = os.path.relpath(url, start=self.base_path)
103         relative_path = relative_path.rstrip('/') + suffix
104 
105         return utils.path_to_url(relative_path)
106 
107 
108 class FileContext(object):
109     """
110     The FileContext is used to ensure that we can generate the appropriate
111     full path for other pages given their relative path from a particular page.
112 
113     This is used when we have relative hyperlinks in the documentation, so that
114     we can ensure that they point to markdown documents that actually exist
115     in the `pages` config.
116     """
117     def __init__(self):
118         self.current_file = None
119         self.base_path = ''
120 
121     def set_current_path(self, current_path):
122         self.current_file = current_path
123         self.base_path = os.path.dirname(current_path)
124 
125     def make_absolute(self, path):
126         """
127         Given a relative file path return it as a POSIX-style
128         absolute filepath, given the context of the current page.
129         """
130         return os.path.normpath(os.path.join(self.base_path, path))
131 
132 
133 class Page(object):
134     def __init__(self, title, url, path, url_context):
135 
136         self.title = title
137         self.abs_url = url
138         self.active = False
139         self.url_context = url_context
140         self.update_date = datetime.datetime.now().strftime("%Y-%m-%d")
141 
142         # Relative paths to the input markdown file and output html file.
143         self.input_path = path
144         self.output_path = utils.get_html_path(path)
145 
146         # Links to related pages
147         self.previous_page = None
148         self.next_page = None
149         self.ancestors = []
150 
151     @property
152     def url(self):
153         return self.url_context.make_relative(self.abs_url)
154 
155     @property
156     def is_homepage(self):
157         return utils.is_homepage(self.input_path)
158 
159     @property
160     def is_top_level(self):
161         return len(self.ancestors) == 0
162 
163     def __str__(self):
164         return self.indent_print()
165 
166     def indent_print(self, depth=0):
167         indent = '    ' * depth
168         active_marker = ' [*]' if self.active else ''
169         title = self.title if (self.title is not None) else '[blank]'
170         return '%s%s - %s%s\n' % (indent, title, self.abs_url, active_marker)
171 
172     def set_active(self, active=True):
173         self.active = active
174         for ancestor in self.ancestors:
175             ancestor.set_active(active)
176 
177 
178 class Header(object):
179     def __init__(self, title, children):
180         self.title, self.children = title, children
181         self.active = False
182         self.ancestors = []
183 
184     def __str__(self):
185         return self.indent_print()
186 
187     @property
188     def is_top_level(self):
189         return len(self.ancestors) == 0
190 
191     def indent_print(self, depth=0):
192         indent = '    ' * depth
193         active_marker = ' [*]' if self.active else ''
194         ret = '%s%s%s\n' % (indent, self.title, active_marker)
195         for item in self.children:
196             ret += item.indent_print(depth + 1)
197         return ret
198 
199     def set_active(self, active=True):
200         self.active = active
201         for ancestor in self.ancestors:
202             ancestor.set_active(active)
203 
204 
205 def _path_to_page(path, title, url_context, use_directory_urls):
206     if title is None:
207         title = filename_to_title(path.split(os.path.sep)[-1])
208     url = utils.get_url_path(path, use_directory_urls)
209     return Page(title=title, url=url, path=path,
210                 url_context=url_context)
211 
212 
213 def _follow(config_line, url_context, use_dir_urls, header=None, title=None):
214 
215     if isinstance(config_line, utils.string_types):
216         path = os.path.normpath(config_line)
217         page = _path_to_page(path, title, url_context, use_dir_urls)
218 
219         if header:
220             page.ancestors = [header]
221             header.children.append(page)
222 
223         yield page
224         raise StopIteration
225 
226     elif not isinstance(config_line, dict):
227         msg = ("Line in 'page' config is of type {0}, dict or string "
228                "expected. Config: {1}").format(type(config_line), config_line)
229         raise exceptions.ConfigurationError(msg)
230 
231     if len(config_line) > 1:
232         raise exceptions.ConfigurationError(
233             "Page configs should be in the format 'name: markdown.md'. The "
234             "config contains an invalid entry: {0}".format(config_line))
235     elif len(config_line) == 0:
236         log.warning("Ignoring empty line in the pages config.")
237         raise StopIteration
238 
239     next_cat_or_title, subpages_or_path = next(iter(config_line.items()))
240 
241     if isinstance(subpages_or_path, utils.string_types):
242         path = subpages_or_path
243         for sub in _follow(path, url_context, use_dir_urls, header=header, title=next_cat_or_title):
244             yield sub
245         raise StopIteration
246 
247     elif not isinstance(subpages_or_path, list):
248         msg = ("Line in 'page' config is of type {0}, list or string "
249                "expected for sub pages. Config: {1}"
250                ).format(type(config_line), config_line)
251         raise exceptions.ConfigurationError(msg)
252 
253     next_header = Header(title=next_cat_or_title, children=[])
254     if header:
255         next_header.ancestors = [header]
256         header.children.append(next_header)
257     yield next_header
258 
259     subpages = subpages_or_path
260 
261     for subpage in subpages:
262         for sub in _follow(subpage, url_context, use_dir_urls, next_header):
263             yield sub
264 
265 
266 def _generate_site_navigation(pages_config, url_context, use_dir_urls=True):
267     """
268     Returns a list of Page and Header instances that represent the
269     top level site navigation.
270     """
271     nav_items = []
272     pages = []
273 
274     previous = None
275 
276     for config_line in pages_config:
277 
278         for page_or_header in _follow(
279                 config_line, url_context, use_dir_urls):
280 
281             if isinstance(page_or_header, Header):
282 
283                 if page_or_header.is_top_level:
284                     nav_items.append(page_or_header)
285 
286             elif isinstance(page_or_header, Page):
287 
288                 if page_or_header.is_top_level:
289                     nav_items.append(page_or_header)
290 
291                 pages.append(page_or_header)
292 
293                 if previous:
294                     page_or_header.previous_page = previous
295                     previous.next_page = page_or_header
296                 previous = page_or_header
297 
298     if len(pages) == 0:
299         raise exceptions.ConfigurationError(
300             "No pages found in the pages config. "
301             "Remove it entirely to enable automatic page discovery.")
302 
303     return (nav_items, pages)
304
```
--- END FILES ---

Please first localize the bug based on the issue statement, and then generate a patch according to the `git diff` format fenced by three backticks.

Here is an example:

```diff
diff --git a/examples/server_async.py b/examples/server_async.py
--- a/examples/server_async.py
+++ b/examples/server_async.py
@@ -313,4 +313,4 @@
 
 
 if __name__ == "__main__":
-    asyncio.run(run_async_server("."), debug=True)
+    asyncio.run(run_async_server(), debug=True)
diff --git a/examples/server_sync.py b/examples/server_sync.py
--- a/examples/server_sync.py
+++ b/examples/server_sync.py
@@ -313,5 +313,5 @@
 
 
 if __name__ == "__main__":
-    server = run_sync_server(".")
+    server = run_sync_server()
     server.shutdown()

```

diff --git a/mkdocs/nav.py b/mkdocs/nav.py
--- a/mkdocs/nav.py
+++ b/mkdocs/nav.py
@@ -217,7 +217,7 @@
         page = _path_to_page(path, title, url_context, use_dir_urls)
 
         if header:
-            page.ancestors = [header]
+            page.ancestors = header.ancestors + [header, ]
             header.children.append(page)
 
         yield page

{"golden_diff": "diff --git a/mkdocs/nav.py b/mkdocs/nav.py\n--- a/mkdocs/nav.py\n+++ b/mkdocs/nav.py\n@@ -217,7 +217,7 @@\n         page = _path_to_page(path, title, url_context, use_dir_urls)\n \n         if header:\n-            page.ancestors = [header]\n+            page.ancestors = header.ancestors + [header, ]\n             header.children.append(page)\n \n         yield page\n", "issue": "current_page.ancestors only contains direct ancestor and not the full path of the page\nI'm using the mkdocs theme and tried to enhance it with a breadcrumb trail. The page navigation is created automatically by mkdocs (I don't use the pages confguration since I have almost 300 pages).\nI copied and adapted the `breadcrumbs.html` file from the readthedocs theme and integrated it in `content.html`:\n\n```\n<ol class=\"breadcrumb\">\n<li><a href=\"{{ homepage_url }}\">Docs</a></li>\n{% if current_page %}\n  {% for doc in current_page.ancestors %}\n    {% if doc.link %}\n      <li><a href=\"{{ doc.link|e }}\">{{ doc.title }}</a></li>\n    {% else %}\n      <li>{{ doc.title }}</li>\n    {% endif %}\n  {% endfor %}\n{% endif %}\n{% if current_page %}<li>{{ current_page.title }}</li>{% endif %}\n</ol>\n```\n\nMy file path (starting from the `docs_dir`)  is:\n`beheerteam/diensten/algemeen/ActiveDirectory.md`\nThe generated breadcrumb trail is:\n`Docs/algemeen/ActiveDirectory`\n\n`algemeen` is the only part that originates from the loop `for doc in current_page.ancestors`.\n\nMaybe this is a stupid question or it is just not possible, but I couldn't find i in the documentation and I'm just starting with mkdocs and couldn't understand the source on how this works.\n\n", "before_files": [{"content": "# coding: utf-8\n\n\"\"\"\nDeals with generating the site-wide navigation.\n\nThis consists of building a set of interlinked page and header objects.\n\"\"\"\n\nfrom __future__ import unicode_literals\nimport datetime\nimport logging\nimport os\n\nfrom mkdocs import utils, exceptions\n\nlog = logging.getLogger(__name__)\n\n\ndef filename_to_title(filename):\n    \"\"\"\n    Automatically generate a default title, given a filename.\n    \"\"\"\n    if utils.is_homepage(filename):\n        return 'Home'\n\n    return utils.filename_to_title(filename)\n\n\nclass SiteNavigation(object):\n    def __init__(self, pages_config, use_directory_urls=True):\n        self.url_context = URLContext()\n        self.file_context = FileContext()\n        self.nav_items, self.pages = _generate_site_navigation(\n            pages_config, self.url_context, use_directory_urls)\n        self.homepage = self.pages[0] if self.pages else None\n        self.use_directory_urls = use_directory_urls\n\n    def __str__(self):\n        return ''.join([str(item) for item in self])\n\n    def __iter__(self):\n        return iter(self.nav_items)\n\n    def walk_pages(self):\n        \"\"\"\n        Returns each page in the site in turn.\n\n        Additionally this sets the active status of the pages and headers,\n        in the site navigation, so that the rendered navbar can correctly\n        highlight the currently active page and/or header item.\n        \"\"\"\n        page = self.homepage\n        page.set_active()\n        self.url_context.set_current_url(page.abs_url)\n        self.file_context.set_current_path(page.input_path)\n        yield page\n        while page.next_page:\n            page.set_active(False)\n            page = page.next_page\n            page.set_active()\n            self.url_context.set_current_url(page.abs_url)\n            self.file_context.set_current_path(page.input_path)\n            yield page\n        page.set_active(False)\n\n    @property\n    def source_files(self):\n        if not hasattr(self, '_source_files'):\n            self._source_files = set([page.input_path for page in self.pages])\n        return self._source_files\n\n\nclass URLContext(object):\n    \"\"\"\n    The URLContext is used to ensure that we can generate the appropriate\n    relative URLs to other pages from any given page in the site.\n\n    We use relative URLs so that static sites can be deployed to any location\n    without having to specify what the path component on the host will be\n    if the documentation is not hosted at the root path.\n    \"\"\"\n\n    def __init__(self):\n        self.base_path = '/'\n\n    def set_current_url(self, current_url):\n        self.base_path = os.path.dirname(current_url)\n\n    def make_relative(self, url):\n        \"\"\"\n        Given a URL path return it as a relative URL,\n        given the context of the current page.\n        \"\"\"\n        suffix = '/' if (url.endswith('/') and len(url) > 1) else ''\n        # Workaround for bug on `os.path.relpath()` in Python 2.6\n        if self.base_path == '/':\n            if url == '/':\n                # Workaround for static assets\n                return '.'\n            return url.lstrip('/')\n        # Under Python 2.6, relative_path adds an extra '/' at the end.\n        relative_path = os.path.relpath(url, start=self.base_path)\n        relative_path = relative_path.rstrip('/') + suffix\n\n        return utils.path_to_url(relative_path)\n\n\nclass FileContext(object):\n    \"\"\"\n    The FileContext is used to ensure that we can generate the appropriate\n    full path for other pages given their relative path from a particular page.\n\n    This is used when we have relative hyperlinks in the documentation, so that\n    we can ensure that they point to markdown documents that actually exist\n    in the `pages` config.\n    \"\"\"\n    def __init__(self):\n        self.current_file = None\n        self.base_path = ''\n\n    def set_current_path(self, current_path):\n        self.current_file = current_path\n        self.base_path = os.path.dirname(current_path)\n\n    def make_absolute(self, path):\n        \"\"\"\n        Given a relative file path return it as a POSIX-style\n        absolute filepath, given the context of the current page.\n        \"\"\"\n        return os.path.normpath(os.path.join(self.base_path, path))\n\n\nclass Page(object):\n    def __init__(self, title, url, path, url_context):\n\n        self.title = title\n        self.abs_url = url\n        self.active = False\n        self.url_context = url_context\n        self.update_date = datetime.datetime.now().strftime(\"%Y-%m-%d\")\n\n        # Relative paths to the input markdown file and output html file.\n        self.input_path = path\n        self.output_path = utils.get_html_path(path)\n\n        # Links to related pages\n        self.previous_page = None\n        self.next_page = None\n        self.ancestors = []\n\n    @property\n    def url(self):\n        return self.url_context.make_relative(self.abs_url)\n\n    @property\n    def is_homepage(self):\n        return utils.is_homepage(self.input_path)\n\n    @property\n    def is_top_level(self):\n        return len(self.ancestors) == 0\n\n    def __str__(self):\n        return self.indent_print()\n\n    def indent_print(self, depth=0):\n        indent = '    ' * depth\n        active_marker = ' [*]' if self.active else ''\n        title = self.title if (self.title is not None) else '[blank]'\n        return '%s%s - %s%s\\n' % (indent, title, self.abs_url, active_marker)\n\n    def set_active(self, active=True):\n        self.active = active\n        for ancestor in self.ancestors:\n            ancestor.set_active(active)\n\n\nclass Header(object):\n    def __init__(self, title, children):\n        self.title, self.children = title, children\n        self.active = False\n        self.ancestors = []\n\n    def __str__(self):\n        return self.indent_print()\n\n    @property\n    def is_top_level(self):\n        return len(self.ancestors) == 0\n\n    def indent_print(self, depth=0):\n        indent = '    ' * depth\n        active_marker = ' [*]' if self.active else ''\n        ret = '%s%s%s\\n' % (indent, self.title, active_marker)\n        for item in self.children:\n            ret += item.indent_print(depth + 1)\n        return ret\n\n    def set_active(self, active=True):\n        self.active = active\n        for ancestor in self.ancestors:\n            ancestor.set_active(active)\n\n\ndef _path_to_page(path, title, url_context, use_directory_urls):\n    if title is None:\n        title = filename_to_title(path.split(os.path.sep)[-1])\n    url = utils.get_url_path(path, use_directory_urls)\n    return Page(title=title, url=url, path=path,\n                url_context=url_context)\n\n\ndef _follow(config_line, url_context, use_dir_urls, header=None, title=None):\n\n    if isinstance(config_line, utils.string_types):\n        path = os.path.normpath(config_line)\n        page = _path_to_page(path, title, url_context, use_dir_urls)\n\n        if header:\n            page.ancestors = [header]\n            header.children.append(page)\n\n        yield page\n        raise StopIteration\n\n    elif not isinstance(config_line, dict):\n        msg = (\"Line in 'page' config is of type {0}, dict or string \"\n               \"expected. Config: {1}\").format(type(config_line), config_line)\n        raise exceptions.ConfigurationError(msg)\n\n    if len(config_line) > 1:\n        raise exceptions.ConfigurationError(\n            \"Page configs should be in the format 'name: markdown.md'. The \"\n            \"config contains an invalid entry: {0}\".format(config_line))\n    elif len(config_line) == 0:\n        log.warning(\"Ignoring empty line in the pages config.\")\n        raise StopIteration\n\n    next_cat_or_title, subpages_or_path = next(iter(config_line.items()))\n\n    if isinstance(subpages_or_path, utils.string_types):\n        path = subpages_or_path\n        for sub in _follow(path, url_context, use_dir_urls, header=header, title=next_cat_or_title):\n            yield sub\n        raise StopIteration\n\n    elif not isinstance(subpages_or_path, list):\n        msg = (\"Line in 'page' config is of type {0}, list or string \"\n               \"expected for sub pages. Config: {1}\"\n               ).format(type(config_line), config_line)\n        raise exceptions.ConfigurationError(msg)\n\n    next_header = Header(title=next_cat_or_title, children=[])\n    if header:\n        next_header.ancestors = [header]\n        header.children.append(next_header)\n    yield next_header\n\n    subpages = subpages_or_path\n\n    for subpage in subpages:\n        for sub in _follow(subpage, url_context, use_dir_urls, next_header):\n            yield sub\n\n\ndef _generate_site_navigation(pages_config, url_context, use_dir_urls=True):\n    \"\"\"\n    Returns a list of Page and Header instances that represent the\n    top level site navigation.\n    \"\"\"\n    nav_items = []\n    pages = []\n\n    previous = None\n\n    for config_line in pages_config:\n\n        for page_or_header in _follow(\n                config_line, url_context, use_dir_urls):\n\n            if isinstance(page_or_header, Header):\n\n                if page_or_header.is_top_level:\n                    nav_items.append(page_or_header)\n\n            elif isinstance(page_or_header, Page):\n\n                if page_or_header.is_top_level:\n                    nav_items.append(page_or_header)\n\n                pages.append(page_or_header)\n\n                if previous:\n                    page_or_header.previous_page = previous\n                    previous.next_page = page_or_header\n                previous = page_or_header\n\n    if len(pages) == 0:\n        raise exceptions.ConfigurationError(\n            \"No pages found in the pages config. \"\n            \"Remove it entirely to enable automatic page discovery.\")\n\n    return (nav_items, pages)\n", "path": "mkdocs/nav.py"}], "after_files": [{"content": "# coding: utf-8\n\n\"\"\"\nDeals with generating the site-wide navigation.\n\nThis consists of building a set of interlinked page and header objects.\n\"\"\"\n\nfrom __future__ import unicode_literals\nimport datetime\nimport logging\nimport os\n\nfrom mkdocs import utils, exceptions\n\nlog = logging.getLogger(__name__)\n\n\ndef filename_to_title(filename):\n    \"\"\"\n    Automatically generate a default title, given a filename.\n    \"\"\"\n    if utils.is_homepage(filename):\n        return 'Home'\n\n    return utils.filename_to_title(filename)\n\n\nclass SiteNavigation(object):\n    def __init__(self, pages_config, use_directory_urls=True):\n        self.url_context = URLContext()\n        self.file_context = FileContext()\n        self.nav_items, self.pages = _generate_site_navigation(\n            pages_config, self.url_context, use_directory_urls)\n        self.homepage = self.pages[0] if self.pages else None\n        self.use_directory_urls = use_directory_urls\n\n    def __str__(self):\n        return ''.join([str(item) for item in self])\n\n    def __iter__(self):\n        return iter(self.nav_items)\n\n    def walk_pages(self):\n        \"\"\"\n        Returns each page in the site in turn.\n\n        Additionally this sets the active status of the pages and headers,\n        in the site navigation, so that the rendered navbar can correctly\n        highlight the currently active page and/or header item.\n        \"\"\"\n        page = self.homepage\n        page.set_active()\n        self.url_context.set_current_url(page.abs_url)\n        self.file_context.set_current_path(page.input_path)\n        yield page\n        while page.next_page:\n            page.set_active(False)\n            page = page.next_page\n            page.set_active()\n            self.url_context.set_current_url(page.abs_url)\n            self.file_context.set_current_path(page.input_path)\n            yield page\n        page.set_active(False)\n\n    @property\n    def source_files(self):\n        if not hasattr(self, '_source_files'):\n            self._source_files = set([page.input_path for page in self.pages])\n        return self._source_files\n\n\nclass URLContext(object):\n    \"\"\"\n    The URLContext is used to ensure that we can generate the appropriate\n    relative URLs to other pages from any given page in the site.\n\n    We use relative URLs so that static sites can be deployed to any location\n    without having to specify what the path component on the host will be\n    if the documentation is not hosted at the root path.\n    \"\"\"\n\n    def __init__(self):\n        self.base_path = '/'\n\n    def set_current_url(self, current_url):\n        self.base_path = os.path.dirname(current_url)\n\n    def make_relative(self, url):\n        \"\"\"\n        Given a URL path return it as a relative URL,\n        given the context of the current page.\n        \"\"\"\n        suffix = '/' if (url.endswith('/') and len(url) > 1) else ''\n        # Workaround for bug on `os.path.relpath()` in Python 2.6\n        if self.base_path == '/':\n            if url == '/':\n                # Workaround for static assets\n                return '.'\n            return url.lstrip('/')\n        # Under Python 2.6, relative_path adds an extra '/' at the end.\n        relative_path = os.path.relpath(url, start=self.base_path)\n        relative_path = relative_path.rstrip('/') + suffix\n\n        return utils.path_to_url(relative_path)\n\n\nclass FileContext(object):\n    \"\"\"\n    The FileContext is used to ensure that we can generate the appropriate\n    full path for other pages given their relative path from a particular page.\n\n    This is used when we have relative hyperlinks in the documentation, so that\n    we can ensure that they point to markdown documents that actually exist\n    in the `pages` config.\n    \"\"\"\n    def __init__(self):\n        self.current_file = None\n        self.base_path = ''\n\n    def set_current_path(self, current_path):\n        self.current_file = current_path\n        self.base_path = os.path.dirname(current_path)\n\n    def make_absolute(self, path):\n        \"\"\"\n        Given a relative file path return it as a POSIX-style\n        absolute filepath, given the context of the current page.\n        \"\"\"\n        return os.path.normpath(os.path.join(self.base_path, path))\n\n\nclass Page(object):\n    def __init__(self, title, url, path, url_context):\n\n        self.title = title\n        self.abs_url = url\n        self.active = False\n        self.url_context = url_context\n        self.update_date = datetime.datetime.now().strftime(\"%Y-%m-%d\")\n\n        # Relative paths to the input markdown file and output html file.\n        self.input_path = path\n        self.output_path = utils.get_html_path(path)\n\n        # Links to related pages\n        self.previous_page = None\n        self.next_page = None\n        self.ancestors = []\n\n    @property\n    def url(self):\n        return self.url_context.make_relative(self.abs_url)\n\n    @property\n    def is_homepage(self):\n        return utils.is_homepage(self.input_path)\n\n    @property\n    def is_top_level(self):\n        return len(self.ancestors) == 0\n\n    def __str__(self):\n        return self.indent_print()\n\n    def indent_print(self, depth=0):\n        indent = '    ' * depth\n        active_marker = ' [*]' if self.active else ''\n        title = self.title if (self.title is not None) else '[blank]'\n        return '%s%s - %s%s\\n' % (indent, title, self.abs_url, active_marker)\n\n    def set_active(self, active=True):\n        self.active = active\n        for ancestor in self.ancestors:\n            ancestor.set_active(active)\n\n\nclass Header(object):\n    def __init__(self, title, children):\n        self.title, self.children = title, children\n        self.active = False\n        self.ancestors = []\n\n    def __str__(self):\n        return self.indent_print()\n\n    @property\n    def is_top_level(self):\n        return len(self.ancestors) == 0\n\n    def indent_print(self, depth=0):\n        indent = '    ' * depth\n        active_marker = ' [*]' if self.active else ''\n        ret = '%s%s%s\\n' % (indent, self.title, active_marker)\n        for item in self.children:\n            ret += item.indent_print(depth + 1)\n        return ret\n\n    def set_active(self, active=True):\n        self.active = active\n        for ancestor in self.ancestors:\n            ancestor.set_active(active)\n\n\ndef _path_to_page(path, title, url_context, use_directory_urls):\n    if title is None:\n        title = filename_to_title(path.split(os.path.sep)[-1])\n    url = utils.get_url_path(path, use_directory_urls)\n    return Page(title=title, url=url, path=path,\n                url_context=url_context)\n\n\ndef _follow(config_line, url_context, use_dir_urls, header=None, title=None):\n\n    if isinstance(config_line, utils.string_types):\n        path = os.path.normpath(config_line)\n        page = _path_to_page(path, title, url_context, use_dir_urls)\n\n        if header:\n            page.ancestors = header.ancestors + [header, ]\n            header.children.append(page)\n\n        yield page\n        raise StopIteration\n\n    elif not isinstance(config_line, dict):\n        msg = (\"Line in 'page' config is of type {0}, dict or string \"\n               \"expected. Config: {1}\").format(type(config_line), config_line)\n        raise exceptions.ConfigurationError(msg)\n\n    if len(config_line) > 1:\n        raise exceptions.ConfigurationError(\n            \"Page configs should be in the format 'name: markdown.md'. The \"\n            \"config contains an invalid entry: {0}\".format(config_line))\n    elif len(config_line) == 0:\n        log.warning(\"Ignoring empty line in the pages config.\")\n        raise StopIteration\n\n    next_cat_or_title, subpages_or_path = next(iter(config_line.items()))\n\n    if isinstance(subpages_or_path, utils.string_types):\n        path = subpages_or_path\n        for sub in _follow(path, url_context, use_dir_urls, header=header, title=next_cat_or_title):\n            yield sub\n        raise StopIteration\n\n    elif not isinstance(subpages_or_path, list):\n        msg = (\"Line in 'page' config is of type {0}, list or string \"\n               \"expected for sub pages. Config: {1}\"\n               ).format(type(config_line), config_line)\n        raise exceptions.ConfigurationError(msg)\n\n    next_header = Header(title=next_cat_or_title, children=[])\n    if header:\n        next_header.ancestors = [header]\n        header.children.append(next_header)\n    yield next_header\n\n    subpages = subpages_or_path\n\n    for subpage in subpages:\n        for sub in _follow(subpage, url_context, use_dir_urls, next_header):\n            yield sub\n\n\ndef _generate_site_navigation(pages_config, url_context, use_dir_urls=True):\n    \"\"\"\n    Returns a list of Page and Header instances that represent the\n    top level site navigation.\n    \"\"\"\n    nav_items = []\n    pages = []\n\n    previous = None\n\n    for config_line in pages_config:\n\n        for page_or_header in _follow(\n                config_line, url_context, use_dir_urls):\n\n            if isinstance(page_or_header, Header):\n\n                if page_or_header.is_top_level:\n                    nav_items.append(page_or_header)\n\n            elif isinstance(page_or_header, Page):\n\n                if page_or_header.is_top_level:\n                    nav_items.append(page_or_header)\n\n                pages.append(page_or_header)\n\n                if previous:\n                    page_or_header.previous_page = previous\n                    previous.next_page = page_or_header\n                previous = page_or_header\n\n    if len(pages) == 0:\n        raise exceptions.ConfigurationError(\n            \"No pages found in the pages config. \"\n            \"Remove it entirely to enable automatic page discovery.\")\n\n    return (nav_items, pages)\n", "path": "mkdocs/nav.py"}]}