Skip to content

Commit

Permalink
Merge pull request #15 from kurtmckee/release-0.4.0
Browse files Browse the repository at this point in the history
Release 0.4.0
  • Loading branch information
kurtmckee authored Nov 30, 2023
2 parents 6f5a926 + 2d62935 commit e75836c
Show file tree
Hide file tree
Showing 11 changed files with 311 additions and 36 deletions.
20 changes: 20 additions & 0 deletions CHANGELOG.rst
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,26 @@ Please see the fragment files in the `changelog.d directory`_.

.. scriv-insert-here
.. _changelog-0.4.0:

0.4.0 - 2023-11-30
==================

Fixed
-----

* Fix file writing, which wasn't rendering newlines correctly.

Changed
-------

* Determine the configured file encoding only if the file has content.

Documentation
-------------

* Document how to create a new custom style.

.. _changelog-0.3.0:

0.3.0 - 2023-11-29
Expand Down
2 changes: 1 addition & 1 deletion README.rst
Original file line number Diff line number Diff line change
Expand Up @@ -56,6 +56,6 @@ Here's a sample configuration for ensuring your files have correct headers:
# .pre-commit-config.yaml
repos:
- repo: 'https://github.com/kurtmckee/chipshot'
rev: 'main'
rev: 'v0.4.0'
hooks:
- id: 'update-headers'
107 changes: 107 additions & 0 deletions docs/how-to/custom-headers.rst
Original file line number Diff line number Diff line change
@@ -0,0 +1,107 @@
How To Create Custom Headers
############################

Chipshot supports a wide variety of header styles
for many different programming and markup languages,
but you might want to create a new style.

This document walks you through the process of creating a new style.

* :ref:`variables`
* :ref:`create`

.. _variables:

The Four Control Variables
==========================

Chipshot anticipates that all header styles require
some kind of document-specific comment markup,
and that the comment markup will follow some of these rules:

* There may be some kind of markup that introduces the comment block.

Some languages support multiline comments
which are introduced with start and end markers.
For example, Typescript supports ``/*`` and ``*/`` markers.

Some languages only use a dedicated start marker.
For example, ReStructuredText uses ``..`` as its start marker
and relies on blank lines to signal the end of the comment.

Chipshot uses the terms ``block_prefix`` and ``block_suffix``
to refer to the start and end markers.

* Individual lines may need to prefixed with some kind of markup.

Some languages only support single-line comments.
For example, PostgreSQL comment lines must start with ``--``.

Chipshot support line prefixes and, if desired, line suffixes.
It uses the terms ``line_prefix`` and ``line_suffix``
to refer to the beginning-of-line and end-of-line markers.


.. _create:

Create a New Style
==================

All styles in Chipshot are created under the ``styles`` configuration key,
and then file extensions are configured to use the new style.
(It is not supported to define the style in the file extension configuration.)

Let's say you want to add support for PHP files.
First, define the style in ``.chipshot.toml``.
In the example below, the style name is defined as ``my-php-style``.
Escaped newlines must be embedded in the block prefix and suffix
to ensure that they render nicely.

.. code-block:: toml
[chipshot.styles.my-php-style]
block_prefix = "<?php\n/*\n"
line_prefix = " * "
block_suffix = "\n */\n?>"
Then, configure files with the "php" extension to use the new style.

.. code-block:: toml
[chipshot.extension.php]
style = "my-php-style"
Here's the complete ``.chipshot.toml`` file, including a template:

.. code-block:: toml
[chipshot]
template = """
Copyright 2022-{{ year }} Company Name
Licensed under the terms of the MIT License.
[chipshot.styles.my-php-style]
block_prefix = "<?php\n/*\n"
line_prefix = " * "
block_suffix = "\n */\n?>"
[chipshot.extension.php]
style = "my-php-style"
Run Chipshot with the ``--update`` flag and pass it a PHP file to update.
For example:

.. code-block:: console
$ chipshot --update example.php
The header will be added at the top of the file like this:

.. code-block:: php
<?php
/*
* Copyright 2022-2023 Company Name
* Licensed under the terms of the MIT License.
*/
?>
6 changes: 5 additions & 1 deletion docs/index.rst
Original file line number Diff line number Diff line change
Expand Up @@ -61,7 +61,11 @@ Getting Started
How-To Guides
=============

* How to customize comment styles
.. toctree::
:maxdepth: 1

how-to/custom-headers

* How to integrate Chipshot in your everyday development


Expand Down
2 changes: 1 addition & 1 deletion pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ build-backend = "poetry.core.masonry.api"

[tool.poetry]
name = "chipshot"
version = "0.3.0"
version = "0.4.0"
description = "Set up game-winning headers!"
readme = "README.rst"
authors = ["Kurt McKee <[email protected]>"]
Expand Down
16 changes: 2 additions & 14 deletions src/chipshot/reader/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,6 @@
import pathlib
import typing as t

from ..config import get_config_value
from ..shared import FileInfo
from . import encoding, header, newlines, prologue

Expand All @@ -19,26 +18,15 @@ def read(path: pathlib.Path, config: dict[str, t.Any]) -> FileInfo:
"""Read a file and return its contents and metadata."""

raw_contents = path.read_bytes()
info = FileInfo(
path=path,
raw_contents=raw_contents,
encoding=_determine_default_encoding(path, config),
)
info = FileInfo(path=path, raw_contents=raw_contents)

# If the file is empty, skip all other steps.
if not raw_contents:
return info

encoding.handle(info)
encoding.handle(info, config)
newlines.handle(info)
prologue.handle(info, config)
header.handle(info, config)

return info


def _determine_default_encoding(path: pathlib.Path, config: dict[str, t.Any]) -> str:
"""Determine the default encoding for the given path."""

(default_encoding,) = get_config_value(config, path, "encoding")
return str(default_encoding)
6 changes: 5 additions & 1 deletion src/chipshot/reader/encoding.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,14 +6,16 @@

import codecs
import logging
import typing

from .. import exceptions
from ..config import get_config_value
from ..shared import FileInfo

log = logging.getLogger(__name__)


def handle(info: FileInfo) -> None:
def handle(info: FileInfo, config: dict[str, typing.Any]) -> None:
"""Detect and handle the file encoding.
The encoding may be determined by a byte order mark at the beginning of the file.
Expand All @@ -36,6 +38,8 @@ def handle(info: FileInfo) -> None:
elif info.raw_contents.startswith(codecs.BOM_UTF8):
info.bom = codecs.BOM_UTF8
info.encoding = "utf-8"
else:
(info.encoding,) = get_config_value(config, info.path, "encoding")

if info.bom:
info.raw_contents = info.raw_contents[len(info.bom) :]
Expand Down
41 changes: 27 additions & 14 deletions src/chipshot/writer.py
Original file line number Diff line number Diff line change
Expand Up @@ -10,17 +10,30 @@


def write(file: FileInfo) -> None:
file.path.write_bytes(_render(file))


def _render(file: FileInfo) -> bytes:
text: str = ""
if file.prologue:
text = f"{file.prologue}{file.newlines * 2}"
if file.header:
text += f"{file.header}{file.newlines * 2}"
if file.original_header:
text += f"{file.original_header}{file.newlines * 2}"
text += file.contents

return file.bom + text.encode(file.encoding)
two_newlines = (file.newlines * 2).encode(file.encoding)
add_two_newlines = False

with file.path.open("wb") as f:
f.write(file.bom)

if file.prologue:
f.write(file.prologue.replace("\n", file.newlines).encode(file.encoding))
add_two_newlines = True

if file.header:
if add_two_newlines:
f.write(two_newlines)
f.write(file.header.replace("\n", file.newlines).encode(file.encoding))
add_two_newlines = True

if file.original_header:
if add_two_newlines:
f.write(two_newlines)
f.write(
file.original_header.replace("\n", file.newlines).encode(file.encoding)
)
add_two_newlines = True

if add_two_newlines:
f.write(two_newlines)
f.write(file.contents.replace("\n", file.newlines).encode(file.encoding))
2 changes: 1 addition & 1 deletion tests/test_reader.py
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ def test_empty_file(fs, default_config):
info = chipshot.reader.read(path, default_config)
assert info.raw_contents == b""
assert info.bom == b""
assert info.encoding == "utf-8"
assert info.encoding == ""
assert info.prologue == ""
assert info.original_header == ""
assert info.contents == ""
Expand Down
6 changes: 3 additions & 3 deletions tests/test_reader_encoding.py
Original file line number Diff line number Diff line change
Expand Up @@ -17,12 +17,12 @@
(codecs.BOM_UTF32_LE, "utf-32-le"),
),
)
def test_bom(bogus_file, bom, encoding):
def test_bom(bogus_file, default_config, bom, encoding):
raw_contents = bom + "success".encode(encoding)
bogus_file.raw_contents = raw_contents
bogus_file.encoding = "utf-8"

chipshot.reader.encoding.handle(bogus_file)
chipshot.reader.encoding.handle(bogus_file, default_config)

assert bogus_file.encoding == encoding
assert bogus_file.bom == bom
Expand All @@ -41,4 +41,4 @@ def test_decode_errors(bogus_file, default_config, bom, exception):
bogus_file.encoding = "utf-8"

with pytest.raises(exception):
chipshot.reader.encoding.handle(bogus_file)
chipshot.reader.encoding.handle(bogus_file, default_config)
Loading

0 comments on commit e75836c

Please sign in to comment.