We are currently solving the following issue within our repository. Here is the issue text:
--- BEGIN ISSUE ---
What is the expected behavior of `readme.md`?
According to the docs, [Writing-your-docs:Index-pages](https://www.mkdocs.org/user-guide/writing-your-docs/#index-pages), `README.md` will render into `index.html`. There is no mention of `readme.md`. The implementation reflects this behavior: https://github.com/mkdocs/mkdocs/blob/dc35569ade5e101c8c48b78bbed8708dd0b1b49a/mkdocs/structure/files.py#L151

But the check in [get_files()](https://github.com/mkdocs/mkdocs/blob/dc35569ade5e101c8c48b78bbed8708dd0b1b49a/mkdocs/structure/files.py#L225) ignores casing:
https://github.com/mkdocs/mkdocs/blob/dc35569ade5e101c8c48b78bbed8708dd0b1b49a/mkdocs/structure/files.py#L246-L247

I believe it should be:
```python
if filename == 'README.md' and 'index.md' in filenames:
    log.warning(f"Both index.md and README.md found. Skipping README.md from {source_dir}")
```

--- 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/structure/files.py`
Content:
```
1 import fnmatch
2 import os
3 import logging
4 from urllib.parse import quote as urlquote
5 
6 from mkdocs import utils
7 
8 
9 log = logging.getLogger(__name__)
10 
11 
12 class Files:
13     """ A collection of File objects. """
14     def __init__(self, files):
15         self._files = files
16 
17     def __iter__(self):
18         return iter(self._files)
19 
20     def __len__(self):
21         return len(self._files)
22 
23     def __contains__(self, path):
24         return path in self.src_paths
25 
26     @property
27     def src_paths(self):
28         return {file.src_path: file for file in self._files}
29 
30     def get_file_from_path(self, path):
31         """ Return a File instance with File.src_path equal to path. """
32         return self.src_paths.get(os.path.normpath(path))
33 
34     def append(self, file):
35         """ Append file to Files collection. """
36         self._files.append(file)
37 
38     def remove(self, file):
39         """ Remove file from Files collection. """
40         self._files.remove(file)
41 
42     def copy_static_files(self, dirty=False):
43         """ Copy static files from source to destination. """
44         for file in self:
45             if not file.is_documentation_page():
46                 file.copy_file(dirty)
47 
48     def documentation_pages(self):
49         """ Return iterable of all Markdown page file objects. """
50         return [file for file in self if file.is_documentation_page()]
51 
52     def static_pages(self):
53         """ Return iterable of all static page file objects. """
54         return [file for file in self if file.is_static_page()]
55 
56     def media_files(self):
57         """ Return iterable of all file objects which are not documentation or static pages. """
58         return [file for file in self if file.is_media_file()]
59 
60     def javascript_files(self):
61         """ Return iterable of all javascript file objects. """
62         return [file for file in self if file.is_javascript()]
63 
64     def css_files(self):
65         """ Return iterable of all CSS file objects. """
66         return [file for file in self if file.is_css()]
67 
68     def add_files_from_theme(self, env, config):
69         """ Retrieve static files from Jinja environment and add to collection. """
70         def filter(name):
71             # '.*' filters dot files/dirs at root level whereas '*/.*' filters nested levels
72             patterns = ['.*', '*/.*', '*.py', '*.pyc', '*.html', '*readme*', 'mkdocs_theme.yml']
73             # Exclude translation files
74             patterns.append("locales/*")
75             patterns.extend(f'*{x}' for x in utils.markdown_extensions)
76             patterns.extend(config['theme'].static_templates)
77             for pattern in patterns:
78                 if fnmatch.fnmatch(name.lower(), pattern):
79                     return False
80             return True
81         for path in env.list_templates(filter_func=filter):
82             # Theme files do not override docs_dir files
83             path = os.path.normpath(path)
84             if path not in self:
85                 for dir in config['theme'].dirs:
86                     # Find the first theme dir which contains path
87                     if os.path.isfile(os.path.join(dir, path)):
88                         self.append(File(path, dir, config['site_dir'], config['use_directory_urls']))
89                         break
90 
91 
92 class File:
93     """
94     A MkDocs File object.
95 
96     Points to the source and destination locations of a file.
97 
98     The `path` argument must be a path that exists relative to `src_dir`.
99 
100     The `src_dir` and `dest_dir` must be absolute paths on the local file system.
101 
102     The `use_directory_urls` argument controls how destination paths are generated. If `False`, a Markdown file is
103     mapped to an HTML file of the same name (the file extension is changed to `.html`). If True, a Markdown file is
104     mapped to an HTML index file (`index.html`) nested in a directory using the "name" of the file in `path`. The
105     `use_directory_urls` argument has no effect on non-Markdown files.
106 
107     File objects have the following properties, which are Unicode strings:
108 
109     File.src_path
110         The pure path of the source file relative to the source directory.
111 
112     File.abs_src_path
113         The absolute concrete path of the source file.
114 
115     File.dest_path
116         The pure path of the destination file relative to the destination directory.
117 
118     File.abs_dest_path
119         The absolute concrete path of the destination file.
120 
121     File.url
122         The url of the destination file relative to the destination directory as a string.
123     """
124     def __init__(self, path, src_dir, dest_dir, use_directory_urls):
125         self.page = None
126         self.src_path = os.path.normpath(path)
127         self.abs_src_path = os.path.normpath(os.path.join(src_dir, self.src_path))
128         self.name = self._get_stem()
129         self.dest_path = self._get_dest_path(use_directory_urls)
130         self.abs_dest_path = os.path.normpath(os.path.join(dest_dir, self.dest_path))
131         self.url = self._get_url(use_directory_urls)
132 
133     def __eq__(self, other):
134         return (
135             isinstance(other, self.__class__) and
136             self.src_path == other.src_path and
137             self.abs_src_path == other.abs_src_path and
138             self.url == other.url
139         )
140 
141     def __repr__(self):
142         return (
143             f"File(src_path='{self.src_path}', dest_path='{self.dest_path}',"
144             f" name='{self.name}', url='{self.url}')"
145         )
146 
147     def _get_stem(self):
148         """ Return the name of the file without it's extension. """
149         filename = os.path.basename(self.src_path)
150         stem, ext = os.path.splitext(filename)
151         return 'index' if stem in ('index', 'README') else stem
152 
153     def _get_dest_path(self, use_directory_urls):
154         """ Return destination path based on source path. """
155         if self.is_documentation_page():
156             parent, filename = os.path.split(self.src_path)
157             if not use_directory_urls or self.name == 'index':
158                 # index.md or README.md => index.html
159                 # foo.md => foo.html
160                 return os.path.join(parent, self.name + '.html')
161             else:
162                 # foo.md => foo/index.html
163                 return os.path.join(parent, self.name, 'index.html')
164         return self.src_path
165 
166     def _get_url(self, use_directory_urls):
167         """ Return url based in destination path. """
168         url = self.dest_path.replace(os.path.sep, '/')
169         dirname, filename = os.path.split(url)
170         if use_directory_urls and filename == 'index.html':
171             if dirname == '':
172                 url = '.'
173             else:
174                 url = dirname + '/'
175         return urlquote(url)
176 
177     def url_relative_to(self, other):
178         """ Return url for file relative to other file. """
179         return utils.get_relative_url(self.url, other.url if isinstance(other, File) else other)
180 
181     def copy_file(self, dirty=False):
182         """ Copy source file to destination, ensuring parent directories exist. """
183         if dirty and not self.is_modified():
184             log.debug(f"Skip copying unmodified file: '{self.src_path}'")
185         else:
186             log.debug(f"Copying media file: '{self.src_path}'")
187             utils.copy_file(self.abs_src_path, self.abs_dest_path)
188 
189     def is_modified(self):
190         if os.path.isfile(self.abs_dest_path):
191             return os.path.getmtime(self.abs_dest_path) < os.path.getmtime(self.abs_src_path)
192         return True
193 
194     def is_documentation_page(self):
195         """ Return True if file is a Markdown page. """
196         return os.path.splitext(self.src_path)[1] in utils.markdown_extensions
197 
198     def is_static_page(self):
199         """ Return True if file is a static page (html, xml, json). """
200         return os.path.splitext(self.src_path)[1] in (
201             '.html',
202             '.htm',
203             '.xml',
204             '.json',
205         )
206 
207     def is_media_file(self):
208         """ Return True if file is not a documentation or static page. """
209         return not (self.is_documentation_page() or self.is_static_page())
210 
211     def is_javascript(self):
212         """ Return True if file is a JavaScript file. """
213         return os.path.splitext(self.src_path)[1] in (
214             '.js',
215             '.javascript',
216         )
217 
218     def is_css(self):
219         """ Return True if file is a CSS file. """
220         return os.path.splitext(self.src_path)[1] in (
221             '.css',
222         )
223 
224 
225 def get_files(config):
226     """ Walk the `docs_dir` and return a Files collection. """
227     files = []
228     exclude = ['.*', '/templates']
229 
230     for source_dir, dirnames, filenames in os.walk(config['docs_dir'], followlinks=True):
231         relative_dir = os.path.relpath(source_dir, config['docs_dir'])
232 
233         for dirname in list(dirnames):
234             path = os.path.normpath(os.path.join(relative_dir, dirname))
235             # Skip any excluded directories
236             if _filter_paths(basename=dirname, path=path, is_dir=True, exclude=exclude):
237                 dirnames.remove(dirname)
238         dirnames.sort()
239 
240         for filename in _sort_files(filenames):
241             path = os.path.normpath(os.path.join(relative_dir, filename))
242             # Skip any excluded files
243             if _filter_paths(basename=filename, path=path, is_dir=False, exclude=exclude):
244                 continue
245             # Skip README.md if an index file also exists in dir
246             if filename.lower() == 'readme.md' and 'index.md' in filenames:
247                 log.warning(f"Both index.md and readme.md found. Skipping readme.md from {source_dir}")
248                 continue
249             files.append(File(path, config['docs_dir'], config['site_dir'], config['use_directory_urls']))
250 
251     return Files(files)
252 
253 
254 def _sort_files(filenames):
255     """ Always sort `index` or `README` as first filename in list. """
256 
257     def key(f):
258         if os.path.splitext(f)[0] in ['index', 'README']:
259             return (0,)
260         return (1, f)
261 
262     return sorted(filenames, key=key)
263 
264 
265 def _filter_paths(basename, path, is_dir, exclude):
266     """ .gitignore style file filtering. """
267     for item in exclude:
268         # Items ending in '/' apply only to directories.
269         if item.endswith('/') and not is_dir:
270             continue
271         # Items starting with '/' apply to the whole path.
272         # In any other cases just the basename is used.
273         match = path if item.startswith('/') else basename
274         if fnmatch.fnmatch(match, item.strip('/')):
275             return True
276     return False
277
```
--- 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/structure/files.py b/mkdocs/structure/files.py
--- a/mkdocs/structure/files.py
+++ b/mkdocs/structure/files.py
@@ -243,8 +243,8 @@
             if _filter_paths(basename=filename, path=path, is_dir=False, exclude=exclude):
                 continue
             # Skip README.md if an index file also exists in dir
-            if filename.lower() == 'readme.md' and 'index.md' in filenames:
-                log.warning(f"Both index.md and readme.md found. Skipping readme.md from {source_dir}")
+            if filename == 'README.md' and 'index.md' in filenames:
+                log.warning(f"Both index.md and README.md found. Skipping README.md from {source_dir}")
                 continue
             files.append(File(path, config['docs_dir'], config['site_dir'], config['use_directory_urls']))

{"golden_diff": "diff --git a/mkdocs/structure/files.py b/mkdocs/structure/files.py\n--- a/mkdocs/structure/files.py\n+++ b/mkdocs/structure/files.py\n@@ -243,8 +243,8 @@\n             if _filter_paths(basename=filename, path=path, is_dir=False, exclude=exclude):\n                 continue\n             # Skip README.md if an index file also exists in dir\n-            if filename.lower() == 'readme.md' and 'index.md' in filenames:\n-                log.warning(f\"Both index.md and readme.md found. Skipping readme.md from {source_dir}\")\n+            if filename == 'README.md' and 'index.md' in filenames:\n+                log.warning(f\"Both index.md and README.md found. Skipping README.md from {source_dir}\")\n                 continue\n             files.append(File(path, config['docs_dir'], config['site_dir'], config['use_directory_urls']))\n", "issue": "What is the expected behavior of `readme.md`?\nAccording to the docs, [Writing-your-docs:Index-pages](https://www.mkdocs.org/user-guide/writing-your-docs/#index-pages), `README.md` will render into `index.html`. There is no mention of `readme.md`. The implementation reflects this behavior: https://github.com/mkdocs/mkdocs/blob/dc35569ade5e101c8c48b78bbed8708dd0b1b49a/mkdocs/structure/files.py#L151\r\n\r\nBut the check in [get_files()](https://github.com/mkdocs/mkdocs/blob/dc35569ade5e101c8c48b78bbed8708dd0b1b49a/mkdocs/structure/files.py#L225) ignores casing:\r\nhttps://github.com/mkdocs/mkdocs/blob/dc35569ade5e101c8c48b78bbed8708dd0b1b49a/mkdocs/structure/files.py#L246-L247\r\n\r\nI believe it should be:\r\n```python\r\nif filename == 'README.md' and 'index.md' in filenames:\r\n    log.warning(f\"Both index.md and README.md found. Skipping README.md from {source_dir}\")\r\n```\n", "before_files": [{"content": "import fnmatch\nimport os\nimport logging\nfrom urllib.parse import quote as urlquote\n\nfrom mkdocs import utils\n\n\nlog = logging.getLogger(__name__)\n\n\nclass Files:\n    \"\"\" A collection of File objects. \"\"\"\n    def __init__(self, files):\n        self._files = files\n\n    def __iter__(self):\n        return iter(self._files)\n\n    def __len__(self):\n        return len(self._files)\n\n    def __contains__(self, path):\n        return path in self.src_paths\n\n    @property\n    def src_paths(self):\n        return {file.src_path: file for file in self._files}\n\n    def get_file_from_path(self, path):\n        \"\"\" Return a File instance with File.src_path equal to path. \"\"\"\n        return self.src_paths.get(os.path.normpath(path))\n\n    def append(self, file):\n        \"\"\" Append file to Files collection. \"\"\"\n        self._files.append(file)\n\n    def remove(self, file):\n        \"\"\" Remove file from Files collection. \"\"\"\n        self._files.remove(file)\n\n    def copy_static_files(self, dirty=False):\n        \"\"\" Copy static files from source to destination. \"\"\"\n        for file in self:\n            if not file.is_documentation_page():\n                file.copy_file(dirty)\n\n    def documentation_pages(self):\n        \"\"\" Return iterable of all Markdown page file objects. \"\"\"\n        return [file for file in self if file.is_documentation_page()]\n\n    def static_pages(self):\n        \"\"\" Return iterable of all static page file objects. \"\"\"\n        return [file for file in self if file.is_static_page()]\n\n    def media_files(self):\n        \"\"\" Return iterable of all file objects which are not documentation or static pages. \"\"\"\n        return [file for file in self if file.is_media_file()]\n\n    def javascript_files(self):\n        \"\"\" Return iterable of all javascript file objects. \"\"\"\n        return [file for file in self if file.is_javascript()]\n\n    def css_files(self):\n        \"\"\" Return iterable of all CSS file objects. \"\"\"\n        return [file for file in self if file.is_css()]\n\n    def add_files_from_theme(self, env, config):\n        \"\"\" Retrieve static files from Jinja environment and add to collection. \"\"\"\n        def filter(name):\n            # '.*' filters dot files/dirs at root level whereas '*/.*' filters nested levels\n            patterns = ['.*', '*/.*', '*.py', '*.pyc', '*.html', '*readme*', 'mkdocs_theme.yml']\n            # Exclude translation files\n            patterns.append(\"locales/*\")\n            patterns.extend(f'*{x}' for x in utils.markdown_extensions)\n            patterns.extend(config['theme'].static_templates)\n            for pattern in patterns:\n                if fnmatch.fnmatch(name.lower(), pattern):\n                    return False\n            return True\n        for path in env.list_templates(filter_func=filter):\n            # Theme files do not override docs_dir files\n            path = os.path.normpath(path)\n            if path not in self:\n                for dir in config['theme'].dirs:\n                    # Find the first theme dir which contains path\n                    if os.path.isfile(os.path.join(dir, path)):\n                        self.append(File(path, dir, config['site_dir'], config['use_directory_urls']))\n                        break\n\n\nclass File:\n    \"\"\"\n    A MkDocs File object.\n\n    Points to the source and destination locations of a file.\n\n    The `path` argument must be a path that exists relative to `src_dir`.\n\n    The `src_dir` and `dest_dir` must be absolute paths on the local file system.\n\n    The `use_directory_urls` argument controls how destination paths are generated. If `False`, a Markdown file is\n    mapped to an HTML file of the same name (the file extension is changed to `.html`). If True, a Markdown file is\n    mapped to an HTML index file (`index.html`) nested in a directory using the \"name\" of the file in `path`. The\n    `use_directory_urls` argument has no effect on non-Markdown files.\n\n    File objects have the following properties, which are Unicode strings:\n\n    File.src_path\n        The pure path of the source file relative to the source directory.\n\n    File.abs_src_path\n        The absolute concrete path of the source file.\n\n    File.dest_path\n        The pure path of the destination file relative to the destination directory.\n\n    File.abs_dest_path\n        The absolute concrete path of the destination file.\n\n    File.url\n        The url of the destination file relative to the destination directory as a string.\n    \"\"\"\n    def __init__(self, path, src_dir, dest_dir, use_directory_urls):\n        self.page = None\n        self.src_path = os.path.normpath(path)\n        self.abs_src_path = os.path.normpath(os.path.join(src_dir, self.src_path))\n        self.name = self._get_stem()\n        self.dest_path = self._get_dest_path(use_directory_urls)\n        self.abs_dest_path = os.path.normpath(os.path.join(dest_dir, self.dest_path))\n        self.url = self._get_url(use_directory_urls)\n\n    def __eq__(self, other):\n        return (\n            isinstance(other, self.__class__) and\n            self.src_path == other.src_path and\n            self.abs_src_path == other.abs_src_path and\n            self.url == other.url\n        )\n\n    def __repr__(self):\n        return (\n            f\"File(src_path='{self.src_path}', dest_path='{self.dest_path}',\"\n            f\" name='{self.name}', url='{self.url}')\"\n        )\n\n    def _get_stem(self):\n        \"\"\" Return the name of the file without it's extension. \"\"\"\n        filename = os.path.basename(self.src_path)\n        stem, ext = os.path.splitext(filename)\n        return 'index' if stem in ('index', 'README') else stem\n\n    def _get_dest_path(self, use_directory_urls):\n        \"\"\" Return destination path based on source path. \"\"\"\n        if self.is_documentation_page():\n            parent, filename = os.path.split(self.src_path)\n            if not use_directory_urls or self.name == 'index':\n                # index.md or README.md => index.html\n                # foo.md => foo.html\n                return os.path.join(parent, self.name + '.html')\n            else:\n                # foo.md => foo/index.html\n                return os.path.join(parent, self.name, 'index.html')\n        return self.src_path\n\n    def _get_url(self, use_directory_urls):\n        \"\"\" Return url based in destination path. \"\"\"\n        url = self.dest_path.replace(os.path.sep, '/')\n        dirname, filename = os.path.split(url)\n        if use_directory_urls and filename == 'index.html':\n            if dirname == '':\n                url = '.'\n            else:\n                url = dirname + '/'\n        return urlquote(url)\n\n    def url_relative_to(self, other):\n        \"\"\" Return url for file relative to other file. \"\"\"\n        return utils.get_relative_url(self.url, other.url if isinstance(other, File) else other)\n\n    def copy_file(self, dirty=False):\n        \"\"\" Copy source file to destination, ensuring parent directories exist. \"\"\"\n        if dirty and not self.is_modified():\n            log.debug(f\"Skip copying unmodified file: '{self.src_path}'\")\n        else:\n            log.debug(f\"Copying media file: '{self.src_path}'\")\n            utils.copy_file(self.abs_src_path, self.abs_dest_path)\n\n    def is_modified(self):\n        if os.path.isfile(self.abs_dest_path):\n            return os.path.getmtime(self.abs_dest_path) < os.path.getmtime(self.abs_src_path)\n        return True\n\n    def is_documentation_page(self):\n        \"\"\" Return True if file is a Markdown page. \"\"\"\n        return os.path.splitext(self.src_path)[1] in utils.markdown_extensions\n\n    def is_static_page(self):\n        \"\"\" Return True if file is a static page (html, xml, json). \"\"\"\n        return os.path.splitext(self.src_path)[1] in (\n            '.html',\n            '.htm',\n            '.xml',\n            '.json',\n        )\n\n    def is_media_file(self):\n        \"\"\" Return True if file is not a documentation or static page. \"\"\"\n        return not (self.is_documentation_page() or self.is_static_page())\n\n    def is_javascript(self):\n        \"\"\" Return True if file is a JavaScript file. \"\"\"\n        return os.path.splitext(self.src_path)[1] in (\n            '.js',\n            '.javascript',\n        )\n\n    def is_css(self):\n        \"\"\" Return True if file is a CSS file. \"\"\"\n        return os.path.splitext(self.src_path)[1] in (\n            '.css',\n        )\n\n\ndef get_files(config):\n    \"\"\" Walk the `docs_dir` and return a Files collection. \"\"\"\n    files = []\n    exclude = ['.*', '/templates']\n\n    for source_dir, dirnames, filenames in os.walk(config['docs_dir'], followlinks=True):\n        relative_dir = os.path.relpath(source_dir, config['docs_dir'])\n\n        for dirname in list(dirnames):\n            path = os.path.normpath(os.path.join(relative_dir, dirname))\n            # Skip any excluded directories\n            if _filter_paths(basename=dirname, path=path, is_dir=True, exclude=exclude):\n                dirnames.remove(dirname)\n        dirnames.sort()\n\n        for filename in _sort_files(filenames):\n            path = os.path.normpath(os.path.join(relative_dir, filename))\n            # Skip any excluded files\n            if _filter_paths(basename=filename, path=path, is_dir=False, exclude=exclude):\n                continue\n            # Skip README.md if an index file also exists in dir\n            if filename.lower() == 'readme.md' and 'index.md' in filenames:\n                log.warning(f\"Both index.md and readme.md found. Skipping readme.md from {source_dir}\")\n                continue\n            files.append(File(path, config['docs_dir'], config['site_dir'], config['use_directory_urls']))\n\n    return Files(files)\n\n\ndef _sort_files(filenames):\n    \"\"\" Always sort `index` or `README` as first filename in list. \"\"\"\n\n    def key(f):\n        if os.path.splitext(f)[0] in ['index', 'README']:\n            return (0,)\n        return (1, f)\n\n    return sorted(filenames, key=key)\n\n\ndef _filter_paths(basename, path, is_dir, exclude):\n    \"\"\" .gitignore style file filtering. \"\"\"\n    for item in exclude:\n        # Items ending in '/' apply only to directories.\n        if item.endswith('/') and not is_dir:\n            continue\n        # Items starting with '/' apply to the whole path.\n        # In any other cases just the basename is used.\n        match = path if item.startswith('/') else basename\n        if fnmatch.fnmatch(match, item.strip('/')):\n            return True\n    return False\n", "path": "mkdocs/structure/files.py"}], "after_files": [{"content": "import fnmatch\nimport os\nimport logging\nfrom urllib.parse import quote as urlquote\n\nfrom mkdocs import utils\n\n\nlog = logging.getLogger(__name__)\n\n\nclass Files:\n    \"\"\" A collection of File objects. \"\"\"\n    def __init__(self, files):\n        self._files = files\n\n    def __iter__(self):\n        return iter(self._files)\n\n    def __len__(self):\n        return len(self._files)\n\n    def __contains__(self, path):\n        return path in self.src_paths\n\n    @property\n    def src_paths(self):\n        return {file.src_path: file for file in self._files}\n\n    def get_file_from_path(self, path):\n        \"\"\" Return a File instance with File.src_path equal to path. \"\"\"\n        return self.src_paths.get(os.path.normpath(path))\n\n    def append(self, file):\n        \"\"\" Append file to Files collection. \"\"\"\n        self._files.append(file)\n\n    def remove(self, file):\n        \"\"\" Remove file from Files collection. \"\"\"\n        self._files.remove(file)\n\n    def copy_static_files(self, dirty=False):\n        \"\"\" Copy static files from source to destination. \"\"\"\n        for file in self:\n            if not file.is_documentation_page():\n                file.copy_file(dirty)\n\n    def documentation_pages(self):\n        \"\"\" Return iterable of all Markdown page file objects. \"\"\"\n        return [file for file in self if file.is_documentation_page()]\n\n    def static_pages(self):\n        \"\"\" Return iterable of all static page file objects. \"\"\"\n        return [file for file in self if file.is_static_page()]\n\n    def media_files(self):\n        \"\"\" Return iterable of all file objects which are not documentation or static pages. \"\"\"\n        return [file for file in self if file.is_media_file()]\n\n    def javascript_files(self):\n        \"\"\" Return iterable of all javascript file objects. \"\"\"\n        return [file for file in self if file.is_javascript()]\n\n    def css_files(self):\n        \"\"\" Return iterable of all CSS file objects. \"\"\"\n        return [file for file in self if file.is_css()]\n\n    def add_files_from_theme(self, env, config):\n        \"\"\" Retrieve static files from Jinja environment and add to collection. \"\"\"\n        def filter(name):\n            # '.*' filters dot files/dirs at root level whereas '*/.*' filters nested levels\n            patterns = ['.*', '*/.*', '*.py', '*.pyc', '*.html', '*readme*', 'mkdocs_theme.yml']\n            # Exclude translation files\n            patterns.append(\"locales/*\")\n            patterns.extend(f'*{x}' for x in utils.markdown_extensions)\n            patterns.extend(config['theme'].static_templates)\n            for pattern in patterns:\n                if fnmatch.fnmatch(name.lower(), pattern):\n                    return False\n            return True\n        for path in env.list_templates(filter_func=filter):\n            # Theme files do not override docs_dir files\n            path = os.path.normpath(path)\n            if path not in self:\n                for dir in config['theme'].dirs:\n                    # Find the first theme dir which contains path\n                    if os.path.isfile(os.path.join(dir, path)):\n                        self.append(File(path, dir, config['site_dir'], config['use_directory_urls']))\n                        break\n\n\nclass File:\n    \"\"\"\n    A MkDocs File object.\n\n    Points to the source and destination locations of a file.\n\n    The `path` argument must be a path that exists relative to `src_dir`.\n\n    The `src_dir` and `dest_dir` must be absolute paths on the local file system.\n\n    The `use_directory_urls` argument controls how destination paths are generated. If `False`, a Markdown file is\n    mapped to an HTML file of the same name (the file extension is changed to `.html`). If True, a Markdown file is\n    mapped to an HTML index file (`index.html`) nested in a directory using the \"name\" of the file in `path`. The\n    `use_directory_urls` argument has no effect on non-Markdown files.\n\n    File objects have the following properties, which are Unicode strings:\n\n    File.src_path\n        The pure path of the source file relative to the source directory.\n\n    File.abs_src_path\n        The absolute concrete path of the source file.\n\n    File.dest_path\n        The pure path of the destination file relative to the destination directory.\n\n    File.abs_dest_path\n        The absolute concrete path of the destination file.\n\n    File.url\n        The url of the destination file relative to the destination directory as a string.\n    \"\"\"\n    def __init__(self, path, src_dir, dest_dir, use_directory_urls):\n        self.page = None\n        self.src_path = os.path.normpath(path)\n        self.abs_src_path = os.path.normpath(os.path.join(src_dir, self.src_path))\n        self.name = self._get_stem()\n        self.dest_path = self._get_dest_path(use_directory_urls)\n        self.abs_dest_path = os.path.normpath(os.path.join(dest_dir, self.dest_path))\n        self.url = self._get_url(use_directory_urls)\n\n    def __eq__(self, other):\n        return (\n            isinstance(other, self.__class__) and\n            self.src_path == other.src_path and\n            self.abs_src_path == other.abs_src_path and\n            self.url == other.url\n        )\n\n    def __repr__(self):\n        return (\n            f\"File(src_path='{self.src_path}', dest_path='{self.dest_path}',\"\n            f\" name='{self.name}', url='{self.url}')\"\n        )\n\n    def _get_stem(self):\n        \"\"\" Return the name of the file without it's extension. \"\"\"\n        filename = os.path.basename(self.src_path)\n        stem, ext = os.path.splitext(filename)\n        return 'index' if stem in ('index', 'README') else stem\n\n    def _get_dest_path(self, use_directory_urls):\n        \"\"\" Return destination path based on source path. \"\"\"\n        if self.is_documentation_page():\n            parent, filename = os.path.split(self.src_path)\n            if not use_directory_urls or self.name == 'index':\n                # index.md or README.md => index.html\n                # foo.md => foo.html\n                return os.path.join(parent, self.name + '.html')\n            else:\n                # foo.md => foo/index.html\n                return os.path.join(parent, self.name, 'index.html')\n        return self.src_path\n\n    def _get_url(self, use_directory_urls):\n        \"\"\" Return url based in destination path. \"\"\"\n        url = self.dest_path.replace(os.path.sep, '/')\n        dirname, filename = os.path.split(url)\n        if use_directory_urls and filename == 'index.html':\n            if dirname == '':\n                url = '.'\n            else:\n                url = dirname + '/'\n        return urlquote(url)\n\n    def url_relative_to(self, other):\n        \"\"\" Return url for file relative to other file. \"\"\"\n        return utils.get_relative_url(self.url, other.url if isinstance(other, File) else other)\n\n    def copy_file(self, dirty=False):\n        \"\"\" Copy source file to destination, ensuring parent directories exist. \"\"\"\n        if dirty and not self.is_modified():\n            log.debug(f\"Skip copying unmodified file: '{self.src_path}'\")\n        else:\n            log.debug(f\"Copying media file: '{self.src_path}'\")\n            utils.copy_file(self.abs_src_path, self.abs_dest_path)\n\n    def is_modified(self):\n        if os.path.isfile(self.abs_dest_path):\n            return os.path.getmtime(self.abs_dest_path) < os.path.getmtime(self.abs_src_path)\n        return True\n\n    def is_documentation_page(self):\n        \"\"\" Return True if file is a Markdown page. \"\"\"\n        return os.path.splitext(self.src_path)[1] in utils.markdown_extensions\n\n    def is_static_page(self):\n        \"\"\" Return True if file is a static page (html, xml, json). \"\"\"\n        return os.path.splitext(self.src_path)[1] in (\n            '.html',\n            '.htm',\n            '.xml',\n            '.json',\n        )\n\n    def is_media_file(self):\n        \"\"\" Return True if file is not a documentation or static page. \"\"\"\n        return not (self.is_documentation_page() or self.is_static_page())\n\n    def is_javascript(self):\n        \"\"\" Return True if file is a JavaScript file. \"\"\"\n        return os.path.splitext(self.src_path)[1] in (\n            '.js',\n            '.javascript',\n        )\n\n    def is_css(self):\n        \"\"\" Return True if file is a CSS file. \"\"\"\n        return os.path.splitext(self.src_path)[1] in (\n            '.css',\n        )\n\n\ndef get_files(config):\n    \"\"\" Walk the `docs_dir` and return a Files collection. \"\"\"\n    files = []\n    exclude = ['.*', '/templates']\n\n    for source_dir, dirnames, filenames in os.walk(config['docs_dir'], followlinks=True):\n        relative_dir = os.path.relpath(source_dir, config['docs_dir'])\n\n        for dirname in list(dirnames):\n            path = os.path.normpath(os.path.join(relative_dir, dirname))\n            # Skip any excluded directories\n            if _filter_paths(basename=dirname, path=path, is_dir=True, exclude=exclude):\n                dirnames.remove(dirname)\n        dirnames.sort()\n\n        for filename in _sort_files(filenames):\n            path = os.path.normpath(os.path.join(relative_dir, filename))\n            # Skip any excluded files\n            if _filter_paths(basename=filename, path=path, is_dir=False, exclude=exclude):\n                continue\n            # Skip README.md if an index file also exists in dir\n            if filename == 'README.md' and 'index.md' in filenames:\n                log.warning(f\"Both index.md and README.md found. Skipping README.md from {source_dir}\")\n                continue\n            files.append(File(path, config['docs_dir'], config['site_dir'], config['use_directory_urls']))\n\n    return Files(files)\n\n\ndef _sort_files(filenames):\n    \"\"\" Always sort `index` or `README` as first filename in list. \"\"\"\n\n    def key(f):\n        if os.path.splitext(f)[0] in ['index', 'README']:\n            return (0,)\n        return (1, f)\n\n    return sorted(filenames, key=key)\n\n\ndef _filter_paths(basename, path, is_dir, exclude):\n    \"\"\" .gitignore style file filtering. \"\"\"\n    for item in exclude:\n        # Items ending in '/' apply only to directories.\n        if item.endswith('/') and not is_dir:\n            continue\n        # Items starting with '/' apply to the whole path.\n        # In any other cases just the basename is used.\n        match = path if item.startswith('/') else basename\n        if fnmatch.fnmatch(match, item.strip('/')):\n            return True\n    return False\n", "path": "mkdocs/structure/files.py"}]}