Compare commits

...

18 Commits

Author SHA1 Message Date
Nick Groenen
6f0401b576
Upgrade pulldown-cmark from 0.11.2 to 0.12 2024-08-25 13:36:21 +02:00
Nick Groenen
e173c32171
Remove custom changelog template in favor of title_format
With https://github.com/twisted/towncrier/issues/648 now fixed, we can
just rely on a correctly-set title_format again for properly formatted
markdown changelogs.
2024-08-25 13:26:44 +02:00
renovate[bot]
9c24f0f6a5
Update dependency towncrier to v24.8.0 (#286)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2024-08-25 13:12:01 +02:00
renovate[bot]
499ea51411 Lock file maintenance 2024-08-24 15:38:56 +00:00
Nick Groenen
aabb6c6132
Disable semantic commits in renovate
The default heuristics were picking up semantic commits even though we
don't use those on this repository. Explicitly disable them to prevent
that.
2024-08-24 14:49:34 +02:00
Nick Groenen
2bee08477d
Disable separateMajorMinor for pulldown-cmark dependency group
Finally figured it out, this was the issue that was preventing renovate
from grouping pulldown-cmark and pulldown-cmark-to-cmark together in a
single PR.

See code comment for a more detailed description of why this is needed.
2024-08-24 14:48:25 +02:00
Nick Groenen
5bede0a357
renovate: matchPackagePatterns -> matchPackageNames
See https://docs.renovatebot.com/release-notes-for-major-versions/#changes-to-package-matching

This might also explain the issues getting grouping to work for
pulldown-cmark.
2024-08-24 13:21:35 +02:00
Nick Groenen
768ebb3ecd
Update groupName for pulldown-cmark in renovate config
66108b5128 didn't actually result in a
grouped update for pulldown-cmark and pulldown-cmark-to-cmark, both of
which have available updates and both of which must be upgraded at the
same time in this instance.

Reading the renovate documentation, it is unclear why this configuration
didn't work, but my best guess is that there's some unexpected behavior
when groupName == packageName.
2024-08-24 13:12:42 +02:00
Nick Groenen
66108b5128
Group updates for pulldown-cmark and pulldown-cmark-to-cmark 2024-08-24 12:52:56 +02:00
renovate[bot]
9044fc83a8 Lock file maintenance 2024-08-19 03:58:34 +00:00
renovate[bot]
76c61c6a07 Lock file maintenance 2024-08-12 03:52:19 +00:00
renovate[bot]
7b1794eb71
Lock file maintenance (#277)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2024-08-10 11:14:47 +02:00
Nick Groenen
7916cf47e3
Merge pull request #276 from zoni/pulldown-cmark-0.11
Upgrade to pulldown-cmark 0.11
2024-08-10 10:54:58 +02:00
Nick Groenen
3afab84d69
Get frontmatter directly from pulldown-cmark
pulldown-cmark 0.10.0 introduced metadata blocks in
pulldown-cmark/pulldown-cmark#641. Using this we can drop the dependency
on matter and grab the frontmatter as we're consuming markdown events
from the note being parsed, instead of having to extract the frontmatter
separately.
2024-08-09 20:01:39 +02:00
Nick Groenen
e2ef435f04
Enable math extension
This ensures LaTeX/MathJax blocks don't get mangled as seen in
https://github.com/zoni/obsidian-export/issues/14.
2024-08-04 21:11:06 +02:00
Nick Groenen
6d0e693ed7
Upgrade to pulldown-cmark 0.11 2024-08-04 21:11:06 +02:00
Nick Groenen
72735cfff8
Change release process to use towncrier and delay regenerating README
1. Use [towncrier][1] to generate release notes/CHANGELOG instead of
   auto-generating it from git commit messages.
2. Move release scripts into a Justfile.
3. Only regenerate the top-level README file when making a release,
   don't force it to be up to date in between releases.

Point 3 helps prevent documentation for unreleased changes from being
shown to users on GitHub, which may otherwise be confusing.

[1]: https://towncrier.readthedocs.io/en/stable/index.html#
2024-08-04 14:19:50 +02:00
Davis Davalos-DeLosh
564bee1d92 Optionally preserve modified time of exported files
Add a new argument --preserve-mtime to keep the original modified time
attribute of notes being exported, instead of setting them to the
current time.
2024-08-04 14:00:49 +02:00
27 changed files with 486 additions and 174 deletions

View File

@ -4,6 +4,7 @@
"schedule:monthly",
// Group updates to linters together in one PR
"group:linters",
":semanticCommitsDisabled"
],
dependencyDashboard: true,
"labels": ["dependencies"],
@ -22,6 +23,19 @@
"packageNameTemplate": "rust-lang/rust",
"datasourceTemplate": "github-releases"
},
{
"customType": "regex",
"fileMatch": ["^Justfile$"],
"matchStrings": [
// https://regex101.com/r/vOmY6R/1
// Matching on word boundaries (\b) around start and end ensures we
// match variations like "towncrier==1.2.3" as well as "pipx run
// towncrier==1.2.3", adding some future proofness.
"\".*\\btowncrier==(?<currentValue>.+?)\\b\""
],
"depNameTemplate": "towncrier",
"datasourceTemplate": "pypi"
},
],
packageRules: [
{
@ -29,6 +43,21 @@
minimumReleaseAge: "3 days",
automerge: true
},
{
// pulldown-cmark and pulldown-cmark-to-mark must be updated in
// lockstep in case of breaking changes, so it's best to group them
// together.
"matchPackageNames": ["pulldown-cmark*"],
// pulldown-cmark is currently on a 0.x release series while
// pulldown-cmark-to-cmark tends to bump the major version
// component whenever pulldown-cmark creates a new release (which
// increments the minor component). Renovate by default creates
// separate PRs for major and minor bumps which fails to match up
// with the grouping. Disabling this behavior ensures that both
// packages are always updated together in a single PR.
"separateMajorMinor": false,
"groupName": "pulldown-cmark (group)"
},
],
ignorePaths: [
// Auto-generated by cargo-dist (https://opensource.axo.dev/cargo-dist/)

View File

@ -15,5 +15,5 @@ jobs:
CARGO_REGISTRY_TOKEN: ${{ secrets.CARGO_REGISTRY_TOKEN }}
steps:
- uses: actions/checkout@692973e3d937129bcbf40652eb9f2f61becf3332 # v4
- uses: dtolnay/rust-toolchain@stable
- uses: ./.github/actions/setup-ci
- run: cargo publish

View File

@ -30,8 +30,3 @@ repos:
language: system
files: \.rs$
pass_filenames: false
- id: README
name: Render README.md
entry: docs/generate.sh
language: script
files: ^(README\.md)|(docs/.*)

View File

@ -1,5 +1,7 @@
# Changelog
<!-- towncrier release notes start -->
## v23.12.0 (2023-12-03)
### New

View File

@ -74,4 +74,9 @@ If you don't feel comfortable writing user documentation, I will be happy to gui
> **⚠ Warning**
>
> If you update the README file, take note that you must edit the fragments in the [docs](docs/) directory as opposed to the README in the root of the repository, which is auto-generated.
> If you update the README file, take note that you must edit the fragments in the [docs](docs/) directory as opposed to the README in the root of the repository, which is auto-generated with every release.
## Release notes
[Towncrier](https://towncrier.readthedocs.io/en/stable/index.html) is used to generate release notes.
If you add a changelog fragment to the `changelog.d` directory with `just add-changelog` (requires [just](https://github.com/casey/just#installation)) it will automatically be picked up when a new release is made.

157
Cargo.lock generated
View File

@ -101,7 +101,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "534c5cf6194dfab3db3242765c03bbe257cf92f22b38f6bc0c58d59108a820ba"
dependencies = [
"libc",
"windows-sys",
"windows-sys 0.52.0",
]
[[package]]
@ -116,9 +116,21 @@ dependencies = [
[[package]]
name = "fastrand"
version = "2.1.0"
version = "2.1.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9fc0510504f03c51ada170672ac806f1f105a88aa97a5281117e1ddc3368e51a"
checksum = "e8c02a5121d4ea3eb16a80748c74f5549a5665e4c21333c6098f283870fbdea6"
[[package]]
name = "filetime"
version = "0.2.24"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "bf401df4a4e3872c4fe8151134cf483738e74b67fc934d6532c882b3d24a4550"
dependencies = [
"cfg-if",
"libc",
"libredox",
"windows-sys 0.59.0",
]
[[package]]
name = "futures"
@ -176,7 +188,7 @@ checksum = "87750cf4b7a4c0625b1529e4c543c2182106e4dedc60a2a6455e00d212c489ac"
dependencies = [
"proc-macro2",
"quote",
"syn 2.0.72",
"syn 2.0.76",
]
[[package]]
@ -299,9 +311,9 @@ checksum = "ce23b50ad8242c51a442f3ff322d56b02f08852c77e4c0b4d3fd684abc89c683"
[[package]]
name = "indexmap"
version = "2.2.6"
version = "2.4.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "168fb715dda47215e360912c096649d23d58bf392ac62f73919e831745e40f26"
checksum = "93ead53efc7ea8ed3cfb0c79fc8023fbb782a5432b52830b6518941cebe6505c"
dependencies = [
"equivalent",
"hashbrown",
@ -314,16 +326,21 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "49f1f14873335454500d59611f1cf4a4b0f786f9ac11f4312a78e4cf2566695b"
[[package]]
name = "lazy_static"
version = "1.5.0"
name = "libc"
version = "0.2.158"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "bbd2bcb4c963f2ddae06a2efc7e9f3591312473c50c6685e1f298068316e66fe"
checksum = "d8adc4bb1803a324070e64a98ae98f38934d91957a99cfb3a43dcbc01bc56439"
[[package]]
name = "libc"
version = "0.2.155"
name = "libredox"
version = "0.1.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "97b3888a4aecf77e811145cadf6eef5901f4782c53886191b2f693f24761847c"
checksum = "c0ff37bd590ca25063e35af745c343cb7a0271906fb7b37e4813e8f79f00268d"
dependencies = [
"bitflags",
"libc",
"redox_syscall",
]
[[package]]
name = "linux-raw-sys"
@ -337,16 +354,6 @@ version = "0.4.22"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a7a70ba024b9dc04c27ea2f0c0548feb474ec5c54bba33a7f72f873a39d07b24"
[[package]]
name = "matter"
version = "0.1.0-alpha4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "fc16e839c57e0ad77957c42d39baab3692a1c6fa47692066470cddc24a5b0cd0"
dependencies = [
"lazy_static",
"regex",
]
[[package]]
name = "memchr"
version = "2.7.4"
@ -358,9 +365,9 @@ name = "obsidian-export"
version = "23.12.0"
dependencies = [
"eyre",
"filetime",
"gumdrop",
"ignore",
"matter",
"pathdiff",
"percent-encoding",
"pretty_assertions",
@ -437,30 +444,37 @@ dependencies = [
[[package]]
name = "pulldown-cmark"
version = "0.9.6"
version = "0.12.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "57206b407293d2bcd3af849ce869d52068623f19e1b5ff8e8778e3309439682b"
checksum = "4d31cbfcd94884c3a67ec210c83efb06cb43674043458b0ad59f6947f8462c23"
dependencies = [
"bitflags",
"getopts",
"memchr",
"pulldown-cmark-escape",
"unicase",
]
[[package]]
name = "pulldown-cmark-to-cmark"
version = "11.2.0"
name = "pulldown-cmark-escape"
version = "0.11.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "6dd464f32d7631035e849fcd969a603e9bb17ceaebe8467352a7728147f34e42"
checksum = "007d8adb5ddab6f8e3f491ac63566a7d5002cc7ed73901f72057943fa71ae1ae"
[[package]]
name = "pulldown-cmark-to-cmark"
version = "16.0.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "171b9687038fb417937aee4ce1d0a269897091f05be16660a1e45777533c54b9"
dependencies = [
"pulldown-cmark",
]
[[package]]
name = "quote"
version = "1.0.36"
version = "1.0.37"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0fa76aaf39101c457836aec0ce2316dbdc3ab723cdda1c6bd4e6ad4208acaca7"
checksum = "b5b9d34b8991d19d98081b46eacdd8eb58c6f2b201139f7c5f643cc155a633af"
dependencies = [
"proc-macro2",
]
@ -485,6 +499,15 @@ dependencies = [
"crossbeam-utils",
]
[[package]]
name = "redox_syscall"
version = "0.5.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "2a908a6e00f1fdd0dfd9c0eb08ce85126f6d8bbda50017e74bc4a4b7d4a926a4"
dependencies = [
"bitflags",
]
[[package]]
name = "regex"
version = "1.10.6"
@ -546,7 +569,7 @@ dependencies = [
"regex",
"relative-path",
"rustc_version",
"syn 2.0.72",
"syn 2.0.76",
"unicode-ident",
]
@ -569,7 +592,7 @@ dependencies = [
"errno",
"libc",
"linux-raw-sys",
"windows-sys",
"windows-sys 0.52.0",
]
[[package]]
@ -595,22 +618,22 @@ checksum = "61697e0a1c7e512e84a621326239844a24d8207b4669b41bc18b32ea5cbf988b"
[[package]]
name = "serde"
version = "1.0.204"
version = "1.0.209"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "bc76f558e0cbb2a839d37354c575f1dc3fdc6546b5be373ba43d95f231bf7c12"
checksum = "99fce0ffe7310761ca6bf9faf5115afbc19688edd00171d81b1bb1b116c63e09"
dependencies = [
"serde_derive",
]
[[package]]
name = "serde_derive"
version = "1.0.204"
version = "1.0.209"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e0cd7e117be63d3c3678776753929474f3b04a43a080c744d6b0ae2a8c28e222"
checksum = "a5831b979fd7b5439637af1752d535ff49f4860c0f341d1baeb6faf0f4242170"
dependencies = [
"proc-macro2",
"quote",
"syn 2.0.72",
"syn 2.0.76",
]
[[package]]
@ -637,9 +660,9 @@ dependencies = [
[[package]]
name = "slug"
version = "0.1.5"
version = "0.1.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "3bd94acec9c8da640005f8e135a39fc0372e74535e6b368b7a04b875f784c8c4"
checksum = "882a80f72ee45de3cc9a5afeb2da0331d58df69e4e7d8eeb5d3c7784ae67e724"
dependencies = [
"deunicode",
"wasm-bindgen",
@ -663,7 +686,7 @@ dependencies = [
"heck",
"proc-macro2",
"quote",
"syn 2.0.72",
"syn 2.0.76",
]
[[package]]
@ -679,9 +702,9 @@ dependencies = [
[[package]]
name = "syn"
version = "2.0.72"
version = "2.0.76"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "dc4b9b9bf2add8093d3f2c0204471e951b2285580335de42f9d2534f3ae7a8af"
checksum = "578e081a14e0cefc3279b0472138c513f37b41a08d5a3cca9b6e4e8ceb6cd525"
dependencies = [
"proc-macro2",
"quote",
@ -690,15 +713,15 @@ dependencies = [
[[package]]
name = "tempfile"
version = "3.11.0"
version = "3.12.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b8fcd239983515c23a32fb82099f97d0b11b8c72f654ed659363a95c3dad7a53"
checksum = "04cbcdd0c794ebb0d4cf35e88edd2f7d2c4c3e9a5a6dab322839b321c6a87a64"
dependencies = [
"cfg-if",
"fastrand",
"once_cell",
"rustix",
"windows-sys",
"windows-sys 0.59.0",
]
[[package]]
@ -718,9 +741,9 @@ checksum = "1f3ccbac311fea05f86f61904b462b55fb3df8837a366dfc601a0161d0532f20"
[[package]]
name = "toml_datetime"
version = "0.6.7"
version = "0.6.8"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f8fb9f64314842840f1d940ac544da178732128f1c78c21772e876579e0da1db"
checksum = "0dd7358ecb8fc2f8d014bf86f6f638ce72ba252a2c3a2572f2a795f1d23efb41"
[[package]]
name = "toml_edit"
@ -787,34 +810,35 @@ dependencies = [
[[package]]
name = "wasm-bindgen"
version = "0.2.92"
version = "0.2.93"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "4be2531df63900aeb2bca0daaaddec08491ee64ceecbee5076636a3b026795a8"
checksum = "a82edfc16a6c469f5f44dc7b571814045d60404b55a0ee849f9bcfa2e63dd9b5"
dependencies = [
"cfg-if",
"once_cell",
"wasm-bindgen-macro",
]
[[package]]
name = "wasm-bindgen-backend"
version = "0.2.92"
version = "0.2.93"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "614d787b966d3989fa7bb98a654e369c762374fd3213d212cfc0251257e747da"
checksum = "9de396da306523044d3302746f1208fa71d7532227f15e347e2d93e4145dd77b"
dependencies = [
"bumpalo",
"log",
"once_cell",
"proc-macro2",
"quote",
"syn 2.0.72",
"syn 2.0.76",
"wasm-bindgen-shared",
]
[[package]]
name = "wasm-bindgen-macro"
version = "0.2.92"
version = "0.2.93"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a1f8823de937b71b9460c0c34e25f3da88250760bec0ebac694b49997550d726"
checksum = "585c4c91a46b072c92e908d99cb1dcdf95c5218eeb6f3bf1efa991ee7a68cccf"
dependencies = [
"quote",
"wasm-bindgen-macro-support",
@ -822,30 +846,30 @@ dependencies = [
[[package]]
name = "wasm-bindgen-macro-support"
version = "0.2.92"
version = "0.2.93"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e94f17b526d0a461a191c78ea52bbce64071ed5c04c9ffe424dcb38f74171bb7"
checksum = "afc340c74d9005395cf9dd098506f7f44e38f2b4a21c6aaacf9a105ea5e1e836"
dependencies = [
"proc-macro2",
"quote",
"syn 2.0.72",
"syn 2.0.76",
"wasm-bindgen-backend",
"wasm-bindgen-shared",
]
[[package]]
name = "wasm-bindgen-shared"
version = "0.2.92"
version = "0.2.93"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "af190c94f2773fdb3729c55b007a722abb5384da03bc0986df4c289bf5567e96"
checksum = "c62a0a307cb4a311d3a07867860911ca130c3494e8c2719593806c08bc5d0484"
[[package]]
name = "winapi-util"
version = "0.1.8"
version = "0.1.9"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "4d4cc384e1e73b93bafa6fb4f1df8c41695c8a91cf9c4c64358067d15a7b6c6b"
checksum = "cf221c93e13a30d793f7645a0e7762c55d169dbb0a49671918a2319d289b10bb"
dependencies = [
"windows-sys",
"windows-sys 0.59.0",
]
[[package]]
@ -857,6 +881,15 @@ dependencies = [
"windows-targets",
]
[[package]]
name = "windows-sys"
version = "0.59.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1e38bc4d79ed67fd075bcc251a1c39b32a1776bbe92e5bef1f0bf1f8c531853b"
dependencies = [
"windows-targets",
]
[[package]]
name = "windows-targets"
version = "0.52.6"

View File

@ -28,17 +28,17 @@ doc = false
eyre = "0.6.12"
gumdrop = "0.8.1"
ignore = "0.4.22"
matter = "0.1.0-alpha4"
pathdiff = "0.2.1"
percent-encoding = "2.3.1"
pulldown-cmark = "0.9.3"
pulldown-cmark-to-cmark = "11.0.2"
pulldown-cmark = "0.12.0"
pulldown-cmark-to-cmark = "16.0.0"
rayon = "1.10.0"
regex = "1.10.5"
serde_yaml = "0.9.34"
slug = "0.1.5"
snafu = "0.8.3"
unicode-normalization = "0.1.23"
filetime = "0.2.23"
[dev-dependencies]
pretty_assertions = "1.4.0"

67
Justfile Normal file
View File

@ -0,0 +1,67 @@
towncrier_cmd := "pipx run towncrier==24.8.0"
_default:
@{{just_executable()}} --choose
# Add a new changelog entry using towncrier
add-changelog:
{{towncrier_cmd}} create --edit
git add changelog.d
# Create a new release
make-new-release:
#!/usr/bin/env bash
set -euo pipefail
get_next_version_number() {
DATEPART=$(date +%y.%-m)
ITERATION=0
while true; do
VERSION_STRING="${DATEPART}.${ITERATION}"
if git rev-list "v$VERSION_STRING" > /dev/null 2>&1; then
((ITERATION++))
else
echo "$VERSION_STRING"
return
fi
done
}
git add .
if ! git diff-index --quiet HEAD; then
printf "Working directory is not clean. Please commit or stash your changes.\n"
exit 1
fi
VERSION=$(get_next_version_number)
COMMITMSG=$(mktemp --tmpdir commitmsg.XXXXXXXXXX)
trap 'rm "$COMMITMSG"' EXIT
set -x
cargo set-version "${VERSION}"
# Construct a git commit message.
# This must be done before the next step so we can leverage the --draft
# flag here to get a list of changes being introduced by this release.
printf "Release v${VERSION}\n\n" > "$COMMITMSG"
{{towncrier_cmd}} build --draft --version "${VERSION}" >> "$COMMITMSG"
# Generate changelog and docs
{{towncrier_cmd}} build --version "${VERSION}"
docs/generate.sh
# Stage all the changes we've prepared
git add .
# There are likely trailing whitespace changes in the changelog, but a single
# run of pre-commit will fix these automatically.
pre-commit run || git add .
git commit --file "$COMMITMSG"
git tag "v${VERSION}"
set +x
printf "\n\nSuccessfully created release %s\n" "v${VERSION}"
printf "\nYou'll probably want to continue with:\n"
printf "\tgit push origin main\n"
printf "\tgit push origin %s\n" "v${VERSION}"

View File

@ -0,0 +1,4 @@
Bump to the minimum supported Rust version to 1.80.0
Obsidian-export now uses [std::sync::LazyLock](https://doc.rust-lang.org/std/sync/struct.LazyLock.html) instead of [lazy_static](https://crates.io/crates/lazy_static), which was only stabilized in Rust 1.80.0.
This change made it possible to drop the external dependency on lazy_static, though as a result of this, compiling with older versions will no longer be possible.

0
changelog.d/.gitignore vendored Normal file
View File

1
changelog.d/14.breaking.md Symbolic link
View File

@ -0,0 +1 @@
252.breaking.md

4
changelog.d/14.fix.md Normal file
View File

@ -0,0 +1,4 @@
Don't escape square brackets in math expressions
The upgrade to [pulldown-cmark](https://crates.io/crates/pulldown-cmark) 0.11 (see Backwards-incompatible Changes) includes official support for LaTeX-style math expressions.
With the markdown parser supporting this syntax natively, math expressions are now processed correctly without edge-cases.

1
changelog.d/154.new.md Symbolic link
View File

@ -0,0 +1 @@
204.new.md

5
changelog.d/204.new.md Normal file
View File

@ -0,0 +1,5 @@
Optionally preserve modified time of exported files
Add a new argument `--preserve-mtime` to keep the original modified time attribute of notes being exported, instead of setting them to the current time.
Contribution made by [Davis Davalos-DeLosh](https://github.com/Programmerino).

View File

@ -0,0 +1,12 @@
Upgrade [pulldown-cmark](https://crates.io/crates/pulldown-cmark) from 0.9 to 0.12
pulldown-cmark is the Markdown/CommonMark parser that is used to read and convert notes (together with [pulldown-cmark-to-cmark](https://crates.io/crates/pulldown-cmark-to-cmark)).
For end-users that call the obsidian-export CLI this upgrade will be mostly transparent, except that Math blocks are now properly processed without getting mangled.
People who use the library directly may face more significant breaking changes if they have custom postprocessors, as pulldown-cmark's events have gone through various breaking changes.
For more information, see:
- <https://github.com/zoni/obsidian-export/pull/252>
- <https://github.com/pulldown-cmark/pulldown-cmark/releases/tag/v0.10.0>
- <https://github.com/zoni/obsidian-export/pull/276/files#diff-b1a35a68f14e696205874893c07fd24fdb88882b47c23cc0e0c80a30c7d53759>

1
changelog.d/252.fix.md Symbolic link
View File

@ -0,0 +1 @@
14.fix.md

1
changelog.d/259.breaking.md Symbolic link
View File

@ -0,0 +1 @@
252.breaking.md

1
changelog.d/285.breaking.md Symbolic link
View File

@ -0,0 +1 @@
252.breaking.md

View File

@ -1,5 +1,5 @@
# Release process
- [ ] Run `./make-new-release.sh`
- [ ] Run `just make-new-release`
- [ ] Push the created release commit/tag to GitHub
- [ ] Wait for builds to turn green (<https://github.com/zoni/obsidian-export/actions>) and confirm everything looks OK.

View File

@ -1,47 +0,0 @@
#!/usr/bin/env bash
set -euo pipefail
get_next_version_number() {
DATEPART=$(date +%y.%-m)
ITERATION=0
while true; do
VERSION_STRING="${DATEPART}.${ITERATION}"
if git rev-list "v$VERSION_STRING" > /dev/null 2>&1; then
((ITERATION++))
else
echo "$VERSION_STRING"
return
fi
done
}
git add .
if ! git diff-index --quiet HEAD; then
printf "Working directory is not clean. Please commit or stash your changes.\n"
exit 1
fi
VERSION=$(get_next_version_number)
git tag "v${VERSION}"
git cliff --latest --prepend CHANGELOG.md > /dev/null
${EDITOR:-vim} CHANGELOG.md
docs/generate.sh
sed -i -E "s/^version = \".+\"$/version = \"${VERSION}\"/" Cargo.toml
cargo check
git add .
# There are likely trailing whitespace changes in the changelog, but a single
# run of pre-commit will fix these automatically.
pre-commit run || git add .
git commit --message "Release v${VERSION}"
git tag "v${VERSION}" --force
printf "\n\nSuccessfully created release %s\n" "v${VERSION}"
printf "\nYou'll probably want to continue with:\n"
printf "\tgit push origin main\n"
printf "\tgit push origin %s\n" "v${VERSION}"

View File

@ -14,11 +14,12 @@ use std::path::{Path, PathBuf};
use std::{fmt, str};
pub use context::Context;
use filetime::set_file_mtime;
use frontmatter::{frontmatter_from_str, frontmatter_to_str};
pub use frontmatter::{Frontmatter, FrontmatterStrategy};
use pathdiff::diff_paths;
use percent_encoding::{utf8_percent_encode, AsciiSet, CONTROLS};
use pulldown_cmark::{CodeBlockKind, CowStr, Event, HeadingLevel, Options, Parser, Tag};
use pulldown_cmark::{CodeBlockKind, CowStr, Event, HeadingLevel, Options, Parser, Tag, TagEnd};
use pulldown_cmark_to_cmark::cmark_with_options;
use rayon::prelude::*;
use references::{ObsidianNoteReference, RefParser, RefParserState, RefType};
@ -160,6 +161,20 @@ pub enum ExportError {
source: ignore::Error,
},
#[snafu(display("Failed to read the mtime of '{}'", path.display()))]
/// This occurs when a file's modified time cannot be read
ModTimeReadError {
path: PathBuf,
source: std::io::Error,
},
#[snafu(display("Failed to set the mtime of '{}'", path.display()))]
/// This occurs when a file's modified time cannot be set
ModTimeSetError {
path: PathBuf,
source: std::io::Error,
},
#[snafu(display("No such file or directory: {}", path.display()))]
/// This occurs when an operation is requested on a file or directory which does not exist.
PathDoesNotExist { path: PathBuf },
@ -227,6 +242,7 @@ pub struct Exporter<'a> {
vault_contents: Option<Vec<PathBuf>>,
walk_options: WalkOptions<'a>,
process_embeds_recursively: bool,
preserve_mtime: bool,
postprocessors: Vec<&'a Postprocessor<'a>>,
embed_postprocessors: Vec<&'a Postprocessor<'a>>,
}
@ -243,6 +259,7 @@ impl<'a> fmt::Debug for Exporter<'a> {
"process_embeds_recursively",
&self.process_embeds_recursively,
)
.field("preserve_mtime", &self.preserve_mtime)
.field(
"postprocessors",
&format!("<{} postprocessors active>", self.postprocessors.len()),
@ -270,6 +287,7 @@ impl<'a> Exporter<'a> {
frontmatter_strategy: FrontmatterStrategy::Auto,
walk_options: WalkOptions::default(),
process_embeds_recursively: true,
preserve_mtime: false,
vault_contents: None,
postprocessors: vec![],
embed_postprocessors: vec![],
@ -312,6 +330,15 @@ impl<'a> Exporter<'a> {
self
}
/// Set whether the modified time of exported files should be preserved.
///
/// When `preserve` is true, the modified time of exported files will be set to the modified
/// time of the source file.
pub fn preserve_mtime(&mut self, preserve: bool) -> &mut Self {
self.preserve_mtime = preserve;
self
}
/// Append a function to the chain of [postprocessors][Postprocessor] to run on exported
/// Obsidian Markdown notes.
pub fn add_postprocessor(&mut self, processor: &'a Postprocessor<'_>) -> &mut Self {
@ -392,7 +419,13 @@ impl<'a> Exporter<'a> {
true => self.parse_and_export_obsidian_note(src, dest),
false => copy_file(src, dest),
}
.context(FileExportSnafu { path: src })
.context(FileExportSnafu { path: src })?;
if self.preserve_mtime {
copy_mtime(src, dest).context(FileExportSnafu { path: src })?;
}
Ok(())
}
fn parse_and_export_obsidian_note(&self, src: &Path, dest: &Path) -> Result<()> {
@ -434,6 +467,7 @@ impl<'a> Exporter<'a> {
#[allow(clippy::too_many_lines)]
#[allow(clippy::panic_in_result_fn)]
#[allow(clippy::shadow_unrelated)]
fn parse_obsidian_note<'b>(
&self,
path: &Path,
@ -445,23 +479,40 @@ impl<'a> Exporter<'a> {
});
}
let content = fs::read_to_string(path).context(ReadSnafu { path })?;
let (frontmatter, content) =
matter::matter(&content).unwrap_or((String::new(), content.clone()));
let frontmatter =
frontmatter_from_str(&frontmatter).context(FrontMatterDecodeSnafu { path })?;
let mut frontmatter = String::new();
let mut parser_options = Options::empty();
parser_options.insert(Options::ENABLE_TABLES);
parser_options.insert(Options::ENABLE_FOOTNOTES);
parser_options.insert(Options::ENABLE_STRIKETHROUGH);
parser_options.insert(Options::ENABLE_TASKLISTS);
let parser_options = Options::ENABLE_TABLES
| Options::ENABLE_FOOTNOTES
| Options::ENABLE_STRIKETHROUGH
| Options::ENABLE_TASKLISTS
| Options::ENABLE_MATH
| Options::ENABLE_YAML_STYLE_METADATA_BLOCKS;
let mut ref_parser = RefParser::new();
let mut events = vec![];
// Most of the time, a reference triggers 5 events: [ or ![, [, <text>, ], ]
let mut buffer = Vec::with_capacity(5);
for event in Parser::new_ext(&content, parser_options) {
let mut parser = Parser::new_ext(&content, parser_options);
'outer: while let Some(event) = parser.next() {
// When encountering a metadata block (frontmatter), collect all events until getting
// to the end of the block, at which point the nested loop will break out to the outer
// loop again.
if matches!(event, Event::Start(Tag::MetadataBlock(_kind))) {
for event in parser.by_ref() {
match event {
Event::Text(cowstr) => frontmatter.push_str(&cowstr),
Event::End(TagEnd::MetadataBlock(_kind)) => {
continue 'outer;
},
_ => panic!(
"Encountered an unexpected event while processing frontmatter in {}. Please report this as a bug with a copy of the note contents and this text: \n\nEvent: {:?}\n",
path.display(),
event
),
}
}
}
if ref_parser.state == RefParserState::Resetting {
events.append(&mut buffer);
buffer.clear();
@ -550,8 +601,9 @@ impl<'a> Exporter<'a> {
if !buffer.is_empty() {
events.append(&mut buffer);
}
Ok((
frontmatter,
frontmatter_from_str(&frontmatter).context(FrontMatterDecodeSnafu { path })?,
events.into_iter().map(event_to_owned).collect(),
))
}
@ -629,20 +681,18 @@ impl<'a> Exporter<'a> {
// into an image reference instead. Slightly hacky, but avoids needing
// to keep another utility function around for this, or introducing an
// extra parameter on make_link_to_file.
Event::Start(Tag::Link(linktype, cowstr1, cowstr2)) => {
Event::Start(Tag::Image(
linktype,
CowStr::from(cowstr1.into_string()),
CowStr::from(cowstr2.into_string()),
))
}
Event::End(Tag::Link(linktype, cowstr1, cowstr2)) => {
Event::End(Tag::Image(
linktype,
CowStr::from(cowstr1.into_string()),
CowStr::from(cowstr2.into_string()),
))
}
Event::Start(Tag::Link {
link_type,
dest_url,
title,
id,
}) => Event::Start(Tag::Image {
link_type,
dest_url: CowStr::from(dest_url.into_string()),
title: CowStr::from(title.into_string()),
id: CowStr::from(id.into_string()),
}),
Event::End(TagEnd::Link) => Event::End(TagEnd::Image),
_ => event,
})
.collect()
@ -674,7 +724,7 @@ impl<'a> Exporter<'a> {
return vec![
Event::Start(Tag::Emphasis),
Event::Text(CowStr::from(reference.display())),
Event::End(Tag::Emphasis),
Event::End(TagEnd::Emphasis),
];
}
let target_file = target_file.unwrap();
@ -698,16 +748,17 @@ impl<'a> Exporter<'a> {
link.push_str(&slugify(section));
}
let link_tag = Tag::Link(
pulldown_cmark::LinkType::Inline,
CowStr::from(link),
CowStr::from(""),
);
let link_tag = Tag::Link {
link_type: pulldown_cmark::LinkType::Inline,
dest_url: CowStr::from(link),
title: CowStr::from(""),
id: CowStr::from(""),
};
vec![
Event::Start(link_tag.clone()),
Event::Start(link_tag),
Event::Text(CowStr::from(reference.display())),
Event::End(link_tag.clone()),
Event::End(TagEnd::Link),
]
}
}
@ -766,6 +817,16 @@ fn create_file(dest: &Path) -> Result<File> {
Ok(file)
}
fn copy_mtime(src: &Path, dest: &Path) -> Result<()> {
let metadata = fs::metadata(src).context(ModTimeReadSnafu { path: src })?;
let modified_time = metadata
.modified()
.context(ModTimeReadSnafu { path: src })?;
set_file_mtime(dest, modified_time.into()).context(ModTimeSetSnafu { path: dest })?;
Ok(())
}
fn copy_file(src: &Path, dest: &Path) -> Result<()> {
fs::copy(src, dest)
.or_else(|err| {
@ -798,8 +859,7 @@ fn reduce_to_section<'a>(events: MarkdownEvents<'a>, section: &str) -> MarkdownE
for event in events {
filtered_events.push(event.clone());
match event {
// FIXME: This should propagate fragment_identifier and classes.
Event::Start(Tag::Heading(level, _fragment_identifier, _classes)) => {
Event::Start(Tag::Heading { level, .. }) => {
last_tag_was_heading = true;
last_level = level;
if currently_in_target_section && level <= section_level {
@ -838,10 +898,11 @@ fn reduce_to_section<'a>(events: MarkdownEvents<'a>, section: &str) -> MarkdownE
fn event_to_owned<'a>(event: Event<'_>) -> Event<'a> {
match event {
Event::Start(tag) => Event::Start(tag_to_owned(tag)),
Event::End(tag) => Event::End(tag_to_owned(tag)),
Event::End(tag) => Event::End(tag),
Event::Text(cowstr) => Event::Text(CowStr::from(cowstr.into_string())),
Event::Code(cowstr) => Event::Code(CowStr::from(cowstr.into_string())),
Event::Html(cowstr) => Event::Html(CowStr::from(cowstr.into_string())),
Event::InlineHtml(cowstr) => Event::InlineHtml(CowStr::from(cowstr.into_string())),
Event::FootnoteReference(cowstr) => {
Event::FootnoteReference(CowStr::from(cowstr.into_string()))
}
@ -849,17 +910,37 @@ fn event_to_owned<'a>(event: Event<'_>) -> Event<'a> {
Event::HardBreak => Event::HardBreak,
Event::Rule => Event::Rule,
Event::TaskListMarker(checked) => Event::TaskListMarker(checked),
Event::InlineMath(cowstr) => Event::InlineMath(CowStr::from(cowstr.into_string())),
Event::DisplayMath(cowstr) => Event::DisplayMath(CowStr::from(cowstr.into_string())),
}
}
fn tag_to_owned<'a>(tag: Tag<'_>) -> Tag<'a> {
match tag {
Tag::Paragraph => Tag::Paragraph,
Tag::Heading(level, _fragment_identifier, _classes) => {
// FIXME: This should propagate fragment_identifier and classes.
Tag::Heading(level, None, Vec::new())
}
Tag::BlockQuote => Tag::BlockQuote,
Tag::Heading {
level: heading_level,
id,
classes,
attrs,
} => Tag::Heading {
level: heading_level,
id: id.map(|cowstr| CowStr::from(cowstr.into_string())),
classes: classes
.into_iter()
.map(|cowstr| CowStr::from(cowstr.into_string()))
.collect(),
attrs: attrs
.into_iter()
.map(|(attr, value)| {
(
CowStr::from(attr.into_string()),
value.map(|cowstr| CowStr::from(cowstr.into_string())),
)
})
.collect(),
},
Tag::BlockQuote(blockquote_kind) => Tag::BlockQuote(blockquote_kind),
Tag::CodeBlock(codeblock_kind) => Tag::CodeBlock(codeblock_kind_to_owned(codeblock_kind)),
Tag::List(optional) => Tag::List(optional),
Tag::Item => Tag::Item,
@ -873,16 +954,33 @@ fn tag_to_owned<'a>(tag: Tag<'_>) -> Tag<'a> {
Tag::Emphasis => Tag::Emphasis,
Tag::Strong => Tag::Strong,
Tag::Strikethrough => Tag::Strikethrough,
Tag::Link(linktype, cowstr1, cowstr2) => Tag::Link(
linktype,
CowStr::from(cowstr1.into_string()),
CowStr::from(cowstr2.into_string()),
),
Tag::Image(linktype, cowstr1, cowstr2) => Tag::Image(
linktype,
CowStr::from(cowstr1.into_string()),
CowStr::from(cowstr2.into_string()),
),
Tag::Link {
link_type,
dest_url,
title,
id,
} => Tag::Link {
link_type,
dest_url: CowStr::from(dest_url.into_string()),
title: CowStr::from(title.into_string()),
id: CowStr::from(id.into_string()),
},
Tag::Image {
link_type,
dest_url,
title,
id,
} => Tag::Image {
link_type,
dest_url: CowStr::from(dest_url.into_string()),
title: CowStr::from(title.into_string()),
id: CowStr::from(id.into_string()),
},
Tag::HtmlBlock => Tag::HtmlBlock,
Tag::MetadataBlock(metadata_block_kind) => Tag::MetadataBlock(metadata_block_kind),
Tag::DefinitionList => Tag::DefinitionList,
Tag::DefinitionListTitle => Tag::DefinitionListTitle,
Tag::DefinitionListDefinition => Tag::DefinitionListDefinition,
}
}

View File

@ -57,6 +57,13 @@ struct Opts {
#[options(no_short, help = "Don't process embeds recursively", default = "false")]
no_recursive_embeds: bool,
#[options(
no_short,
help = "Preserve the mtime of exported files",
default = "false"
)]
preserve_mtime: bool,
#[options(
no_short,
help = "Convert soft line breaks to hard line breaks. This mimics Obsidian's 'Strict line breaks' setting",
@ -97,6 +104,7 @@ fn main() {
let mut exporter = Exporter::new(root, destination);
exporter.frontmatter_strategy(args.frontmatter_strategy);
exporter.process_embeds_recursively(!args.no_recursive_embeds);
exporter.preserve_mtime(args.preserve_mtime);
exporter.walk_options(walk_options);
if args.hard_linebreaks {

View File

@ -360,6 +360,44 @@ fn test_no_recursive_embeds() {
);
}
#[test]
fn test_preserve_mtime() {
let tmp_dir = TempDir::new().expect("failed to make tempdir");
let mut exporter = Exporter::new(
PathBuf::from("tests/testdata/input/main-samples/"),
tmp_dir.path().to_path_buf(),
);
exporter.preserve_mtime(true);
exporter.run().expect("exporter returned error");
let src = "tests/testdata/input/main-samples/obsidian-wikilinks.md";
let dest = tmp_dir.path().join(PathBuf::from("obsidian-wikilinks.md"));
let src_meta = std::fs::metadata(src).unwrap();
let dest_meta = std::fs::metadata(dest).unwrap();
assert_eq!(src_meta.modified().unwrap(), dest_meta.modified().unwrap());
}
#[test]
fn test_no_preserve_mtime() {
let tmp_dir = TempDir::new().expect("failed to make tempdir");
let mut exporter = Exporter::new(
PathBuf::from("tests/testdata/input/main-samples/"),
tmp_dir.path().to_path_buf(),
);
exporter.preserve_mtime(false);
exporter.run().expect("exporter returned error");
let src = "tests/testdata/input/main-samples/obsidian-wikilinks.md";
let dest = tmp_dir.path().join(PathBuf::from("obsidian-wikilinks.md"));
let src_meta = std::fs::metadata(src).unwrap();
let dest_meta = std::fs::metadata(dest).unwrap();
assert_ne!(src_meta.modified().unwrap(), dest_meta.modified().unwrap());
}
#[test]
fn test_non_ascii_filenames() {
let tmp_dir = TempDir::new().expect("failed to make tempdir");

View File

@ -0,0 +1,11 @@
This sentence uses `$` delimiters to show math inline: $\sqrt{3x-1}+(1+x)^2$
This is the same math expression expressed as a block element:
$$\sqrt{3x-1}+(1+x)^2$$
<!-- https://github.com/zoni/obsidian-export/issues/14 -->
With square brackets (inline): $[0, 2\pi]$
With square brackets (block):
$$[0, 2\pi]$$

View File

@ -37,7 +37,7 @@
~~Strikethrough~~
|Table||
|-----|--|
|-----|-|
|Foo|Bar|
[link text](link-location.md)

View File

@ -0,0 +1,10 @@
This sentence uses `$` delimiters to show math inline: $\sqrt{3x-1}+(1+x)^2$
This is the same math expression expressed as a block element:
$$\sqrt{3x-1}+(1+x)^2$$
<!-- https://github.com/zoni/obsidian-export/issues/14 -->
With square brackets (inline): $[0, 2\pi]$
With square brackets (block):
$$[0, 2\pi]$$

33
towncrier.toml Normal file
View File

@ -0,0 +1,33 @@
[tool.towncrier]
name = "obsidian-export"
directory = "changelog.d"
filename = "CHANGELOG.md"
start_string = "<!-- towncrier release notes start -->\n"
underlines = ["", "", ""]
title_format = "## [{version}](https://github.com/zoni/obsidian-export/tree/{version}) - {project_date}"
issue_format = "[#{issue}](https://github.com/zoni/obsidian-export/issues/{issue})"
[[tool.towncrier.type]]
directory = "new"
name = "New Features"
showcontent = true
[[tool.towncrier.type]]
directory = "change"
name = "Changes"
showcontent = true
[[tool.towncrier.type]]
directory = "fix"
name = "Fixes"
showcontent = true
[[tool.towncrier.type]]
directory = "breaking"
name = "Backwards-incompatible Changes"
showcontent = true
[[tool.towncrier.type]]
directory = "deprecation"
name = "Deprecations"
showcontent = true