*Contents:* **[Installation and usage](#installation-and-usage)** |
 **[The *Black* code style](#the-black-code-style)** |
+**[pyproject.toml](#pyproject.toml)** |
 **[Editor integration](#editor-integration)** |
 **[Version control integration](#version-control-integration)** |
 **[Ignoring unmodified files](#ignoring-unmodified-files)** |
                               that were not changed or were ignored due to
                               --exclude=.
   --version                   Show the version and exit.
+  --config PATH               Read configuration from PATH.
   --help                      Show this message and exit.
 ```
 
 * use `float` instead of `Union[int, float]`.
 
 
+## pyproject.toml
+
+*Black* is able to read project-specific default values for its
+command line options from a `pyproject.toml` file.  This is
+especially useful for specifying custom `--include` and `--exclude`
+patterns for your project.
+
+**Pro-tip**: If you're asking yourself "Do I need to configure anything?"
+the answer is "No".  *Black* is all about sensible defaults.
+
+
+### What on Earth is a `pyproject.toml` file?
+
+[PEP 518](https://www.python.org/dev/peps/pep-0518/) defines
+`pyproject.toml` as a configuration file to store build system
+requirements for Python projects.  With the help of tools
+like [Poetry](https://poetry.eustace.io/) or
+[Flit](https://flit.readthedocs.io/en/latest/) it can fully replace the
+need for `setup.py` and `setup.cfg` files.
+
+
+### Where *Black* looks for the file
+
+By default *Black* looks for `pyproject.toml` starting from the common
+base directory of all files and directories passed on the command line.
+If it's not there, it looks in parent directories.  It stops looking
+when it finds the file, or a `.git` directory, or a `.hg` directory,
+or the root of the file system, whichever comes first.
+
+If you're formatting standard input, *Black* will look for configuration
+starting from the current working directory.
+
+You can also explicitly specify the path to a particular file that you
+want with `--config`.  In this situation *Black* will not look for any
+other file.
+
+If you're running with `--verbose`, you will see a blue message if
+a file was found and used.
+
+
+### Configuration format
+
+As the file extension suggests, `pyproject.toml` is a [TOML](https://github.com/toml-lang/toml) file.  It contains separate
+sections for different tools.  *Black* is using the `[tool.black]`
+section.  The option keys are the same as long names of options on
+the command line.
+
+Note that you have to use single-quoted strings in TOML for regular
+expressions. It's the equivalent of r-strings in Python.  Multiline
+strings are treated as verbose regular expressions by Black.  Use `[ ]`
+to denote a significant space character.
+
+<details>
+<summary>Example `pyproject.toml`</summary>
+
+```toml
+[tool.black]
+line-length = 88
+py36 = true
+include = '\.pyi?$'
+exclude = '''
+/(
+    \.git
+  | \.hg
+  | \.mypy_cache
+  | \.tox
+  | \.venv
+  | _build
+  | buck-out
+  | build
+  | dist
+
+  # The following are specific to Black, you probably don't want those.
+  | blib2to3
+  | tests/data
+)/
+'''
+```
+
+</details>
+
+### Lookup hierarchy
+
+Command-line options have defaults that you can see in `--help`.
+A `pyproject.toml` can override those defaults.  Finally, options
+provided by the user on the command line override both.
+
+*Black* will only ever use one `pyproject.toml` file during an entire
+run. It doesn't look for multiple files, and doesn't compose
+configuration from different levels of the file hierarchy.
+
+
 ## Editor integration
 
 ### Emacs
     rev: stable
     hooks:
     - id: black
-      args: [--line-length=88, --safe]
       language_version: python3.6
 ```
 Then run `pre-commit install` and you're ready to go.
 
-`args` in the above config is optional but shows you how you can change
-the line length if you really need to.  If you're already using Python
-3.7, switch the `language_version` accordingly. Finally, `stable` is a tag
-that is pinned to the latest release on PyPI.  If you'd rather run on
-master, this is also an option.
+Avoid using `args` in the hook.  Instead, store necessary configuration
+in `pyproject.toml` so that editors and command-line usage of Black all
+behave consistently for your project.  See *Black*'s own `pyproject.toml`
+for an example.
+
+If you're already using Python 3.7, switch the `language_version`
+accordingly. Finally, `stable` is a tag that is pinned to the latest
+release on PyPI.  If you'd rather run on master, this is also an option.
 
 
 ## Ignoring unmodified files
 
 ### 18.6b2
 
+* added `--config` (#65)
+
 * fixed improper unmodified file caching when `-S` was used
 
 
 * [Stavros Korokithakis](mailto:hi@stavros.io)
 * [Sunil Kapil](mailto:snlkapil@gmail.com)
 * [Vishwas B Sharma](mailto:sharma.vishwas88@gmail.com)
-
----
-
-*Contents:*
-**[Installation and Usage](#installation-and-usage)** |
-**[The *Black* code style](#the-black-code-style)** |
-**[Editor integration](#editor-integration)** |
-**[Version control integration](#version-control-integration)** |
-**[Ignoring unmodified files](#ignoring-unmodified-files)** |
-**[Testimonials](#testimonials)** |
-**[Show your style](#show-your-style)** |
-**[License](#license)** |
-**[Contributing](#contributing-to-black)** |
-**[Change Log](#change-log)** |
-**[Authors](#authors)**
 
 from concurrent.futures import Executor, ProcessPoolExecutor
 from datetime import datetime
 from enum import Enum, Flag
-from functools import partial, wraps
+from functools import lru_cache, partial, wraps
 import io
 import keyword
 import logging
 from appdirs import user_cache_dir
 from attr import dataclass, Factory
 import click
+import toml
 
 # lib2to3 fork
 from blib2to3.pytree import Node, Leaf, type_repr
         return mode
 
 
+def read_pyproject_toml(
+    ctx: click.Context, param: click.Parameter, value: Union[str, int, bool, None]
+) -> Optional[str]:
+    """Inject Black configuration from "pyproject.toml" into defaults in `ctx`.
+
+    Returns the path to a successfully found and read configuration file, None
+    otherwise.
+    """
+    assert not isinstance(value, (int, bool)), "Invalid parameter type passed"
+    if not value:
+        root = find_project_root(ctx.params.get("src", ()))
+        path = root / "pyproject.toml"
+        if path.is_file():
+            value = str(path)
+        else:
+            return None
+
+    try:
+        pyproject_toml = toml.load(value)
+        config = pyproject_toml.get("tool", {}).get("black", {})
+    except (toml.TomlDecodeError, OSError) as e:
+        raise click.BadOptionUsage(f"Error reading configuration file: {e}", ctx)
+
+    if not config:
+        return None
+
+    if ctx.default_map is None:
+        ctx.default_map = {}
+    ctx.default_map.update(  # type: ignore  # bad types in .pyi
+        {k.replace("--", "").replace("-", "_"): v for k, v in config.items()}
+    )
+    return value
+
+
 @click.command()
 @click.option(
     "-l",
     type=click.Path(
         exists=True, file_okay=True, dir_okay=True, readable=True, allow_dash=True
     ),
+    is_eager=True,
+)
+@click.option(
+    "--config",
+    type=click.Path(
+        exists=False, file_okay=True, dir_okay=False, readable=True, allow_dash=False
+    ),
+    is_eager=True,
+    callback=read_pyproject_toml,
+    help="Read configuration from PATH.",
 )
 @click.pass_context
 def main(
     verbose: bool,
     include: str,
     exclude: str,
-    src: List[str],
+    src: Tuple[str],
+    config: Optional[str],
 ) -> None:
     """The uncompromising code formatter."""
     write_back = WriteBack.from_configuration(check=check, diff=diff)
     mode = FileMode.from_configuration(
         py36=py36, pyi=pyi, skip_string_normalization=skip_string_normalization
     )
-    report = Report(check=check, quiet=quiet, verbose=verbose)
-    sources: Set[Path] = set()
+    if config and verbose:
+        out(f"Using configuration from {config}.", bold=False, fg="blue")
     try:
-        include_regex = re.compile(include)
+        include_regex = re_compile_maybe_verbose(include)
     except re.error:
         err(f"Invalid regular expression for include given: {include!r}")
         ctx.exit(2)
     try:
-        exclude_regex = re.compile(exclude)
+        exclude_regex = re_compile_maybe_verbose(exclude)
     except re.error:
         err(f"Invalid regular expression for exclude given: {exclude!r}")
         ctx.exit(2)
+    report = Report(check=check, quiet=quiet, verbose=verbose)
     root = find_project_root(src)
+    sources: Set[Path] = set()
     for s in src:
         p = Path(s)
         if p.is_dir():
         if verbose or not quiet:
             out("No paths given. Nothing to do 😴")
         ctx.exit(0)
-        return
 
-    elif len(sources) == 1:
+    if len(sources) == 1:
         reformat_one(
             src=sources.pop(),
             line_length=line_length,
             normalized_path += "/"
         exclude_match = exclude.search(normalized_path)
         if exclude_match and exclude_match.group(0):
-            report.path_ignored(child, f"matches --exclude={exclude.pattern}")
+            report.path_ignored(child, f"matches the --exclude regular expression")
             continue
 
         if child.is_dir():
                 yield child
 
 
-def find_project_root(srcs: List[str]) -> Path:
+@lru_cache()
+def find_project_root(srcs: Iterable[str]) -> Path:
     """Return a directory containing .git, .hg, or pyproject.toml.
 
     That directory can be one of the directories passed in `srcs` or their
     return regex.sub(replacement, regex.sub(replacement, original))
 
 
+def re_compile_maybe_verbose(regex: str) -> Pattern[str]:
+    """Compile a regular expression string in `regex`.
+
+    If it contains newlines, use verbose mode.
+    """
+    if "\n" in regex:
+        regex = "(?x)" + regex
+    return re.compile(regex)
+
+
 def enumerate_reversed(sequence: Sequence[T]) -> Iterator[Tuple[Index, T]]:
     """Like `reversed(enumerate(sequence))` if that were possible."""
     index = len(sequence) - 1
 
 
    installation_and_usage
    the_black_code_style
+   pyproject_toml
    editor_integration
    version_control_integration
    ignoring_unmodified_files
    contributing
+   show_your_style
    change_log
    reference/reference_summary
    authors
 
--- /dev/null
+_build/generated/pyproject_toml.md
\ No newline at end of file
 
--- /dev/null
+# Example configuration for Black.
+
+# NOTE: you have to use single-quoted strings in TOML for regular expressions.
+# It's the equivalent of r-strings in Python.  Multiline strings are treated as
+# verbose regular expressions by Black.  Use [ ] to denote a significant space
+# character.
+
+[tool.black]
+line-length = 88
+py36 = true
+include = '\.pyi?$'
+exclude = '''
+/(
+    \.git
+  | \.hg
+  | \.mypy_cache
+  | \.tox
+  | \.venv
+  | _build
+  | buck-out
+  | build
+  | dist
+
+  # The following are specific to Black, you probably don't want those.
+  | blib2to3
+  | tests/data
+)/
+'''
 
--- /dev/null
+# Empty pyproject.toml to use with some tests that depend on Python 3.6 autodiscovery
+# and so on.
 
         )
         source, _ = read_data("expression.py")
         expected, _ = read_data("expression.diff")
+        config = THIS_DIR / "data" / "empty_pyproject.toml"
         stderrbuf = BytesIO()
-        result = BlackRunner(stderrbuf).invoke(
-            black.main, ["-", "--fast", f"--line-length={ll}", "--diff"], input=source
-        )
+        args = ["-", "--fast", f"--line-length={ll}", "--diff", f"--config={config}"]
+        result = BlackRunner(stderrbuf).invoke(black.main, args, input=source)
         self.assertEqual(result.exit_code, 0)
         actual = diff_header.sub("[Deterministic header]", result.output)
         actual = actual.rstrip() + "\n"  # the diff output has a trailing space