Update miette to 6.0.1
Test: m rust
Change-Id: Id2d0f05405e31650a643ab92f7bcf6880bd9b49c
diff --git a/crates/miette-derive/.android-checksum.json b/crates/miette-derive/.android-checksum.json
index 4be151a..d0f5ea0 100644
--- a/crates/miette-derive/.android-checksum.json
+++ b/crates/miette-derive/.android-checksum.json
@@ -1 +1 @@
-{"package":null,"files":{".cargo-checksum.json":"56c350001d94f4355e68ade15e96076a589a5418655da5a41f8a26a0d5d4e1ca","Android.bp":"ba150e2abdf659fddec9fb612c5d81547042ba2b7647aa2f9981d0aba204ef3c","Cargo.toml":"7063a079d674da607fd4f2edd787423ca20a124b6648e99f4867ced77eb40fd1","LICENSE":"d4e2ffa43f59103b9893849a22699ec727d5898d7081098310750135c4bf0134","METADATA":"6c2260f22248d406f5fe802f8cb435509469df86074eb65679a9284b862e8b0a","MODULE_LICENSE_APACHE2":"0d6f8afa3940b7f06bebee651376d43bc8b0d5b437337be2696d30377451e93a","cargo_embargo.json":"d17b8bd25b48dae259fb01e76a2efeb790fc436fe351c2ac40912800d1647777","src/code.rs":"ff1c86d9cbe87f54f0de905a5274b4c703a228168b64753a6bf3acfe1bd18356","src/diagnostic.rs":"04811edb4086d614f727fe2e098298a9414489de6e5eeb63b809ec7783e38177","src/diagnostic_arg.rs":"db4788b8b301024401b46f21eb4e29118e59ad9d6790bc0fdec897d1d0dfcffa","src/diagnostic_source.rs":"e6012c7f6b8154ac36006d2a10eddf1311c322e504a9534b870bedb0ce36bd5c","src/fmt.rs":"a6dff276ccdd09a19f9d04ad1a2f3ffa56872c402c94da57c9c8fb3bc891e70f","src/forward.rs":"b344a6a48a90cb9832b8ffa9ca0f913539b1df0c2d34c4975235c5dc39c4a23a","src/help.rs":"cb79fa64bd5e1f9c57ad7995614da1ef461298c20d8c962be4454e2c0f318b49","src/label.rs":"b16cd5a1e3cb9ae09ff28b28bf420e49b306029064411949db0b006b28f4e2a9","src/lib.rs":"d56635c4293a3f4a9264cec62706d2ae2fb4819dadaf598875baeabcdc47013d","src/related.rs":"113fb18b30ad2f05a3552fecb45f01b27bca569494fedfe9471f99a0467d7677","src/severity.rs":"00c2d86de650dde4809add940230eff7981dffea9151d08da377924e0f4cf7f2","src/source_code.rs":"ab68bd7dcdf5121403b3443d5741d770cd2b634de4b5011c1340eb173b42089a","src/url.rs":"4aacdfe28f9a1259ceb59368832c1a601906d64f268024d30856b6196c62aaeb","src/utils.rs":"249eb5284572fe97d24e0c1fff3009100a1fd2739414f70e4fe1542b73e8bbc2"}}
\ No newline at end of file
+{"package":null,"files":{".cargo-checksum.json":"0dc70120bfc5380119fdcb3079b533a3d2fd68ea6381c7496c4fcd3d460a669d","Android.bp":"29af978d63b6898e465b98ae30692c13c93258ec049a8c5913050a87c67e7601","Cargo.toml":"ac7e7f8878a251954538417da23110fcf4cbb6da1e0bc346c5ba18b7c2f5acdb","LICENSE":"5281d46044d26df9efdd5588d65aad4d49c29e46f7f720d727c9d33cd6b4644d","METADATA":"b32972e105176fe1ada6ec3f6d11f32b2547c0f7a826e62247cfd6d183441891","MODULE_LICENSE_APACHE2":"0d6f8afa3940b7f06bebee651376d43bc8b0d5b437337be2696d30377451e93a","cargo_embargo.json":"d17b8bd25b48dae259fb01e76a2efeb790fc436fe351c2ac40912800d1647777","src/code.rs":"ff1c86d9cbe87f54f0de905a5274b4c703a228168b64753a6bf3acfe1bd18356","src/diagnostic.rs":"04811edb4086d614f727fe2e098298a9414489de6e5eeb63b809ec7783e38177","src/diagnostic_arg.rs":"db4788b8b301024401b46f21eb4e29118e59ad9d6790bc0fdec897d1d0dfcffa","src/diagnostic_source.rs":"e6012c7f6b8154ac36006d2a10eddf1311c322e504a9534b870bedb0ce36bd5c","src/fmt.rs":"a6dff276ccdd09a19f9d04ad1a2f3ffa56872c402c94da57c9c8fb3bc891e70f","src/forward.rs":"b344a6a48a90cb9832b8ffa9ca0f913539b1df0c2d34c4975235c5dc39c4a23a","src/help.rs":"cb79fa64bd5e1f9c57ad7995614da1ef461298c20d8c962be4454e2c0f318b49","src/label.rs":"2170aade3cd887d81d472528bb8931542d0a2845c00dd559f30fdabb6a555402","src/lib.rs":"d56635c4293a3f4a9264cec62706d2ae2fb4819dadaf598875baeabcdc47013d","src/related.rs":"113fb18b30ad2f05a3552fecb45f01b27bca569494fedfe9471f99a0467d7677","src/severity.rs":"1f24380a911a143517cf48c170798dea1e36e56c32b4e2309b93840385df65c5","src/source_code.rs":"5593661da6ae95bf91b150de64963136cc19c05e0f736761012b225647a4259c","src/url.rs":"4aacdfe28f9a1259ceb59368832c1a601906d64f268024d30856b6196c62aaeb","src/utils.rs":"249eb5284572fe97d24e0c1fff3009100a1fd2739414f70e4fe1542b73e8bbc2"}}
\ No newline at end of file
diff --git a/crates/miette-derive/.cargo-checksum.json b/crates/miette-derive/.cargo-checksum.json
index 940faa0..c8d2556 100644
--- a/crates/miette-derive/.cargo-checksum.json
+++ b/crates/miette-derive/.cargo-checksum.json
@@ -1 +1 @@
-{"files":{"Cargo.toml":"599ac29de32593292836994f2f10f64950344d3c052f142fa9d0512d652af53c","LICENSE":"610432849befa0e875d601d044e7e5441eb86c83d052681b23aef357b2a0b928","src/code.rs":"a66b16975c657e931bf650617c1652efed89710fdf7d8610f300f6538cdd9ad6","src/diagnostic.rs":"137e53721f10bf12f7bcd47dd699f3c7a44dad1fe7284b6924624c9853e96803","src/diagnostic_arg.rs":"6cc263379779b42e57152399bd8cc3ec872f7c047696705379cee9e147fc4156","src/diagnostic_source.rs":"e99b8decaa0d4dced2a0fd0667ebe348a6ff0169fc1298859c1a656d754ea86c","src/fmt.rs":"de956c7bdcf44458bb77d029c402e8aae3b97fc8de450710824e4b88f059229e","src/forward.rs":"778ab4ce5455efa9b9673ec83ddfae4b7064a4bf57a674a09fd40ef3b2ff2ff7","src/help.rs":"fa141050182100265174e40957d355dfa175d12822e6ef8ed2e5ba3e41abf991","src/label.rs":"762a5728672cd1ed1587bd048b43ea4ffd0974d49d976b3cf0f4282c8633f02a","src/lib.rs":"92aa427146667829d4faa2cf11da57defdedaa0f237384f3bfd7d8a0a593116f","src/related.rs":"fa6af1336fc6184f6727390757fa732d32cde249907fec56638f9a5f41bfcf45","src/severity.rs":"70fc6c592a0ad39fe9414b7115c7e0fb3e64eaa3ba5bef6b34ac35ad08ec6aa2","src/source_code.rs":"c3c8bf539dae2de0b0bc63ccf70dd4973b0516d9b95e9ac4190fa7468cce63de","src/url.rs":"b0ab232341844c0a3d604716624da8a9edcdb9cffbb188e008bfcc3f3046fe25","src/utils.rs":"db268d54ecc04bbba4cfb9b5a7b8b09922b2e4da396fdd47dd955b97a38740a2"},"package":"49e7bc1560b95a3c4a25d03de42fe76ca718ab92d1a22a55b9b4cf67b3ae635c"}
\ No newline at end of file
+{"files":{"Cargo.toml":"5b5ab85a65d01ae008c2bdc6428570f34ca8be1a1c0e73a1c60514fcafb4605f","LICENSE":"02872c3ff23e11da5a9b3289df11b581be36b4956bfb7cfdc3d8043f92d64794","src/code.rs":"a66b16975c657e931bf650617c1652efed89710fdf7d8610f300f6538cdd9ad6","src/diagnostic.rs":"137e53721f10bf12f7bcd47dd699f3c7a44dad1fe7284b6924624c9853e96803","src/diagnostic_arg.rs":"6cc263379779b42e57152399bd8cc3ec872f7c047696705379cee9e147fc4156","src/diagnostic_source.rs":"e99b8decaa0d4dced2a0fd0667ebe348a6ff0169fc1298859c1a656d754ea86c","src/fmt.rs":"de956c7bdcf44458bb77d029c402e8aae3b97fc8de450710824e4b88f059229e","src/forward.rs":"778ab4ce5455efa9b9673ec83ddfae4b7064a4bf57a674a09fd40ef3b2ff2ff7","src/help.rs":"fa141050182100265174e40957d355dfa175d12822e6ef8ed2e5ba3e41abf991","src/label.rs":"5b93399ee4052339d742bad9ad5554a4ec73cedab0e44514fdbb90ee86a6f763","src/lib.rs":"92aa427146667829d4faa2cf11da57defdedaa0f237384f3bfd7d8a0a593116f","src/related.rs":"fa6af1336fc6184f6727390757fa732d32cde249907fec56638f9a5f41bfcf45","src/severity.rs":"7fdfbfd685a2b47eb8bd8b9175141a11caeb0d205242ec0c9b35168c2b537f65","src/source_code.rs":"a573a755faf895e9326f4ab5651a2e16182dbc98aab1ea649102f2d7ee9d27ed","src/url.rs":"b0ab232341844c0a3d604716624da8a9edcdb9cffbb188e008bfcc3f3046fe25","src/utils.rs":"db268d54ecc04bbba4cfb9b5a7b8b09922b2e4da396fdd47dd955b97a38740a2"},"package":"71e622f2a0dd84cbca79bc6c3c33f4fd7dc69faf992216516aacc1d136102800"}
\ No newline at end of file
diff --git a/crates/miette-derive/Android.bp b/crates/miette-derive/Android.bp
index 2fb4dea..ace989a 100644
--- a/crates/miette-derive/Android.bp
+++ b/crates/miette-derive/Android.bp
@@ -17,7 +17,7 @@
name: "libmiette_derive",
crate_name: "miette_derive",
cargo_env_compat: true,
- cargo_pkg_version: "5.10.0",
+ cargo_pkg_version: "6.0.1",
crate_root: "src/lib.rs",
edition: "2018",
rustlibs: [
diff --git a/crates/miette-derive/Cargo.toml b/crates/miette-derive/Cargo.toml
index 84a9fe3..ce212a2 100644
--- a/crates/miette-derive/Cargo.toml
+++ b/crates/miette-derive/Cargo.toml
@@ -12,7 +12,7 @@
[package]
edition = "2018"
name = "miette-derive"
-version = "5.10.0"
+version = "6.0.1"
authors = ["Kat Marchán <kzm@zkat.tech>"]
description = "Derive macros for miette. Like `thiserror` for Diagnostics."
license = "Apache-2.0"
@@ -22,7 +22,7 @@
proc-macro = true
[dependencies.proc-macro2]
-version = "1.0"
+version = "1.0.60"
[dependencies.quote]
version = "1.0"
diff --git a/crates/miette-derive/LICENSE b/crates/miette-derive/LICENSE
index 10fa5cc..545f464 100644
--- a/crates/miette-derive/LICENSE
+++ b/crates/miette-derive/LICENSE
@@ -1,229 +1,229 @@
-Apache License
-
-Version 2.0, January 2004
-
-http://www.apache.org/licenses/ TERMS
-AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
-
- 1. Definitions.
-
-
-
-
- "License" shall mean the terms and conditions for use, reproduction, and
-distribution as defined by Sections 1 through 9 of this document.
-
-
-
-
-"Licensor" shall mean the copyright owner or entity authorized by the copyright
-owner that is granting the License.
-
-
-
- "Legal Entity" shall mean the
-union of the acting entity and all other entities that control, are controlled
-by, or are under common control with that entity. For the purposes of this
-definition, "control" means (i) the power, direct or indirect, to cause the
-direction or management of such entity, whether by contract or otherwise, or (ii)
-ownership of fifty percent (50%) or more of the outstanding shares, or (iii)
-beneficial ownership of such entity.
-
-
-
- "You" (or "Your") shall mean
-an individual or Legal Entity exercising permissions granted by this License.
-
-
-
-
- "Source" form shall mean the preferred form for making modifications,
-including but not limited to software source code, documentation source, and
-configuration files.
-
-
-
- "Object" form shall mean any form resulting
-from mechanical transformation or translation of a Source form, including but not
-limited to compiled object code, generated documentation, and conversions to
-other media types.
-
-
-
- "Work" shall mean the work of authorship,
-whether in Source or Object form, made available under the License, as indicated
-by a copyright notice that is included in or attached to the work (an example is
-provided in the Appendix below).
-
-
-
- "Derivative Works" shall mean any
-work, whether in Source or Object form, that is based on (or derived from) the
-Work and for which the editorial revisions, annotations, elaborations, or other
-modifications represent, as a whole, an original work of authorship. For the
-purposes of this License, Derivative Works shall not include works that remain
-separable from, or merely link (or bind by name) to the interfaces of, the Work
-and Derivative Works thereof.
-
-
-
- "Contribution" shall mean any work
-of authorship, including the original version of the Work and any modifications
-or additions to that Work or Derivative Works thereof, that is intentionally
-submitted to Licensor for inclusion in the Work by the copyright owner or by an
-individual or Legal Entity authorized to submit on behalf of the copyright owner.
-For the purposes of this definition, "submitted" means any form of electronic,
-verbal, or written communication sent to the Licensor or its representatives,
-including but not limited to communication on electronic mailing lists, source
-code control systems, and issue tracking systems that are managed by, or on
-behalf of, the Licensor for the purpose of discussing and improving the Work, but
-excluding communication that is conspicuously marked or otherwise designated in
-writing by the copyright owner as "Not a Contribution."
-
-
-
-
-"Contributor" shall mean Licensor and any individual or Legal Entity on behalf of
-whom a Contribution has been received by Licensor and subsequently incorporated
-within the Work.
-
- 2. Grant of Copyright License. Subject to the terms and
-conditions of this License, each Contributor hereby grants to You a perpetual,
-worldwide, non-exclusive, no-charge, royalty-free, irrevocable copyright license
-to reproduce, prepare Derivative Works of, publicly display, publicly perform,
-sublicense, and distribute the Work and such Derivative Works in Source or Object
-form.
-
- 3. Grant of Patent License. Subject to the terms and conditions of this
-License, each Contributor hereby grants to You a perpetual, worldwide,
-non-exclusive, no-charge, royalty-free, irrevocable (except as stated in this
-section) patent license to make, have made, use, offer to sell, sell, import, and
-otherwise transfer the Work, where such license applies only to those patent
-claims licensable by such Contributor that are necessarily infringed by their
-Contribution(s) alone or by combination of their Contribution(s) with the Work to
-which such Contribution(s) was submitted. If You institute patent litigation
-against any entity (including a cross-claim or counterclaim in a lawsuit)
-alleging that the Work or a Contribution incorporated within the Work constitutes
-direct or contributory patent infringement, then any patent licenses granted to
-You under this License for that Work shall terminate as of the date such
-litigation is filed.
-
- 4. Redistribution. You may reproduce and distribute
-copies of the Work or Derivative Works thereof in any medium, with or without
-modifications, and in Source or Object form, provided that You meet the following
-conditions:
-
- (a) You must give any other recipients of the Work or
-Derivative Works a copy of this License; and
-
- (b) You must cause any
-modified files to carry prominent notices stating that You changed the files;
-and
-
- (c) You must retain, in the Source form of any Derivative Works that
-You distribute, all copyright, patent, trademark, and attribution notices from
-the Source form of the Work, excluding those notices that do not pertain to any
-part of the Derivative Works; and
-
- (d) If the Work includes a "NOTICE" text
-file as part of its distribution, then any Derivative Works that You distribute
-must include a readable copy of the attribution notices contained within such
-NOTICE file, excluding those notices that do not pertain to any part of the
-Derivative Works, in at least one of the following places: within a NOTICE text
-file distributed as part of the Derivative Works; within the Source form or
-documentation, if provided along with the Derivative Works; or, within a display
-generated by the Derivative Works, if and wherever such third-party notices
-normally appear. The contents of the NOTICE file are for informational purposes
-only and do not modify the License. You may add Your own attribution notices
-within Derivative Works that You distribute, alongside or as an addendum to the
-NOTICE text from the Work, provided that such additional attribution notices
-cannot be construed as modifying the License.
-
- You may add Your own
-copyright statement to Your modifications and may provide additional or different
-license terms and conditions for use, reproduction, or distribution of Your
-modifications, or for any such Derivative Works as a whole, provided Your use,
-reproduction, and distribution of the Work otherwise complies with the conditions
-stated in this License.
-
- 5. Submission of Contributions. Unless You explicitly
-state otherwise, any Contribution intentionally submitted for inclusion in the
-Work by You to the Licensor shall be under the terms and conditions of this
-License, without any additional terms or conditions. Notwithstanding the above,
-nothing herein shall supersede or modify the terms of any separate license
-agreement you may have executed with Licensor regarding such Contributions.
-
-
-6. Trademarks. This License does not grant permission to use the trade names,
-trademarks, service marks, or product names of the Licensor, except as required
-for reasonable and customary use in describing the origin of the Work and
-reproducing the content of the NOTICE file.
-
- 7. Disclaimer of Warranty. Unless
-required by applicable law or agreed to in writing, Licensor provides the Work
-(and each Contributor provides its Contributions) on an "AS IS" BASIS, WITHOUT
-WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied, including,
-without limitation, any warranties or conditions of TITLE, NON-INFRINGEMENT,
-MERCHANTABILITY, or FITNESS FOR A PARTICULAR PURPOSE. You are solely responsible
-for determining the appropriateness of using or redistributing the Work and
-assume any risks associated with Your exercise of permissions under this
-License.
-
- 8. Limitation of Liability. In no event and under no legal theory,
-whether in tort (including negligence), contract, or otherwise, unless required
-by applicable law (such as deliberate and grossly negligent acts) or agreed to in
-writing, shall any Contributor be liable to You for damages, including any
-direct, indirect, special, incidental, or consequential damages of any character
-arising as a result of this License or out of the use or inability to use the
-Work (including but not limited to damages for loss of goodwill, work stoppage,
-computer failure or malfunction, or any and all other commercial damages or
-losses), even if such Contributor has been advised of the possibility of such
-damages.
-
- 9. Accepting Warranty or Additional Liability. While redistributing
-the Work or Derivative Works thereof, You may choose to offer, and charge a fee
-for, acceptance of support, warranty, indemnity, or other liability obligations
-and/or rights consistent with this License. However, in accepting such
-obligations, You may act only on Your own behalf and on Your sole responsibility,
-not on behalf of any other Contributor, and only if You agree to indemnify,
-defend, and hold each Contributor harmless for any liability incurred by, or
-claims asserted against, such Contributor by reason of your accepting any such
-warranty or additional liability. END OF TERMS AND CONDITIONS
-
-APPENDIX: How to
-apply the Apache License to your work.
-
-To apply the Apache License to your work,
-attach the following boilerplate notice, with the fields enclosed by brackets
-"[]" replaced with your own identifying information. (Don't include the
-brackets!) The text should be enclosed in the appropriate comment syntax for the
-file format. We also recommend that a file or class name and description of
-purpose be included on the same "printed page" as the copyright notice for easier
-identification within third-party archives.
-
-Copyright [yyyy] Kat
-Marchán
-
-Licensed under the Apache License, Version 2.0 (the "License");
-
-you may
-not use this file except in compliance with the License.
-
-You may obtain a copy
-of the License at
-
-http://www.apache.org/licenses/LICENSE-2.0
-
-Unless required by
-applicable law or agreed to in writing, software
-
-distributed under the License
-is distributed on an "AS IS" BASIS,
-
-WITHOUT WARRANTIES OR CONDITIONS OF ANY
-KIND, either express or implied.
-
-See the License for the specific language
-governing permissions and
-
+Apache License
+
+Version 2.0, January 2004
+
+http://www.apache.org/licenses/ TERMS
+AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
+
+ 1. Definitions.
+
+
+
+
+ "License" shall mean the terms and conditions for use, reproduction, and
+distribution as defined by Sections 1 through 9 of this document.
+
+
+
+
+"Licensor" shall mean the copyright owner or entity authorized by the copyright
+owner that is granting the License.
+
+
+
+ "Legal Entity" shall mean the
+union of the acting entity and all other entities that control, are controlled
+by, or are under common control with that entity. For the purposes of this
+definition, "control" means (i) the power, direct or indirect, to cause the
+direction or management of such entity, whether by contract or otherwise, or (ii)
+ownership of fifty percent (50%) or more of the outstanding shares, or (iii)
+beneficial ownership of such entity.
+
+
+
+ "You" (or "Your") shall mean
+an individual or Legal Entity exercising permissions granted by this License.
+
+
+
+
+ "Source" form shall mean the preferred form for making modifications,
+including but not limited to software source code, documentation source, and
+configuration files.
+
+
+
+ "Object" form shall mean any form resulting
+from mechanical transformation or translation of a Source form, including but not
+limited to compiled object code, generated documentation, and conversions to
+other media types.
+
+
+
+ "Work" shall mean the work of authorship,
+whether in Source or Object form, made available under the License, as indicated
+by a copyright notice that is included in or attached to the work (an example is
+provided in the Appendix below).
+
+
+
+ "Derivative Works" shall mean any
+work, whether in Source or Object form, that is based on (or derived from) the
+Work and for which the editorial revisions, annotations, elaborations, or other
+modifications represent, as a whole, an original work of authorship. For the
+purposes of this License, Derivative Works shall not include works that remain
+separable from, or merely link (or bind by name) to the interfaces of, the Work
+and Derivative Works thereof.
+
+
+
+ "Contribution" shall mean any work
+of authorship, including the original version of the Work and any modifications
+or additions to that Work or Derivative Works thereof, that is intentionally
+submitted to Licensor for inclusion in the Work by the copyright owner or by an
+individual or Legal Entity authorized to submit on behalf of the copyright owner.
+For the purposes of this definition, "submitted" means any form of electronic,
+verbal, or written communication sent to the Licensor or its representatives,
+including but not limited to communication on electronic mailing lists, source
+code control systems, and issue tracking systems that are managed by, or on
+behalf of, the Licensor for the purpose of discussing and improving the Work, but
+excluding communication that is conspicuously marked or otherwise designated in
+writing by the copyright owner as "Not a Contribution."
+
+
+
+
+"Contributor" shall mean Licensor and any individual or Legal Entity on behalf of
+whom a Contribution has been received by Licensor and subsequently incorporated
+within the Work.
+
+ 2. Grant of Copyright License. Subject to the terms and
+conditions of this License, each Contributor hereby grants to You a perpetual,
+worldwide, non-exclusive, no-charge, royalty-free, irrevocable copyright license
+to reproduce, prepare Derivative Works of, publicly display, publicly perform,
+sublicense, and distribute the Work and such Derivative Works in Source or Object
+form.
+
+ 3. Grant of Patent License. Subject to the terms and conditions of this
+License, each Contributor hereby grants to You a perpetual, worldwide,
+non-exclusive, no-charge, royalty-free, irrevocable (except as stated in this
+section) patent license to make, have made, use, offer to sell, sell, import, and
+otherwise transfer the Work, where such license applies only to those patent
+claims licensable by such Contributor that are necessarily infringed by their
+Contribution(s) alone or by combination of their Contribution(s) with the Work to
+which such Contribution(s) was submitted. If You institute patent litigation
+against any entity (including a cross-claim or counterclaim in a lawsuit)
+alleging that the Work or a Contribution incorporated within the Work constitutes
+direct or contributory patent infringement, then any patent licenses granted to
+You under this License for that Work shall terminate as of the date such
+litigation is filed.
+
+ 4. Redistribution. You may reproduce and distribute
+copies of the Work or Derivative Works thereof in any medium, with or without
+modifications, and in Source or Object form, provided that You meet the following
+conditions:
+
+ (a) You must give any other recipients of the Work or
+Derivative Works a copy of this License; and
+
+ (b) You must cause any
+modified files to carry prominent notices stating that You changed the files;
+and
+
+ (c) You must retain, in the Source form of any Derivative Works that
+You distribute, all copyright, patent, trademark, and attribution notices from
+the Source form of the Work, excluding those notices that do not pertain to any
+part of the Derivative Works; and
+
+ (d) If the Work includes a "NOTICE" text
+file as part of its distribution, then any Derivative Works that You distribute
+must include a readable copy of the attribution notices contained within such
+NOTICE file, excluding those notices that do not pertain to any part of the
+Derivative Works, in at least one of the following places: within a NOTICE text
+file distributed as part of the Derivative Works; within the Source form or
+documentation, if provided along with the Derivative Works; or, within a display
+generated by the Derivative Works, if and wherever such third-party notices
+normally appear. The contents of the NOTICE file are for informational purposes
+only and do not modify the License. You may add Your own attribution notices
+within Derivative Works that You distribute, alongside or as an addendum to the
+NOTICE text from the Work, provided that such additional attribution notices
+cannot be construed as modifying the License.
+
+ You may add Your own
+copyright statement to Your modifications and may provide additional or different
+license terms and conditions for use, reproduction, or distribution of Your
+modifications, or for any such Derivative Works as a whole, provided Your use,
+reproduction, and distribution of the Work otherwise complies with the conditions
+stated in this License.
+
+ 5. Submission of Contributions. Unless You explicitly
+state otherwise, any Contribution intentionally submitted for inclusion in the
+Work by You to the Licensor shall be under the terms and conditions of this
+License, without any additional terms or conditions. Notwithstanding the above,
+nothing herein shall supersede or modify the terms of any separate license
+agreement you may have executed with Licensor regarding such Contributions.
+
+
+6. Trademarks. This License does not grant permission to use the trade names,
+trademarks, service marks, or product names of the Licensor, except as required
+for reasonable and customary use in describing the origin of the Work and
+reproducing the content of the NOTICE file.
+
+ 7. Disclaimer of Warranty. Unless
+required by applicable law or agreed to in writing, Licensor provides the Work
+(and each Contributor provides its Contributions) on an "AS IS" BASIS, WITHOUT
+WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied, including,
+without limitation, any warranties or conditions of TITLE, NON-INFRINGEMENT,
+MERCHANTABILITY, or FITNESS FOR A PARTICULAR PURPOSE. You are solely responsible
+for determining the appropriateness of using or redistributing the Work and
+assume any risks associated with Your exercise of permissions under this
+License.
+
+ 8. Limitation of Liability. In no event and under no legal theory,
+whether in tort (including negligence), contract, or otherwise, unless required
+by applicable law (such as deliberate and grossly negligent acts) or agreed to in
+writing, shall any Contributor be liable to You for damages, including any
+direct, indirect, special, incidental, or consequential damages of any character
+arising as a result of this License or out of the use or inability to use the
+Work (including but not limited to damages for loss of goodwill, work stoppage,
+computer failure or malfunction, or any and all other commercial damages or
+losses), even if such Contributor has been advised of the possibility of such
+damages.
+
+ 9. Accepting Warranty or Additional Liability. While redistributing
+the Work or Derivative Works thereof, You may choose to offer, and charge a fee
+for, acceptance of support, warranty, indemnity, or other liability obligations
+and/or rights consistent with this License. However, in accepting such
+obligations, You may act only on Your own behalf and on Your sole responsibility,
+not on behalf of any other Contributor, and only if You agree to indemnify,
+defend, and hold each Contributor harmless for any liability incurred by, or
+claims asserted against, such Contributor by reason of your accepting any such
+warranty or additional liability. END OF TERMS AND CONDITIONS
+
+APPENDIX: How to
+apply the Apache License to your work.
+
+To apply the Apache License to your work,
+attach the following boilerplate notice, with the fields enclosed by brackets
+"[]" replaced with your own identifying information. (Don't include the
+brackets!) The text should be enclosed in the appropriate comment syntax for the
+file format. We also recommend that a file or class name and description of
+purpose be included on the same "printed page" as the copyright notice for easier
+identification within third-party archives.
+
+Copyright [yyyy] Kat
+Marchán
+
+Licensed under the Apache License, Version 2.0 (the "License");
+
+you may
+not use this file except in compliance with the License.
+
+You may obtain a copy
+of the License at
+
+http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by
+applicable law or agreed to in writing, software
+
+distributed under the License
+is distributed on an "AS IS" BASIS,
+
+WITHOUT WARRANTIES OR CONDITIONS OF ANY
+KIND, either express or implied.
+
+See the License for the specific language
+governing permissions and
+
limitations under the License.
\ No newline at end of file
diff --git a/crates/miette-derive/METADATA b/crates/miette-derive/METADATA
index 3756b57..c4f8881 100644
--- a/crates/miette-derive/METADATA
+++ b/crates/miette-derive/METADATA
@@ -1,17 +1,17 @@
name: "miette-derive"
description: "Derive macros for miette. Like `thiserror` for Diagnostics."
third_party {
- version: "5.10.0"
+ version: "6.0.1"
license_type: NOTICE
last_upgrade_date {
- year: 2024
+ year: 2025
month: 2
- day: 2
+ day: 6
}
homepage: "https://crates.io/crates/miette-derive"
identifier {
type: "Archive"
- value: "https://static.crates.io/crates/miette-derive/miette-derive-5.10.0.crate"
- version: "5.10.0"
+ value: "https://static.crates.io/crates/miette-derive/miette-derive-6.0.1.crate"
+ version: "6.0.1"
}
}
diff --git a/crates/miette-derive/src/label.rs b/crates/miette-derive/src/label.rs
index e0bc70a..dd5ec69 100644
--- a/crates/miette-derive/src/label.rs
+++ b/crates/miette-derive/src/label.rs
@@ -20,10 +20,12 @@
label: Option<Display>,
ty: syn::Type,
span: syn::Member,
+ primary: bool,
}
struct LabelAttr {
label: Option<Display>,
+ primary: bool,
}
impl Parse for LabelAttr {
@@ -40,10 +42,22 @@
}
});
let la = input.lookahead1();
- let label = if la.peek(syn::token::Paren) {
- // #[label("{}", x)]
+ let (primary, label) = if la.peek(syn::token::Paren) {
+ // #[label(primary?, "{}", x)]
let content;
parenthesized!(content in input);
+
+ let primary = if content.peek(syn::Ident) {
+ let ident: syn::Ident = content.parse()?;
+ if ident != "primary" {
+ return Err(syn::Error::new(input.span(), "Invalid argument to label() attribute. The argument must be a literal string or the keyword `primary`."));
+ }
+ let _ = content.parse::<Token![,]>();
+ true
+ } else {
+ false
+ };
+
if content.peek(syn::LitStr) {
let fmt = content.parse()?;
let args = if content.is_empty() {
@@ -56,22 +70,27 @@
args,
has_bonus_display: false,
};
- Some(display)
+ (primary, Some(display))
+ } else if !primary {
+ return Err(syn::Error::new(input.span(), "Invalid argument to label() attribute. The argument must be a literal string or the keyword `primary`."));
} else {
- return Err(syn::Error::new(input.span(), "Invalid argument to label() attribute. The first argument must be a literal string."));
+ (primary, None)
}
} else if la.peek(Token![=]) {
// #[label = "blabla"]
input.parse::<Token![=]>()?;
- Some(Display {
- fmt: input.parse()?,
- args: TokenStream::new(),
- has_bonus_display: false,
- })
+ (
+ false,
+ Some(Display {
+ fmt: input.parse()?,
+ args: TokenStream::new(),
+ has_bonus_display: false,
+ }),
+ )
} else {
- None
+ (false, None)
};
- Ok(LabelAttr { label })
+ Ok(LabelAttr { label, primary })
}
}
@@ -100,12 +119,21 @@
})
};
use quote::ToTokens;
- let LabelAttr { label } =
+ let LabelAttr { label, primary } =
syn::parse2::<LabelAttr>(attr.meta.to_token_stream())?;
+
+ if primary && labels.iter().any(|l: &Label| l.primary) {
+ return Err(syn::Error::new(
+ field.span(),
+ "Cannot have more than one primary label.",
+ ));
+ }
+
labels.push(Label {
label,
span,
ty: field.ty.clone(),
+ primary,
});
}
}
@@ -120,13 +148,23 @@
pub(crate) fn gen_struct(&self, fields: &syn::Fields) -> Option<TokenStream> {
let (display_pat, display_members) = display_pat_members(fields);
let labels = self.0.iter().map(|highlight| {
- let Label { span, label, ty } = highlight;
+ let Label {
+ span,
+ label,
+ ty,
+ primary,
+ } = highlight;
let var = quote! { __miette_internal_var };
+ let ctor = if *primary {
+ quote! { miette::LabeledSpan::new_primary_with_span }
+ } else {
+ quote! { miette::LabeledSpan::new_with_span }
+ };
if let Some(display) = label {
let (fmt, args) = display.expand_shorthand_cloned(&display_members);
quote! {
miette::macro_helpers::OptionalWrapper::<#ty>::new().to_option(&self.#span)
- .map(|#var| miette::LabeledSpan::new_with_span(
+ .map(|#var| #ctor(
std::option::Option::Some(format!(#fmt #args)),
#var.clone(),
))
@@ -134,7 +172,7 @@
} else {
quote! {
miette::macro_helpers::OptionalWrapper::<#ty>::new().to_option(&self.#span)
- .map(|#var| miette::LabeledSpan::new_with_span(
+ .map(|#var| #ctor(
std::option::Option::None,
#var.clone(),
))
@@ -161,7 +199,7 @@
let (display_pat, display_members) = display_pat_members(fields);
labels.as_ref().and_then(|labels| {
let variant_labels = labels.0.iter().map(|label| {
- let Label { span, label, ty } = label;
+ let Label { span, label, ty, primary } = label;
let field = match &span {
syn::Member::Named(ident) => ident.clone(),
syn::Member::Unnamed(syn::Index { index, .. }) => {
@@ -169,11 +207,16 @@
}
};
let var = quote! { __miette_internal_var };
+ let ctor = if *primary {
+ quote! { miette::LabeledSpan::new_primary_with_span }
+ } else {
+ quote! { miette::LabeledSpan::new_with_span }
+ };
if let Some(display) = label {
let (fmt, args) = display.expand_shorthand_cloned(&display_members);
quote! {
miette::macro_helpers::OptionalWrapper::<#ty>::new().to_option(#field)
- .map(|#var| miette::LabeledSpan::new_with_span(
+ .map(|#var| #ctor(
std::option::Option::Some(format!(#fmt #args)),
#var.clone(),
))
@@ -181,7 +224,7 @@
} else {
quote! {
miette::macro_helpers::OptionalWrapper::<#ty>::new().to_option(#field)
- .map(|#var| miette::LabeledSpan::new_with_span(
+ .map(|#var| #ctor(
std::option::Option::None,
#var.clone(),
))
diff --git a/crates/miette-derive/src/severity.rs b/crates/miette-derive/src/severity.rs
index 76126de..4f26e4e 100644
--- a/crates/miette-derive/src/severity.rs
+++ b/crates/miette-derive/src/severity.rs
@@ -1,89 +1,89 @@
-use proc_macro2::{Span, TokenStream};
-use quote::quote;
-use syn::{
- parenthesized,
- parse::{Parse, ParseStream},
- Token,
-};
-
-use crate::{
- diagnostic::{DiagnosticConcreteArgs, DiagnosticDef},
- forward::WhichFn,
- utils::gen_all_variants_with,
-};
-
-pub struct Severity(pub syn::Ident);
-
-impl Parse for Severity {
- fn parse(input: ParseStream) -> syn::Result<Self> {
- let ident = input.parse::<syn::Ident>()?;
- if ident == "severity" {
- let la = input.lookahead1();
- if la.peek(syn::token::Paren) {
- let content;
- parenthesized!(content in input);
- let la = content.lookahead1();
- if la.peek(syn::LitStr) {
- let str = content.parse::<syn::LitStr>()?;
- let sev = get_severity(&str.value(), str.span())?;
- Ok(Severity(syn::Ident::new(&sev, str.span())))
- } else {
- let ident = content.parse::<syn::Ident>()?;
- let sev = get_severity(&ident.to_string(), ident.span())?;
- Ok(Severity(syn::Ident::new(&sev, ident.span())))
- }
- } else {
- input.parse::<Token![=]>()?;
- let str = input.parse::<syn::LitStr>()?;
- let sev = get_severity(&str.value(), str.span())?;
- Ok(Severity(syn::Ident::new(&sev, str.span())))
- }
- } else {
- Err(syn::Error::new(
- ident.span(),
- "MIETTE BUG: not a severity option",
- ))
- }
- }
-}
-
-fn get_severity(input: &str, span: Span) -> syn::Result<String> {
- match input.to_lowercase().as_ref() {
- "error" | "err" => Ok("Error".into()),
- "warning" | "warn" => Ok("Warning".into()),
- "advice" | "adv" | "info" => Ok("Advice".into()),
- _ => Err(syn::Error::new(
- span,
- "Invalid severity level. Only Error, Warning, and Advice are supported.",
- )),
- }
-}
-
-impl Severity {
- pub(crate) fn gen_enum(variants: &[DiagnosticDef]) -> Option<TokenStream> {
- gen_all_variants_with(
- variants,
- WhichFn::Severity,
- |ident, fields, DiagnosticConcreteArgs { severity, .. }| {
- let severity = &severity.as_ref()?.0;
- let fields = match fields {
- syn::Fields::Named(_) => quote! { { .. } },
- syn::Fields::Unnamed(_) => quote! { (..) },
- syn::Fields::Unit => quote! {},
- };
- Some(
- quote! { Self::#ident #fields => std::option::Option::Some(miette::Severity::#severity), },
- )
- },
- )
- }
-
- pub(crate) fn gen_struct(&self) -> Option<TokenStream> {
- let sev = &self.0;
- Some(quote! {
- fn severity(&self) -> std::option::Option<miette::Severity> {
- Some(miette::Severity::#sev)
- }
- })
- }
-}
+use proc_macro2::{Span, TokenStream};
+use quote::quote;
+use syn::{
+ parenthesized,
+ parse::{Parse, ParseStream},
+ Token,
+};
+
+use crate::{
+ diagnostic::{DiagnosticConcreteArgs, DiagnosticDef},
+ forward::WhichFn,
+ utils::gen_all_variants_with,
+};
+
+pub struct Severity(pub syn::Ident);
+
+impl Parse for Severity {
+ fn parse(input: ParseStream) -> syn::Result<Self> {
+ let ident = input.parse::<syn::Ident>()?;
+ if ident == "severity" {
+ let la = input.lookahead1();
+ if la.peek(syn::token::Paren) {
+ let content;
+ parenthesized!(content in input);
+ let la = content.lookahead1();
+ if la.peek(syn::LitStr) {
+ let str = content.parse::<syn::LitStr>()?;
+ let sev = get_severity(&str.value(), str.span())?;
+ Ok(Severity(syn::Ident::new(&sev, str.span())))
+ } else {
+ let ident = content.parse::<syn::Ident>()?;
+ let sev = get_severity(&ident.to_string(), ident.span())?;
+ Ok(Severity(syn::Ident::new(&sev, ident.span())))
+ }
+ } else {
+ input.parse::<Token![=]>()?;
+ let str = input.parse::<syn::LitStr>()?;
+ let sev = get_severity(&str.value(), str.span())?;
+ Ok(Severity(syn::Ident::new(&sev, str.span())))
+ }
+ } else {
+ Err(syn::Error::new(
+ ident.span(),
+ "MIETTE BUG: not a severity option",
+ ))
+ }
+ }
+}
+
+fn get_severity(input: &str, span: Span) -> syn::Result<String> {
+ match input.to_lowercase().as_ref() {
+ "error" | "err" => Ok("Error".into()),
+ "warning" | "warn" => Ok("Warning".into()),
+ "advice" | "adv" | "info" => Ok("Advice".into()),
+ _ => Err(syn::Error::new(
+ span,
+ "Invalid severity level. Only Error, Warning, and Advice are supported.",
+ )),
+ }
+}
+
+impl Severity {
+ pub(crate) fn gen_enum(variants: &[DiagnosticDef]) -> Option<TokenStream> {
+ gen_all_variants_with(
+ variants,
+ WhichFn::Severity,
+ |ident, fields, DiagnosticConcreteArgs { severity, .. }| {
+ let severity = &severity.as_ref()?.0;
+ let fields = match fields {
+ syn::Fields::Named(_) => quote! { { .. } },
+ syn::Fields::Unnamed(_) => quote! { (..) },
+ syn::Fields::Unit => quote! {},
+ };
+ Some(
+ quote! { Self::#ident #fields => std::option::Option::Some(miette::Severity::#severity), },
+ )
+ },
+ )
+ }
+
+ pub(crate) fn gen_struct(&self) -> Option<TokenStream> {
+ let sev = &self.0;
+ Some(quote! {
+ fn severity(&self) -> std::option::Option<miette::Severity> {
+ Some(miette::Severity::#sev)
+ }
+ })
+ }
+}
diff --git a/crates/miette-derive/src/source_code.rs b/crates/miette-derive/src/source_code.rs
index 62f28e7..e1b85ab 100644
--- a/crates/miette-derive/src/source_code.rs
+++ b/crates/miette-derive/src/source_code.rs
@@ -10,6 +10,7 @@
pub struct SourceCode {
source_code: syn::Member,
+ is_option: bool,
}
impl SourceCode {
@@ -27,6 +28,19 @@
for (i, field) in fields.iter().enumerate() {
for attr in &field.attrs {
if attr.path().is_ident("source_code") {
+ let is_option = if let syn::Type::Path(syn::TypePath {
+ path: syn::Path { segments, .. },
+ ..
+ }) = &field.ty
+ {
+ segments
+ .last()
+ .map(|seg| seg.ident == "Option")
+ .unwrap_or(false)
+ } else {
+ false
+ };
+
let source_code = if let Some(ident) = field.ident.clone() {
syn::Member::Named(ident)
} else {
@@ -35,7 +49,10 @@
span: field.span(),
})
};
- return Ok(Some(SourceCode { source_code }));
+ return Ok(Some(SourceCode {
+ source_code,
+ is_option,
+ }));
}
}
}
@@ -45,11 +62,21 @@
pub(crate) fn gen_struct(&self, fields: &syn::Fields) -> Option<TokenStream> {
let (display_pat, _display_members) = display_pat_members(fields);
let src = &self.source_code;
+ let ret = if self.is_option {
+ quote! {
+ self.#src.as_ref().map(|s| s as _)
+ }
+ } else {
+ quote! {
+ Some(&self.#src)
+ }
+ };
+
Some(quote! {
#[allow(unused_variables)]
fn source_code(&self) -> std::option::Option<&dyn miette::SourceCode> {
let Self #display_pat = self;
- Some(&self.#src)
+ #ret
}
})
}
@@ -68,10 +95,19 @@
}
};
let variant_name = ident.clone();
+ let ret = if source_code.is_option {
+ quote! {
+ #field.as_ref().map(|s| s as _)
+ }
+ } else {
+ quote! {
+ std::option::Option::Some(#field)
+ }
+ };
match &fields {
syn::Fields::Unit => None,
_ => Some(quote! {
- Self::#variant_name #display_pat => std::option::Option::Some(#field),
+ Self::#variant_name #display_pat => #ret,
}),
}
})
diff --git a/crates/miette/.android-checksum.json b/crates/miette/.android-checksum.json
index 0c22a45..eda9479 100644
--- a/crates/miette/.android-checksum.json
+++ b/crates/miette/.android-checksum.json
@@ -1 +1 @@
-{"package":null,"files":{".cargo-checksum.json":"e2ecacabd05956737a85e4e75ea661564d52d0471f7ed477fbd779dcca439840","Android.bp":"e4fea20a58db4ab9b85ffecf38879040e0e836bf71c04be1a765f335921399a4","CHANGELOG.md":"a87a64fbead27926a63900614e026eef7e70e25706d9deb2342c7e0d66ef486f","CODE_OF_CONDUCT.md":"a4bd04c90895c800d5662d75e618bace729c69ec2f922f45c9e60a4fc3e04e96","CONTRIBUTING.md":"c2aca4ba28d37a84e35806c4f0c329fb9518ff40906d73cebb947b495202ee53","Cargo.toml":"658e4b0ff1897af045e6d2f13670b961339df45cb2ca89183da669a5a755bdaa","LICENSE":"7c6512d88b3127990067585f24881ba1f182c5c49a04cb1975b226b7be95709e","METADATA":"5392b685ffc0ea84ebdeb3f19fc1b1b1bc461df53cac7efb365ba1d70a1595d5","MODULE_LICENSE_APACHE2":"0d6f8afa3940b7f06bebee651376d43bc8b0d5b437337be2696d30377451e93a","Makefile.toml":"e789bcdd20135e93c4532dce514faed0759635735485ca31ad3a2f02b747dd27","README.md":"00ca00514c70fcff5e39079c32c1873ae011150bd1fda4b1b9efdc22b1cbef01","README.tpl":"5697d6b6d27e4ddbb06bd91d6e23883302933a969d3b5573bac44b010b1202f3","android_config.toml":"0f390735e2ee04b3a9d1130bbaca72c29c58ad34322f9ed6b1a22a4763d6fd5f","cargo_embargo.json":"7191eedf1302c4727641965be2efc8ebb1baa210b39476f7d6efe67540763f54","cliff.toml":"9fec58f591ed928c5b77833616b3e18ee13ec0b4baabab7e5ffc3951aa50d4a4","clippy.toml":"27bfdcf0bdcd097497ac6f2250c60d5f0700bedf25a5534eb7dd82766e747f6a","rustfmt.toml":"38087783077635f0ecd4348f01553dfaf070519ad5a6cf0ff1cc32a900d8492b","src/chain.rs":"c193750bbb0deb1ce6bd711bd5c1a6edc97cf2421fd4eb54e1a65bed37917928","src/diagnostic_chain.rs":"aa2d4965f0e3e326d254d3bba384fe1b9492d504e77467a6adb5266053e57485","src/error.rs":"e6708edca2b43fb751cc191eb54ff83cc6ed4790d17fef032f97409c8b8d4605","src/eyreish/context.rs":"daa066a03c7609c7d62bddb047ea54cdc084258788247f143e04645efd217145","src/eyreish/error.rs":"f9b19f17dc4a82e9334f76f0d8846de3a1749b36172da60fe437c0eb95652588","src/eyreish/fmt.rs":"3a65c1dcf3cadb01b6609a4ae69998f95a917b73d26c0900b3fd262bcca1e5f9","src/eyreish/into_diagnostic.rs":"365fb842e4570630d9b612edc528bcf95df08aaec0dd8db50d2ebe2265f5fe75","src/eyreish/kind.rs":"64a031ebf0dc0de47696a229b9e045bb773396c08798583fd013a7aa551d859f","src/eyreish/macros.rs":"9caa49bfc6c9d034a552e5e468a66a62b8ac330b20e50b9e4f30a62d5634c269","src/eyreish/mod.rs":"9d66cc0179ad784aa0ed73bc7c841bbcbed94400b8a2a672c30b074a4680bc32","src/eyreish/ptr.rs":"793928e7b6f9fc5a636448059e94debdb2938c0a27636045b1b4bb1742a0a7e7","src/eyreish/wrapper.rs":"a9c698441a30c91b9b41eff0ebc208f5bd7d574d6f158d78f7cac728b16fd286","src/handler.rs":"fce9a98069ccf863f0c51324a96e9a24e36bec8beed76e6ee801c2ceb741f85b","src/handlers/debug.rs":"1839488cd311ac9499971bcc0b0cd0afdc6566669608e9958c6e80675406acdf","src/handlers/graphical.rs":"9f5cc44de562ea6f34b3a5b5c216677648eadd28030157f18251e7aac6b385c9","src/handlers/json.rs":"839bf02fe3df50c3c1a17802883e6ad5c31bac4911ac1202a38d26cb21d76f5d","src/handlers/mod.rs":"4ab817f09f1a85d35286910e0930a0ecc70969f8fbe61069b76ebf55264673ab","src/handlers/narratable.rs":"88a61e3cefa991700fcea5a7afacdab37dc4981fd67554db96e36f4c2169b791","src/handlers/theme.rs":"b8a29b97de147e180acbed9ae469854f1ff322aa33df4b399d000078a24fc275","src/lib.rs":"18fbc38c384561fc39b9babb1b75207bf525316e4230d55f7ab841bdec9b7ee1","src/macro_helpers.rs":"2a125e4b19c69a344affac9964f32d6c04c380bea50d4643e9973e2a7935382f","src/miette_diagnostic.rs":"6395030c60cee16f04ad471ddb9d77cab70a687594b30f95916af7463ab775f3","src/named_source.rs":"53782458b84e2424cced22842b087181d94fd2b8cbedf80d443182ee5ea46609","src/panic.rs":"49d2f5dcaf1308ef787b40e7174d80492a049f05ed52e379aee10b1bfb71470b","src/protocol.rs":"a7d9a9f36ce7bb22a9d893c482cb8fbb990855f3464bf0b6a897445cc4745d3e","src/source_impls.rs":"ec9e5689f088c960ba753d91568267a0bd1f29941d53990b5845ffd70b65681e"}}
\ No newline at end of file
+{"package":null,"files":{".cargo-checksum.json":"98c3eda8c26d80e1320080b55ccb10ed8b6436f4bb0b6221c0a6069a283d3f94","Android.bp":"2aa20c2a6dd4001ecca96fd5b58b8e0c7e0efd1e72de580f6499f3c24d539b7d","CHANGELOG.md":"65c6979bd66b80aa37756269e96dcc86cfff0100e5c034b2cd685dd5674e245a","CODE_OF_CONDUCT.md":"a4bd04c90895c800d5662d75e618bace729c69ec2f922f45c9e60a4fc3e04e96","CONTRIBUTING.md":"c2aca4ba28d37a84e35806c4f0c329fb9518ff40906d73cebb947b495202ee53","Cargo.toml":"ecf8546a7b5d78acf498f88f89d8e56438b9b508d3515555421d519af18d7597","LICENSE":"7c6512d88b3127990067585f24881ba1f182c5c49a04cb1975b226b7be95709e","METADATA":"18859bbb85332f892a9319cce94d9f263422c3ffba8a110a0b083f5bc328f5a5","MODULE_LICENSE_APACHE2":"0d6f8afa3940b7f06bebee651376d43bc8b0d5b437337be2696d30377451e93a","Makefile.toml":"e789bcdd20135e93c4532dce514faed0759635735485ca31ad3a2f02b747dd27","README.md":"631553a4ca4335fac29631fb1d132c76f4bda351affa89ca4fd892565366cee9","README.tpl":"5697d6b6d27e4ddbb06bd91d6e23883302933a969d3b5573bac44b010b1202f3","android_config.toml":"0f390735e2ee04b3a9d1130bbaca72c29c58ad34322f9ed6b1a22a4763d6fd5f","cargo_embargo.json":"7191eedf1302c4727641965be2efc8ebb1baa210b39476f7d6efe67540763f54","cliff.toml":"9422e51f9349abcfa54e6d9deb0622352a5d29041eb6d1329f5fa4c72da22e7d","clippy.toml":"74d6d343eeb86889927c2bf9290cbc67dc83f2e520519afae082da3dc8fda69c","rustfmt.toml":"b2f9d730640bc5428434fedcff3caf3e1675fd9f4b9ba5b78fc7a880c6f4e2a5","src/chain.rs":"c193750bbb0deb1ce6bd711bd5c1a6edc97cf2421fd4eb54e1a65bed37917928","src/diagnostic_chain.rs":"aa2d4965f0e3e326d254d3bba384fe1b9492d504e77467a6adb5266053e57485","src/error.rs":"3d717cf74c5b796b07f8a09192dda4abaa087915ee0d181733ec9678635e09f1","src/eyreish/context.rs":"daa066a03c7609c7d62bddb047ea54cdc084258788247f143e04645efd217145","src/eyreish/error.rs":"7ca2a397116b08953920e5ff3bd75e26d0aa1faf2cdb19c3ebdcd69af2026bd6","src/eyreish/fmt.rs":"3a65c1dcf3cadb01b6609a4ae69998f95a917b73d26c0900b3fd262bcca1e5f9","src/eyreish/into_diagnostic.rs":"365fb842e4570630d9b612edc528bcf95df08aaec0dd8db50d2ebe2265f5fe75","src/eyreish/kind.rs":"8acfce5634ac76d912bf1039d3f59f03fc59f4a76d6fb31378768eec2d220271","src/eyreish/macros.rs":"9caa49bfc6c9d034a552e5e468a66a62b8ac330b20e50b9e4f30a62d5634c269","src/eyreish/mod.rs":"fcb558244877211acc1e89e19682f726dfb4ffd3923c1b7bac25b9bb49fbce70","src/eyreish/ptr.rs":"793928e7b6f9fc5a636448059e94debdb2938c0a27636045b1b4bb1742a0a7e7","src/eyreish/wrapper.rs":"5c0a555aa3ae8b593012aad03bbf086715a6cd55f70168beeb691f1cb4c092d0","src/handler.rs":"aed1f872d61fd54c62457dfd3e18626fffae9a3ad1194752da33ee43c5ee5cb8","src/handlers/debug.rs":"1839488cd311ac9499971bcc0b0cd0afdc6566669608e9958c6e80675406acdf","src/handlers/graphical.rs":"c292e0177f37564b4b8f695b26265dfb88fd12b65f2cfb41eeea14aaba176ef1","src/handlers/json.rs":"aa20e185318125eea7abf6a26bc55953e833102ce8ace5f63243c5895139f679","src/handlers/mod.rs":"4ab817f09f1a85d35286910e0930a0ecc70969f8fbe61069b76ebf55264673ab","src/handlers/narratable.rs":"88a61e3cefa991700fcea5a7afacdab37dc4981fd67554db96e36f4c2169b791","src/handlers/theme.rs":"e42eadd69467f92271fc75a3204d39497bce98f196d448f308ed6582e8e1cb6c","src/highlighters/blank.rs":"70cfe07316e5c99cd05f8fb2071702129891270381df4cf9588194209513a34b","src/highlighters/mod.rs":"69d37156ca38f5f1ed757ca36bc471f53187451bf6a332119082f0f6c5bac2ef","src/highlighters/syntect.rs":"1996f20a367c4846d2e38d74f9eadf0c22f6d3c8aaab32eeac2004572fe69f39","src/lib.rs":"9515fd47618a74bb8e877e9311168576f502d456551fb666c700552e1904cd6f","src/macro_helpers.rs":"2a125e4b19c69a344affac9964f32d6c04c380bea50d4643e9973e2a7935382f","src/miette_diagnostic.rs":"adfa35237cdda9c071de07218f542926f47981732a7c044fc6fb78b433a3cf8e","src/named_source.rs":"7b29bce74d1459ff1dc0b84e1c3a7417eac72c497d03ec29d3159291c8169ca9","src/panic.rs":"49d2f5dcaf1308ef787b40e7174d80492a049f05ed52e379aee10b1bfb71470b","src/protocol.rs":"1e0ceeeae1e8f2ca1bf034a7e88c07b105f1a16179e5e2ce90d02545c8abd23f","src/source_impls.rs":"ec9e5689f088c960ba753d91568267a0bd1f29941d53990b5845ffd70b65681e"}}
\ No newline at end of file
diff --git a/crates/miette/.cargo-checksum.json b/crates/miette/.cargo-checksum.json
index 7d27774..68ae044 100644
--- a/crates/miette/.cargo-checksum.json
+++ b/crates/miette/.cargo-checksum.json
@@ -1 +1 @@
-{"files":{"CHANGELOG.md":"811bd1126957b274befd66a6e05e5d8dea7d18a7a4e31c2cb6dde86b9b22d0b7","CODE_OF_CONDUCT.md":"6c1484ec74fed9b28348a6c9f42b63c05b01cb515e2e96649a00eefd058570b7","CONTRIBUTING.md":"7093c0c1d9227e3ffd6bc3f6d46bad0e57f1f4f9fd1fb6297b2fd6410fb2582b","Cargo.toml":"3f053f8592b35ad1628219dee1019a76b846ab5cd39307e09e9b13d4d93da202","LICENSE":"cfc7749b96f63bd31c3c42b5c471bf756814053e847c10f3eb003417bc523d30","Makefile.toml":"05e22484f6aa57dcdd27258ea9feb9d81aeb908f8dfea88b3067d37da535da79","README.md":"90de9a829571ecc82bd44812da8e7964faac58206fc97746fad765e8fc484d4d","README.tpl":"821c9d642662efecf91f9ae8283d0e5f5546dcd2210fff8b842d2323bbde6b3f","cliff.toml":"81c34d677a357969145e5ebc0976609b47504eb3fcfe520b9551ef6d7ced80c6","clippy.toml":"41df2f641fd7b29e27bb24f8bdfe10c98cc9bd2b75c074216f55b6a7abdb8f54","rustfmt.toml":"2c1fd2b83aa781b21d1f1511a9b11f2142b999a58795bb72ce7760a8f388fa36","src/chain.rs":"7dec2683bd5b934e74c04644f342b6a7c8c98b488effd22313e18e34f3c3fcc5","src/diagnostic_chain.rs":"a970144f33423f702c9044286ff621d385fd2eaeac04f03a1a6e62da602ab2af","src/error.rs":"9a5de6ced7d1238b57c09cd4383ea08d5edd943898d21920f4285a6685da928e","src/eyreish/context.rs":"c4cef07841191850d0113ff89eeccf6777deaa4517d789098088e75c6ebc77f8","src/eyreish/error.rs":"2df6ef70efc7ec34542d75e72a7a1eca69126f7d568fc38c28c342b3bfe8692c","src/eyreish/fmt.rs":"90ead24156830fdc9b4c46e257f7712446e3e9c56c089c66cb79884a69692da7","src/eyreish/into_diagnostic.rs":"d4d736b0f9e30f1c59504f62bafd9c08aaef24777e9ed935ea1cecb755525f93","src/eyreish/kind.rs":"726796e888193d13de1c036828392ab829ffa0a65add1eed1b1f9cc087f4380f","src/eyreish/macros.rs":"44e43128876b772c62a984ad68cc78d688defd331f5188967e2414d36f79ec48","src/eyreish/mod.rs":"0ac54b8c66b4dc999a985834b8bc1065dd061adbafa6ad426e7149d0971985b6","src/eyreish/ptr.rs":"57a312ab811e0b1e2056c0bd441a06ae920d7e0c5dabe279cad456804f4a9081","src/eyreish/wrapper.rs":"15e81c9a8cc6875270c8e9e46df104a2f9f7addc60183f013d03155db9e58a1f","src/handler.rs":"e36a3d348b0403c2ba9c2adbb5d82aa224a5f347e9ccd830c51715b4151361b4","src/handlers/debug.rs":"bbaa035327b3b3a526ebf5259cfa6be65ec86731b9c0ce9ec7b6175156fa2039","src/handlers/graphical.rs":"72b6a7f7dbde3f1cb50f4774f5cae0d10d8e0d15a51a5ad186fc6a4d67efebdb","src/handlers/json.rs":"29cb4f9726ec7e38eb2a08d6eb4aaa05d317738f21db9b214468d4572e7a69db","src/handlers/mod.rs":"52a553957d79370baf31b0903ae9e932d3edf57869ad2cc3338b834dcab7c45e","src/handlers/narratable.rs":"126793bc006d8909ed960637602ccc2104e05b1f4379fb712f0b6a2034b67a85","src/handlers/theme.rs":"ba5358fb9a89fd1b700ba8a9a0e1d08d227a18d2a1ff990e275e10260f691730","src/lib.rs":"e2c6c3936fce991df0afd28ed468e89e95f4ea24bcd0d6e457ecb2bae5734941","src/macro_helpers.rs":"bb3c63b9cd0e700338d2ecdf57ba0a170dc51daf6bf62e7806f957c1dfd020fe","src/miette_diagnostic.rs":"5782f26494adc6da924de63b3a3f61a53c7636e8fd11b1aabeec3e352a339508","src/named_source.rs":"c00e59fc62c50149730a592a46c8e63c50f486c3ae3ff632550f8aa21dc1bc6b","src/panic.rs":"b1dd1f1fed023fd3ac6b4ff6ba6d3ae8058d481e7bd16c3888c05ce58a5cbe9a","src/protocol.rs":"4d26db6be245425262abd8f2e93774a4c90ca4dcafb34891943f612fb86ed7c3","src/source_impls.rs":"08e33b3a93f5c600a6454b2aa9d1e9c3dd09dfbef95afcb6407ab499c3d69704"},"package":"59bb584eaeeab6bd0226ccf3509a69d7936d148cf3d036ad350abe35e8c6856e"}
\ No newline at end of file
+{"files":{"CHANGELOG.md":"9d7141568346cca8f5a8f6cfa13222d4942ae5a2470d426dee0c8bb799a673e0","CODE_OF_CONDUCT.md":"6c1484ec74fed9b28348a6c9f42b63c05b01cb515e2e96649a00eefd058570b7","CONTRIBUTING.md":"7093c0c1d9227e3ffd6bc3f6d46bad0e57f1f4f9fd1fb6297b2fd6410fb2582b","Cargo.toml":"98d4c0aba7983893b6deda47da8e0cfcbc0eadc5a76e312a2141636c8c1d54d4","LICENSE":"cfc7749b96f63bd31c3c42b5c471bf756814053e847c10f3eb003417bc523d30","Makefile.toml":"05e22484f6aa57dcdd27258ea9feb9d81aeb908f8dfea88b3067d37da535da79","README.md":"58ae3043357edfaa769d2f616833b139933d7a2677955c7eb8fd9dc682d45ce7","README.tpl":"821c9d642662efecf91f9ae8283d0e5f5546dcd2210fff8b842d2323bbde6b3f","cliff.toml":"bee8b26eb8f3f1241e039b89469cbccc5edd1fbe49024a40b42baaf8accf5920","clippy.toml":"f66b3abd56c83e02f1f5a257624c343a0a029439441e3d9417e825a2cf411db9","rustfmt.toml":"f1a22634bbffec62822faa208b352d302c19c2f70e29e57613b9df18d7c2fc36","src/chain.rs":"7dec2683bd5b934e74c04644f342b6a7c8c98b488effd22313e18e34f3c3fcc5","src/diagnostic_chain.rs":"a970144f33423f702c9044286ff621d385fd2eaeac04f03a1a6e62da602ab2af","src/error.rs":"33b256ac386b3c83d3c9749a4de40c217b92da8a1e1429bf27bb1bdbc37121e1","src/eyreish/context.rs":"c4cef07841191850d0113ff89eeccf6777deaa4517d789098088e75c6ebc77f8","src/eyreish/error.rs":"771480534996aedc6cefb4629d4921b139d7a070acf852c6e8fc7ec3d2c11085","src/eyreish/fmt.rs":"90ead24156830fdc9b4c46e257f7712446e3e9c56c089c66cb79884a69692da7","src/eyreish/into_diagnostic.rs":"d4d736b0f9e30f1c59504f62bafd9c08aaef24777e9ed935ea1cecb755525f93","src/eyreish/kind.rs":"0d4dd291809a8effe27f06a4b86c9c3630165fa656c2ba30ce74028358e0dd1c","src/eyreish/macros.rs":"44e43128876b772c62a984ad68cc78d688defd331f5188967e2414d36f79ec48","src/eyreish/mod.rs":"fd121fad03219f54b63ded70a25a9f219e3ec84482ae1334467babd6dc0ada55","src/eyreish/ptr.rs":"57a312ab811e0b1e2056c0bd441a06ae920d7e0c5dabe279cad456804f4a9081","src/eyreish/wrapper.rs":"32b052bb06f4c994635d1000a260a87fa0104541b67f8509ed433a39520ae700","src/handler.rs":"5f74e386d4a2bf47fdf2096138cb94f87c158867063551b99c202aa271261948","src/handlers/debug.rs":"bbaa035327b3b3a526ebf5259cfa6be65ec86731b9c0ce9ec7b6175156fa2039","src/handlers/graphical.rs":"c57b480b13c83c2bc537007f0cfde5c2c6ba2ad21934d15d611583faa1f142c7","src/handlers/json.rs":"f7f22ec628a825693e2042f02ca0f5fcf7b3f9d163c1217c5d1dc53e33a236ca","src/handlers/mod.rs":"52a553957d79370baf31b0903ae9e932d3edf57869ad2cc3338b834dcab7c45e","src/handlers/narratable.rs":"126793bc006d8909ed960637602ccc2104e05b1f4379fb712f0b6a2034b67a85","src/handlers/theme.rs":"74939baeec44d3f497e5e8eaf903c2b1209d7f10abb57b2e5a61d1f6c9d062e5","src/highlighters/blank.rs":"60d5d765ded99ea4c93cbb0d6c6cc2d1a4d10b3f5517481fcfd12f2f34f5ba99","src/highlighters/mod.rs":"79d6a6d6ee377adb3df56d42796f70c7d3fad4566c0476c0c8f47713e4526466","src/highlighters/syntect.rs":"54818065f3a4cb3e5fc2741dedcbb8d165b81bd1cde0a536deec7ad01c8e9cec","src/lib.rs":"a86eefd57ba55b8c185aa4811594946ba2b3fe6ba730b73fef438deca46b32ae","src/macro_helpers.rs":"bb3c63b9cd0e700338d2ecdf57ba0a170dc51daf6bf62e7806f957c1dfd020fe","src/miette_diagnostic.rs":"7def75be49385787d353aaa6773d9dc92eb94687b77fdbd1676e17fc247385aa","src/named_source.rs":"378fd71891cc659218e5c62d02994da00d3fa815c5237730f3bfb8487b01b06a","src/panic.rs":"b1dd1f1fed023fd3ac6b4ff6ba6d3ae8058d481e7bd16c3888c05ce58a5cbe9a","src/protocol.rs":"beae95370c8e309e3d1e56dc06db584cfaf18c0f68f52f4aef82720db6971d66","src/source_impls.rs":"08e33b3a93f5c600a6454b2aa9d1e9c3dd09dfbef95afcb6407ab499c3d69704"},"package":"337e1043bbc086dac9d9674983bef52ac991ce150e09b5b8e35c5a73dd83f66c"}
\ No newline at end of file
diff --git a/crates/miette/Android.bp b/crates/miette/Android.bp
index 4cf927f..25103f3 100644
--- a/crates/miette/Android.bp
+++ b/crates/miette/Android.bp
@@ -17,12 +17,15 @@
name: "libmiette",
crate_name: "miette",
cargo_env_compat: true,
- cargo_pkg_version: "5.10.0",
+ cargo_pkg_version: "6.0.1",
crate_root: "src/lib.rs",
edition: "2018",
- features: ["default"],
+ features: [
+ "default",
+ "derive",
+ "miette-derive",
+ ],
rustlibs: [
- "libonce_cell",
"libthiserror",
"libunicode_width",
],
diff --git a/crates/miette/CHANGELOG.md b/crates/miette/CHANGELOG.md
index 955c27d..4de742a 100644
--- a/crates/miette/CHANGELOG.md
+++ b/crates/miette/CHANGELOG.md
@@ -1,5 +1,63 @@
# `miette` Release Changelog
+<a name="6.0.1"></a>
+## 6.0.1 (2024-02-04)
+
+### Bug Fixes
+
+* **graphical:** oops. Fix theme issue ([8b46679c](https://github.com/zkat/miette/commit/8b46679c3647e1455d91b4c68743c619fb3f3eb3))
+* **fmt:** remove nightly-only fmt flags ([1fa7f524](https://github.com/zkat/miette/commit/1fa7f5241fb91d2e5bad9b0e26bcc7cd5f9011f1))
+* **highlighter:** ugh, missed another spot ([ab7c066e](https://github.com/zkat/miette/commit/ab7c066e7675d8c7ecb956000d278fc31f3bc6a1))
+
+<a name="6.0.0"></a>
+## 6.0.0 (2024-02-04)
+
+The long-awaited 6.0 release of `miette` is here, with TONS of goodies, not
+least of which is syntax highlighting support!
+
+It also comes with a few breaking changes so make sure to check below and
+update your code as needed!
+
+### Features
+
+* **labels:** Add support for primary label in specifying line/col information (#291) ([db0b7e40](https://github.com/zkat/miette/commit/db0b7e403a5ae52ae360991b6508490d8c579886))
+* **derive:** Allow optional sources in derive (#301) ([88d00e0e](https://github.com/zkat/miette/commit/88d00e0e20bf95e03b8f81dcd5adf38c917e190e))
+* **derive:** Make `miette-derive` be able to be turned off (#304) ([c7ba5b7e](https://github.com/zkat/miette/commit/c7ba5b7e52e05991cecd3ca925c710bbe49850b9))
+* **graphical:** Expose additional `textwrap` options (#321) ([fd77257c](https://github.com/zkat/miette/commit/fd77257cee0f5d03aa7dccb4ba8cbaa40c1a88c6))
+* **graphical:** support rendering labels that contain newlines (#318) ([865d67c8](https://github.com/zkat/miette/commit/865d67c8dda119ddd03ac43be22f4fa272a9f433))
+* **graphical:** Add `wrap_lines: bool` option allowing wrapping be disabled entirely (#328) ([b0744462](https://github.com/zkat/miette/commit/b0744462adbbfbb6d845f382db36be883c7f3c45))
+* **graphical:** render disjoint snippets separately for cleaner output (#324) ([19c22143](https://github.com/zkat/miette/commit/19c22143cb544616046784e35c5e78cc5b881289))
+* **deps:** Bump terminal-size to v0.3.0 (#308) ([c0a298e5](https://github.com/zkat/miette/commit/c0a298e5a8d699acf9fcd61b5d5fa4f6279a47ab))
+ * **BREAKING CHANGE**: This requires an MSRV bump to 1.70.0.
+* **source-code:** Don't override provided source code (#300) ([0d5c2ce7](https://github.com/zkat/miette/commit/0d5c2ce7536b0ea205346595d8a00d00bfb6cbd2))
+ * **BREAKING CHANGE**: Source code is no longer overridden if it was provided by the diagnostic's own `source_code()` impl.
+* **source:** use `usize` for length (#265) ([fad0e76a](https://github.com/zkat/miette/commit/fad0e76ad2e19d5cac13cf8324338aca0d623d93))
+ * **BREAKING CHANGE**: This changes `SourceSpan`'s length type to `usize`.
+* **source:** Allow inner source type of a NamedSource to be borrowed (#254) ([1df3b1a5](https://github.com/zkat/miette/commit/1df3b1a537f2e54cd40ec45f5cd851337a22e95a))
+ * **BREAKING CHANGE**: This makes the `NamedSource` type generic over its `Source` type, instead of boxing it.
+* **highlighting:** add syntax highlighting support with syntect crate (#313) ([e65d0a78](https://github.com/zkat/miette/commit/e65d0a78cc639653f061a45d8ce35b1a3551ade7))
+* **deps:** remove is-terminal dep in favor of `std::io::IsTerminal` ([e5c7ae46](https://github.com/zkat/miette/commit/e5c7ae469e40a8bc102e1fca3b8fd4b2ec137696))
+* **deps:** remove once_cell dep in favor of `std::sync::OnceLock` ([4c48584f](https://github.com/zkat/miette/commit/4c48584f304414c6924bede3308b455cfef60749))
+ * **BREAKING CHANGE**: This requires an MSRV bump to 1.70.0.
+* **deps:** bump some semver-breaking deps to newer versions ([29d000f2](https://github.com/zkat/miette/commit/29d000f201b259a056867a2876384f97653a6e9e))
+* **MSRV:** Actually bump the MSRV to 1.70.0 ([ab59a7bc](https://github.com/zkat/miette/commit/ab59a7bc9bceace5761a862ee2ebff3e5943b12f))
+
+### Bug Fixes
+
+* **misc:** Improve ci and fix clippy (#290) ([cc81382a](https://github.com/zkat/miette/commit/cc81382a6070dd226a20e4a39518d88e957ac0e1))
+* **tests:** Fix `cargo test` with default features. (#294) ([1f448e47](https://github.com/zkat/miette/commit/1f448e47751d0f914134b0e9138fdb1a5a95d55c))
+* **clippy:** Add missing semicolons where nothing is returned. (#293) ([06b34823](https://github.com/zkat/miette/commit/06b348230aaf153b8b050322f05e5d185351d2d1))
+* **graphical:** Extend error text span to whole code points (#312) ([a8b4ae01](https://github.com/zkat/miette/commit/a8b4ae012aa0cf03b53a18f013c2b3f76c5040e7))
+* **formatting:** Fix formatting bug when an empty span is not aligned to a char boundary (#314) ([3d6f903d](https://github.com/zkat/miette/commit/3d6f903df0e7c9d0eb9a1fdbbf0028bab5496429))
+* **docs:** add example to README and docs fixing #96 (#319) ([251d6d59](https://github.com/zkat/miette/commit/251d6d59292397458328ef57fb7957faedafd019))
+* **graphical:** rendering bug on small spans in large spans (#316) ([7ff4f874](https://github.com/zkat/miette/commit/7ff4f874d693a665af4df40f4e94505013e3e262))
+* **graphical:** render cause chains for inner errors (#330) ([cb2ae2e1](https://github.com/zkat/miette/commit/cb2ae2e18b446a5e90885faf8a30b5672c307df8))
+* **handler:** remove the two extra `is_terminal` sys call from `MietteHandlerOpts::build` (#325) ([f1dc89c0](https://github.com/zkat/miette/commit/f1dc89c07640445d224b61ef96c6b25fcdf62dee))
+
+### Documentation
+
+* **README:** Move import of `NamedResult` to where it is used (#309) ([d37ada87](https://github.com/zkat/miette/commit/d37ada876a5831d3f47622274e334c9a24aa5d2b))
+
<a name="5.10.0"></a>
## 5.10.0 (2023-07-16)
diff --git a/crates/miette/Cargo.toml b/crates/miette/Cargo.toml
index 4c57331..9c6a3c5 100644
--- a/crates/miette/Cargo.toml
+++ b/crates/miette/Cargo.toml
@@ -11,9 +11,9 @@
[package]
edition = "2018"
-rust-version = "1.56.0"
+rust-version = "1.70.0"
name = "miette"
-version = "5.10.0"
+version = "6.0.1"
authors = ["Kat Marchán <kzm@zkat.tech>"]
exclude = [
"images/",
@@ -38,18 +38,12 @@
version = "0.2.1"
optional = true
-[dependencies.is-terminal]
-version = "0.4.0"
+[dependencies.miette-derive]
+version = "=6.0.1"
optional = true
-[dependencies.miette-derive]
-version = "=5.10.0"
-
-[dependencies.once_cell]
-version = "1.8.0"
-
[dependencies.owo-colors]
-version = "3.0.0"
+version = "3.4.0"
optional = true
[dependencies.serde]
@@ -58,23 +52,27 @@
optional = true
[dependencies.supports-color]
-version = "2.0.0"
+version = "3.0.0"
optional = true
[dependencies.supports-hyperlinks]
-version = "2.0.0"
+version = "3.0.0"
optional = true
[dependencies.supports-unicode]
-version = "2.0.0"
+version = "3.0.0"
+optional = true
+
+[dependencies.syntect]
+version = "5.1.0"
optional = true
[dependencies.terminal_size]
-version = "0.1.17"
+version = "0.3.0"
optional = true
[dependencies.textwrap]
-version = "0.15.0"
+version = "0.16.0"
optional = true
[dependencies.thiserror]
@@ -105,6 +103,9 @@
[dev-dependencies.serde_json]
version = "1.0.64"
+[dev-dependencies.strip-ansi-escapes]
+version = "0.2.0"
+
[dev-dependencies.syn]
version = "2.0"
features = ["full"]
@@ -114,7 +115,8 @@
features = ["diff"]
[features]
-default = []
+default = ["derive"]
+derive = ["miette-derive"]
fancy = [
"fancy-no-backtrace",
"backtrace",
@@ -122,7 +124,6 @@
]
fancy-no-backtrace = [
"owo-colors",
- "is-terminal",
"textwrap",
"terminal_size",
"supports-hyperlinks",
@@ -130,3 +131,7 @@
"supports-unicode",
]
no-format-args-capture = []
+syntect-highlighter = [
+ "fancy-no-backtrace",
+ "syntect",
+]
diff --git a/crates/miette/METADATA b/crates/miette/METADATA
index d75074a..c10974d 100644
--- a/crates/miette/METADATA
+++ b/crates/miette/METADATA
@@ -1,17 +1,17 @@
name: "miette"
description: "Fancy diagnostic reporting library and protocol for us mere mortals who aren\'t compiler hackers."
third_party {
- version: "5.10.0"
+ version: "6.0.1"
license_type: NOTICE
last_upgrade_date {
- year: 2024
+ year: 2025
month: 2
- day: 2
+ day: 6
}
homepage: "https://crates.io/crates/miette"
identifier {
type: "Archive"
- value: "https://static.crates.io/crates/miette/miette-5.10.0.crate"
- version: "5.10.0"
+ value: "https://static.crates.io/crates/miette/miette-6.0.1.crate"
+ version: "6.0.1"
}
}
diff --git a/crates/miette/README.md b/crates/miette/README.md
index 7aabe99..0e936b3 100644
--- a/crates/miette/README.md
+++ b/crates/miette/README.md
@@ -48,6 +48,7 @@
- [... delayed source code](#-delayed-source-code)
- [... handler options](#-handler-options)
- [... dynamic diagnostics](#-dynamic-diagnostics)
+ - [... syntax highlighting](#-syntax-highlighting)
- [Acknowledgements](#acknowledgements)
- [License](#license)
@@ -110,7 +111,7 @@
// The Source that we're gonna be printing snippets out of.
// This can be a String if you don't have or care about file names.
#[source_code]
- src: NamedSource,
+ src: NamedSource<String>,
// Snippets and highlights can be included in the diagnostic!
#[label("This bit here")]
bad_bit: SourceSpan,
@@ -305,6 +306,23 @@
miette = { version = "X.Y.Z", features = ["fancy"] }
```
+Another way to display a diagnostic is by printing them using the debug formatter.
+This is, in fact, what returning diagnostics from main ends up doing.
+To do it yourself, you can write the following:
+
+```rust
+use miette::{IntoDiagnostic, Result};
+use semver::Version;
+
+fn just_a_random_function() {
+ let version_result: Result<Version> = "1.2.x".parse().into_diagnostic();
+ match version_result {
+ Err(e) => println!("{:?}", e),
+ Ok(version) => println!("{}", version),
+ }
+}
+```
+
#### ... diagnostic code URLs
`miette` supports providing a URL for individual diagnostics. This URL will
@@ -594,6 +612,7 @@
.unicode(false)
.context_lines(3)
.tab_width(4)
+ .break_words(true)
.build(),
)
}))
@@ -624,6 +643,38 @@
println!("{:?}", report)
```
+#### ... syntax highlighting
+
+`miette` can be configured to highlight syntax in source code snippets.
+
+<!-- TODO: screenshot goes here once default Theme is decided -->
+
+To use the built-in highlighting functionality, you must enable the
+`syntect-highlighter` crate feature. When this feature is enabled, `miette` will
+automatically use the [`syntect`] crate to highlight the `#[source_code]`
+field of your [`Diagnostic`].
+
+Syntax detection with [`syntect`] is handled by checking 2 methods on the [`SpanContents`] trait, in order:
+* [language()](SpanContents::language) - Provides the name of the language
+ as a string. For example `"Rust"` will indicate Rust syntax highlighting.
+ You can set the language of the [`SpanContents`] produced by a
+ [`NamedSource`] via the [`with_language`](NamedSource::with_language)
+ method.
+* [name()](SpanContents::name) - In the absence of an explicitly set
+ language, the name is assumed to contain a file name or file path.
+ The highlighter will check for a file extension at the end of the name and
+ try to guess the syntax from that.
+
+If you want to use a custom highlighter, you can provide a custom
+implementation of the [`Highlighter`](highlighters::Highlighter)
+trait to [`MietteHandlerOpts`] by calling the
+[`with_syntax_highlighting`](MietteHandlerOpts::with_syntax_highlighting)
+method. See the [`highlighters`] module docs for more details.
+
+### MSRV
+
+This crate requires rustc 1.70.0 or later.
+
### Acknowledgements
`miette` was not developed in a void. It owes enormous credit to various
diff --git a/crates/miette/cliff.toml b/crates/miette/cliff.toml
index 22a0b78..7117c52 100644
--- a/crates/miette/cliff.toml
+++ b/crates/miette/cliff.toml
@@ -1,62 +1,62 @@
-# configuration file for git-cliff (0.1.0)
-
-[changelog]
-# changelog header
-header = """
-# `miette` Release Changelog
-
-"""
-
-# template for the changelog body
-# https://tera.netlify.app/docs/#introduction
-body = """
-{% if version %}\
-<a name="{{ version }}"></a>
-## {{ version | replace(from="v", to="") }} ({{ timestamp | date(format="%Y-%m-%d") }})
-{% else %}\
-## Unreleased
-{% endif %}\
-{% for group, commits in commits | filter(attribute="scope") | group_by(attribute="group") %}
-### {{ group | upper_first }}
-{% for commit in commits %}
-{% if commit.scope %}\
-* **{{ commit.scope }}:** {{ commit.message }} ([{{ commit.id | truncate(length=8, end="") }}](https://github.com/zkat/miette/commit/{{ commit.id }}))
-{%- if commit.breaking %}
- * **BREAKING CHANGE**: {{ commit.breaking_description }}
-{%- endif %}\
-{% endif %}\
-{% endfor %}
-{% endfor %}
-"""
-
-# remove the leading and trailing whitespace from the template
-trim = false
-
-# changelog footer
-# footer = """
-# <!-- generated by git-cliff -->
-# """
-
-[git]
-# allow only conventional commits
-# https://www.conventionalcommits.org
-conventional_commits = true
-# regex for parsing and grouping commits
-commit_parsers = [
- { message = "^feat*", group = "Features"},
- { message = "^fix*", group = "Bug Fixes"},
- { message = "^doc*", group = "Documentation"},
- { message = "^perf*", group = "Performance"},
- { message = "^refactor*", group = "Refactor"},
- { message = "^style*", group = "Styling"},
- { message = "^test*", group = "Testing"},
- { message = "^chore\\(release\\): prepare for*", skip = true},
- { message = "^chore*", group = "Miscellaneous Tasks"},
- { body = ".*security", group = "Security"},
-]
-# filter out the commits that are not matched by commit parsers
-filter_commits = true
-# glob pattern for matching git tags
-# tag_pattern = "v?[0-9]*"
-# regex for skipping tags
-# skip_tags = "v0.1.0-beta.1"
+# configuration file for git-cliff (0.1.0)
+
+[changelog]
+# changelog header
+header = """
+# `miette` Release Changelog
+
+"""
+
+# template for the changelog body
+# https://tera.netlify.app/docs/#introduction
+body = """
+{% if version %}\
+<a name="{{ version }}"></a>
+## {{ version | replace(from="v", to="") }} ({{ timestamp | date(format="%Y-%m-%d") }})
+{% else %}\
+## Unreleased
+{% endif %}\
+{% for group, commits in commits | filter(attribute="scope") | group_by(attribute="group") %}
+### {{ group | upper_first }}
+{% for commit in commits %}
+{% if commit.scope %}\
+* **{{ commit.scope }}:** {{ commit.message }} ([{{ commit.id | truncate(length=8, end="") }}](https://github.com/zkat/miette/commit/{{ commit.id }}))
+{%- if commit.breaking %}
+ * **BREAKING CHANGE**: {{ commit.breaking_description }}
+{%- endif %}\
+{% endif %}\
+{% endfor %}
+{% endfor %}
+"""
+
+# remove the leading and trailing whitespace from the template
+trim = false
+
+# changelog footer
+# footer = """
+# <!-- generated by git-cliff -->
+# """
+
+[git]
+# allow only conventional commits
+# https://www.conventionalcommits.org
+conventional_commits = true
+# regex for parsing and grouping commits
+commit_parsers = [
+ { message = "^feat*", group = "Features"},
+ { message = "^fix*", group = "Bug Fixes"},
+ { message = "^doc*", group = "Documentation"},
+ { message = "^perf*", group = "Performance"},
+ { message = "^refactor*", group = "Refactor"},
+ { message = "^style*", group = "Styling"},
+ { message = "^test*", group = "Testing"},
+ { message = "^chore\\(release\\): prepare for*", skip = true},
+ { message = "^chore*", group = "Miscellaneous Tasks"},
+ { body = ".*security", group = "Security"},
+]
+# filter out the commits that are not matched by commit parsers
+filter_commits = true
+# glob pattern for matching git tags
+# tag_pattern = "v?[0-9]*"
+# regex for skipping tags
+# skip_tags = "v0.1.0-beta.1"
diff --git a/crates/miette/clippy.toml b/crates/miette/clippy.toml
index 0d369b5..1645c19 100644
--- a/crates/miette/clippy.toml
+++ b/crates/miette/clippy.toml
@@ -1 +1 @@
-msrv = "1.56.0"
+msrv = "1.70.0"
diff --git a/crates/miette/rustfmt.toml b/crates/miette/rustfmt.toml
index 8f9ebdd..3a26366 100644
--- a/crates/miette/rustfmt.toml
+++ b/crates/miette/rustfmt.toml
@@ -1,3 +1 @@
edition = "2021"
-wrap_comments = true
-format_code_in_doc_comments = true
diff --git a/crates/miette/src/error.rs b/crates/miette/src/error.rs
index 56041ca..4e57a78 100644
--- a/crates/miette/src/error.rs
+++ b/crates/miette/src/error.rs
@@ -1,27 +1,51 @@
-use std::io;
+use std::{fmt, io};
use thiserror::Error;
-use crate::{self as miette, Diagnostic};
+use crate::Diagnostic;
/**
Error enum for miette. Used by certain operations in the protocol.
*/
-#[derive(Debug, Diagnostic, Error)]
+#[derive(Debug, Error)]
pub enum MietteError {
/// Wrapper around [`std::io::Error`]. This is returned when something went
/// wrong while reading a [`SourceCode`](crate::SourceCode).
#[error(transparent)]
- #[diagnostic(code(miette::io_error), url(docsrs))]
IoError(#[from] io::Error),
/// Returned when a [`SourceSpan`](crate::SourceSpan) extends beyond the
/// bounds of a given [`SourceCode`](crate::SourceCode).
#[error("The given offset is outside the bounds of its Source")]
- #[diagnostic(
- code(miette::span_out_of_bounds),
- help("Double-check your spans. Do you have an off-by-one error?"),
- url(docsrs)
- )]
OutOfBounds,
}
+
+impl Diagnostic for MietteError {
+ fn code<'a>(&'a self) -> Option<Box<dyn fmt::Display + 'a>> {
+ match self {
+ MietteError::IoError(_) => Some(Box::new("miette::io_error")),
+ MietteError::OutOfBounds => Some(Box::new("miette::span_out_of_bounds")),
+ }
+ }
+
+ fn help<'a>(&'a self) -> Option<Box<dyn fmt::Display + 'a>> {
+ match self {
+ MietteError::IoError(_) => None,
+ MietteError::OutOfBounds => Some(Box::new(
+ "Double-check your spans. Do you have an off-by-one error?",
+ )),
+ }
+ }
+
+ fn url<'a>(&'a self) -> Option<Box<dyn fmt::Display + 'a>> {
+ let crate_version = env!("CARGO_PKG_VERSION");
+ let variant = match self {
+ MietteError::IoError(_) => "#variant.IoError",
+ MietteError::OutOfBounds => "#variant.OutOfBounds",
+ };
+ Some(Box::new(format!(
+ "https://docs.rs/miette/{}/miette/enum.MietteError.html{}",
+ crate_version, variant,
+ )))
+ }
+}
diff --git a/crates/miette/src/eyreish/error.rs b/crates/miette/src/eyreish/error.rs
index 6b0dc34..677f368 100644
--- a/crates/miette/src/eyreish/error.rs
+++ b/crates/miette/src/eyreish/error.rs
@@ -30,9 +30,9 @@
/// Create a new error object from a printable error message.
///
- /// If the argument implements std::error::Error, prefer `Report::new`
+ /// If the argument implements [`std::error::Error`], prefer `Report::new`
/// instead which preserves the underlying error's cause chain and
- /// backtrace. If the argument may or may not implement std::error::Error
+ /// backtrace. If the argument may or may not implement [`std::error::Error`]
/// now or in the future, use `miette!(err)` which handles either way
/// correctly.
///
@@ -206,7 +206,7 @@
/// Create a new error from an error message to wrap the existing error.
///
/// For attaching a higher level error message to a `Result` as it is
- /// propagated, the [crate::WrapErr] extension trait may be more
+ /// propagated, the [`WrapErr`](crate::WrapErr) extension trait may be more
/// convenient than this function.
///
/// The primary reason to use `error.wrap_err(...)` instead of
@@ -233,7 +233,7 @@
unsafe { Report::construct(error, vtable, handler) }
}
- /// Compatibility re-export of wrap_err for interop with `anyhow`
+ /// Compatibility re-export of `wrap_err` for interop with `anyhow`
pub fn context<D>(self, msg: D) -> Self
where
D: Display + Send + Sync + 'static,
diff --git a/crates/miette/src/eyreish/kind.rs b/crates/miette/src/eyreish/kind.rs
index 4eb9fef..ce60b50 100644
--- a/crates/miette/src/eyreish/kind.rs
+++ b/crates/miette/src/eyreish/kind.rs
@@ -1,111 +1,111 @@
-#![allow(missing_debug_implementations, missing_docs)]
-// Tagged dispatch mechanism for resolving the behavior of `miette!($expr)`.
-//
-// When miette! is given a single expr argument to turn into miette::Report, we
-// want the resulting Report to pick up the input's implementation of source()
-// and backtrace() if it has a std::error::Error impl, otherwise require nothing
-// more than Display and Debug.
-//
-// Expressed in terms of specialization, we want something like:
-//
-// trait EyreNew {
-// fn new(self) -> Report;
-// }
-//
-// impl<T> EyreNew for T
-// where
-// T: Display + Debug + Send + Sync + 'static,
-// {
-// default fn new(self) -> Report {
-// /* no std error impl */
-// }
-// }
-//
-// impl<T> EyreNew for T
-// where
-// T: std::error::Error + Send + Sync + 'static,
-// {
-// fn new(self) -> Report {
-// /* use std error's source() and backtrace() */
-// }
-// }
-//
-// Since specialization is not stable yet, instead we rely on autoref behavior
-// of method resolution to perform tagged dispatch. Here we have two traits
-// AdhocKind and TraitKind that both have an miette_kind() method. AdhocKind is
-// implemented whether or not the caller's type has a std error impl, while
-// TraitKind is implemented only when a std error impl does exist. The ambiguity
-// is resolved by AdhocKind requiring an extra autoref so that it has lower
-// precedence.
-//
-// The miette! macro will set up the call in this form:
-//
-// #[allow(unused_imports)]
-// use $crate::private::{AdhocKind, TraitKind};
-// let error = $msg;
-// (&error).miette_kind().new(error)
-
-use super::Report;
-use core::fmt::{Debug, Display};
-
-use crate::Diagnostic;
-
-pub struct Adhoc;
-
-pub trait AdhocKind: Sized {
- #[inline]
- fn miette_kind(&self) -> Adhoc {
- Adhoc
- }
-}
-
-impl<T> AdhocKind for &T where T: ?Sized + Display + Debug + Send + Sync + 'static {}
-
-impl Adhoc {
- #[cfg_attr(track_caller, track_caller)]
- pub fn new<M>(self, message: M) -> Report
- where
- M: Display + Debug + Send + Sync + 'static,
- {
- Report::from_adhoc(message)
- }
-}
-
-pub struct Trait;
-
-pub trait TraitKind: Sized {
- #[inline]
- fn miette_kind(&self) -> Trait {
- Trait
- }
-}
-
-impl<E> TraitKind for E where E: Into<Report> {}
-
-impl Trait {
- #[cfg_attr(track_caller, track_caller)]
- pub fn new<E>(self, error: E) -> Report
- where
- E: Into<Report>,
- {
- error.into()
- }
-}
-
-pub struct Boxed;
-
-pub trait BoxedKind: Sized {
- #[inline]
- fn miette_kind(&self) -> Boxed {
- Boxed
- }
-}
-
-impl BoxedKind for Box<dyn Diagnostic + Send + Sync> {}
-
-impl Boxed {
- #[cfg_attr(track_caller, track_caller)]
- pub fn new(self, error: Box<dyn Diagnostic + Send + Sync>) -> Report {
- Report::from_boxed(error)
- }
-}
+#![allow(missing_debug_implementations, missing_docs)]
+// Tagged dispatch mechanism for resolving the behavior of `miette!($expr)`.
+//
+// When miette! is given a single expr argument to turn into miette::Report, we
+// want the resulting Report to pick up the input's implementation of source()
+// and backtrace() if it has a std::error::Error impl, otherwise require nothing
+// more than Display and Debug.
+//
+// Expressed in terms of specialization, we want something like:
+//
+// trait EyreNew {
+// fn new(self) -> Report;
+// }
+//
+// impl<T> EyreNew for T
+// where
+// T: Display + Debug + Send + Sync + 'static,
+// {
+// default fn new(self) -> Report {
+// /* no std error impl */
+// }
+// }
+//
+// impl<T> EyreNew for T
+// where
+// T: std::error::Error + Send + Sync + 'static,
+// {
+// fn new(self) -> Report {
+// /* use std error's source() and backtrace() */
+// }
+// }
+//
+// Since specialization is not stable yet, instead we rely on autoref behavior
+// of method resolution to perform tagged dispatch. Here we have two traits
+// AdhocKind and TraitKind that both have an miette_kind() method. AdhocKind is
+// implemented whether or not the caller's type has a std error impl, while
+// TraitKind is implemented only when a std error impl does exist. The ambiguity
+// is resolved by AdhocKind requiring an extra autoref so that it has lower
+// precedence.
+//
+// The miette! macro will set up the call in this form:
+//
+// #[allow(unused_imports)]
+// use $crate::private::{AdhocKind, TraitKind};
+// let error = $msg;
+// (&error).miette_kind().new(error)
+
+use super::Report;
+use core::fmt::{Debug, Display};
+
+use crate::Diagnostic;
+
+pub struct Adhoc;
+
+pub trait AdhocKind: Sized {
+ #[inline]
+ fn miette_kind(&self) -> Adhoc {
+ Adhoc
+ }
+}
+
+impl<T> AdhocKind for &T where T: ?Sized + Display + Debug + Send + Sync + 'static {}
+
+impl Adhoc {
+ #[cfg_attr(track_caller, track_caller)]
+ pub fn new<M>(self, message: M) -> Report
+ where
+ M: Display + Debug + Send + Sync + 'static,
+ {
+ Report::from_adhoc(message)
+ }
+}
+
+pub struct Trait;
+
+pub trait TraitKind: Sized {
+ #[inline]
+ fn miette_kind(&self) -> Trait {
+ Trait
+ }
+}
+
+impl<E> TraitKind for E where E: Into<Report> {}
+
+impl Trait {
+ #[cfg_attr(track_caller, track_caller)]
+ pub fn new<E>(self, error: E) -> Report
+ where
+ E: Into<Report>,
+ {
+ error.into()
+ }
+}
+
+pub struct Boxed;
+
+pub trait BoxedKind: Sized {
+ #[inline]
+ fn miette_kind(&self) -> Boxed {
+ Boxed
+ }
+}
+
+impl BoxedKind for Box<dyn Diagnostic + Send + Sync> {}
+
+impl Boxed {
+ #[cfg_attr(track_caller, track_caller)]
+ pub fn new(self, error: Box<dyn Diagnostic + Send + Sync>) -> Report {
+ Report::from_boxed(error)
+ }
+}
diff --git a/crates/miette/src/eyreish/mod.rs b/crates/miette/src/eyreish/mod.rs
index 0efceed..0dfd878 100644
--- a/crates/miette/src/eyreish/mod.rs
+++ b/crates/miette/src/eyreish/mod.rs
@@ -7,8 +7,7 @@
use core::fmt::Display;
use std::error::Error as StdError;
-
-use once_cell::sync::OnceCell;
+use std::sync::OnceLock;
#[allow(unreachable_pub)]
pub use into_diagnostic::*;
@@ -62,7 +61,7 @@
pub type ErrorHook =
Box<dyn Fn(&(dyn Diagnostic + 'static)) -> Box<dyn ReportHandler> + Sync + Send + 'static>;
-static HOOK: OnceCell<ErrorHook> = OnceCell::new();
+static HOOK: OnceLock<ErrorHook> = OnceLock::new();
/// Error indicating that [`set_hook()`] was unable to install the provided
/// [`ErrorHook`].
diff --git a/crates/miette/src/eyreish/wrapper.rs b/crates/miette/src/eyreish/wrapper.rs
index 91a5ef3..6e65eb7 100644
--- a/crates/miette/src/eyreish/wrapper.rs
+++ b/crates/miette/src/eyreish/wrapper.rs
@@ -163,7 +163,7 @@
}
fn source_code(&self) -> Option<&dyn miette::SourceCode> {
- Some(&self.source_code)
+ self.error.source_code().or(Some(&self.source_code))
}
fn related<'a>(&'a self) -> Option<Box<dyn Iterator<Item = &'a dyn Diagnostic> + 'a>> {
@@ -197,7 +197,7 @@
}
fn source_code(&self) -> Option<&dyn miette::SourceCode> {
- Some(&self.source_code)
+ self.error.source_code().or(Some(&self.source_code))
}
fn related<'a>(&'a self) -> Option<Box<dyn Iterator<Item = &'a dyn Diagnostic> + 'a>> {
@@ -232,3 +232,88 @@
self.error.source()
}
}
+
+#[cfg(test)]
+mod tests {
+ use thiserror::Error;
+
+ use crate::{Diagnostic, LabeledSpan, Report, SourceCode, SourceSpan};
+
+ #[derive(Error, Debug)]
+ #[error("inner")]
+ struct Inner {
+ pub(crate) at: SourceSpan,
+ pub(crate) source_code: Option<String>,
+ }
+
+ impl Diagnostic for Inner {
+ fn labels(&self) -> Option<Box<dyn Iterator<Item = LabeledSpan> + '_>> {
+ Some(Box::new(std::iter::once(LabeledSpan::underline(self.at))))
+ }
+
+ fn source_code(&self) -> Option<&dyn SourceCode> {
+ self.source_code.as_ref().map(|s| s as _)
+ }
+ }
+
+ #[derive(Error, Debug)]
+ #[error("outer")]
+ struct Outer {
+ pub(crate) errors: Vec<Inner>,
+ }
+
+ impl Diagnostic for Outer {
+ fn related<'a>(&'a self) -> Option<Box<dyn Iterator<Item = &'a dyn Diagnostic> + 'a>> {
+ Some(Box::new(self.errors.iter().map(|e| e as _)))
+ }
+ }
+
+ #[test]
+ fn no_override() {
+ let inner_source = "hello world";
+ let outer_source = "abc";
+
+ let report = Report::from(Inner {
+ at: (0..5).into(),
+ source_code: Some(inner_source.to_string()),
+ })
+ .with_source_code(outer_source.to_string());
+
+ let underlined = String::from_utf8(
+ report
+ .source_code()
+ .unwrap()
+ .read_span(&(0..5).into(), 0, 0)
+ .unwrap()
+ .data()
+ .to_vec(),
+ )
+ .unwrap();
+ assert_eq!(underlined, "hello");
+ }
+
+ #[test]
+ #[cfg(feature = "fancy")]
+ fn two_source_codes() {
+ let inner_source = "hello world";
+ let outer_source = "abc";
+
+ let report = Report::from(Outer {
+ errors: vec![
+ Inner {
+ at: (0..5).into(),
+ source_code: Some(inner_source.to_string()),
+ },
+ Inner {
+ at: (1..2).into(),
+ source_code: None,
+ },
+ ],
+ })
+ .with_source_code(outer_source.to_string());
+
+ let message = format!("{:?}", report);
+ assert!(message.contains(inner_source));
+ assert!(message.contains(outer_source));
+ }
+}
diff --git a/crates/miette/src/handler.rs b/crates/miette/src/handler.rs
index e983a55..dcf8b13 100644
--- a/crates/miette/src/handler.rs
+++ b/crates/miette/src/handler.rs
@@ -1,5 +1,7 @@
use std::fmt;
+use crate::highlighters::Highlighter;
+use crate::highlighters::MietteHighlighter;
use crate::protocol::Diagnostic;
use crate::GraphicalReportHandler;
use crate::GraphicalTheme;
@@ -55,6 +57,11 @@
pub(crate) context_lines: Option<usize>,
pub(crate) tab_width: Option<usize>,
pub(crate) with_cause_chain: Option<bool>,
+ pub(crate) break_words: Option<bool>,
+ pub(crate) wrap_lines: Option<bool>,
+ pub(crate) word_separator: Option<textwrap::WordSeparator>,
+ pub(crate) word_splitter: Option<textwrap::WordSplitter>,
+ pub(crate) highlighter: Option<MietteHighlighter>,
}
impl MietteHandlerOpts {
@@ -80,12 +87,79 @@
self
}
+ /// Set a syntax highlighter when rendering in graphical mode.
+ /// Use [`force_graphical()`](MietteHandlerOpts::force_graphical()) to
+ /// force graphical mode.
+ ///
+ /// Syntax highlighting is disabled by default unless the
+ /// `syntect-highlighter` feature is enabled. Call this method
+ /// to override the default and use a custom highlighter
+ /// implmentation instead.
+ ///
+ /// Use
+ /// [`without_syntax_highlighting()`](MietteHandlerOpts::without_syntax_highlighting())
+ /// To disable highlighting completely.
+ ///
+ /// Setting this option will not force color output. In all cases, the
+ /// current color configuration via
+ /// [`color()`](MietteHandlerOpts::color()) takes precedence over
+ /// highlighter configuration.
+ pub fn with_syntax_highlighting(
+ mut self,
+ highlighter: impl Highlighter + Send + Sync + 'static,
+ ) -> Self {
+ self.highlighter = Some(MietteHighlighter::from(highlighter));
+ self
+ }
+
+ /// Disables syntax highlighting when rendering in graphical mode.
+ /// Use [`force_graphical()`](MietteHandlerOpts::force_graphical()) to
+ /// force graphical mode.
+ ///
+ /// Syntax highlighting is disabled by default unless the
+ /// `syntect-highlighter` feature is enabled. Call this method if you want
+ /// to disable highlighting when building with this feature.
+ pub fn without_syntax_highlighting(mut self) -> Self {
+ self.highlighter = Some(MietteHighlighter::nocolor());
+ self
+ }
+
/// Sets the width to wrap the report at. Defaults to 80.
pub fn width(mut self, width: usize) -> Self {
self.width = Some(width);
self
}
+ /// If true, long lines can be wrapped.
+ ///
+ /// If false, long lines will not be broken when they exceed the width.
+ ///
+ /// Defaults to true.
+ pub fn wrap_lines(mut self, wrap_lines: bool) -> Self {
+ self.wrap_lines = Some(wrap_lines);
+ self
+ }
+
+ /// If true, long words can be broken when wrapping.
+ ///
+ /// If false, long words will not be broken when they exceed the width.
+ ///
+ /// Defaults to true.
+ pub fn break_words(mut self, break_words: bool) -> Self {
+ self.break_words = Some(break_words);
+ self
+ }
+ /// Sets the `textwrap::WordSeparator` to use when determining wrap points.
+ pub fn word_separator(mut self, word_separator: textwrap::WordSeparator) -> Self {
+ self.word_separator = Some(word_separator);
+ self
+ }
+
+ /// Sets the `textwrap::WordSplitter` to use when determining wrap points.
+ pub fn word_splitter(mut self, word_splitter: textwrap::WordSplitter) -> Self {
+ self.word_splitter = Some(word_splitter);
+ self
+ }
/// Include the cause chain of the top-level error in the report.
pub fn with_cause_chain(mut self) -> Self {
self.with_cause_chain = Some(true);
@@ -212,11 +286,33 @@
} else {
ThemeStyles::none()
};
+ #[cfg(not(feature = "syntect-highlighter"))]
+ let highlighter = self.highlighter.unwrap_or_else(MietteHighlighter::nocolor);
+ #[cfg(feature = "syntect-highlighter")]
+ let highlighter = if self.color == Some(false) {
+ MietteHighlighter::nocolor()
+ } else if self.color == Some(true)
+ || supports_color::on(supports_color::Stream::Stderr).is_some()
+ {
+ match self.highlighter {
+ Some(highlighter) => highlighter,
+ None => match self.rgb_colors {
+ // Because the syntect highlighter currently only supports 24-bit truecolor,
+ // respect RgbColor::Never by disabling the highlighter.
+ // TODO: In the future, find a way to convert the RGB syntect theme
+ // into an ANSI color theme.
+ RgbColors::Never => MietteHighlighter::nocolor(),
+ _ => MietteHighlighter::syntect_truecolor(),
+ },
+ }
+ } else {
+ MietteHighlighter::nocolor()
+ };
let theme = self.theme.unwrap_or(GraphicalTheme { characters, styles });
- let mut handler = GraphicalReportHandler::new()
+ let mut handler = GraphicalReportHandler::new_themed(theme)
.with_width(width)
- .with_links(linkify)
- .with_theme(theme);
+ .with_links(linkify);
+ handler.highlighter = highlighter;
if let Some(with_cause_chain) = self.with_cause_chain {
if with_cause_chain {
handler = handler.with_cause_chain();
@@ -233,6 +329,19 @@
if let Some(w) = self.tab_width {
handler = handler.tab_width(w);
}
+ if let Some(b) = self.break_words {
+ handler = handler.with_break_words(b)
+ }
+ if let Some(b) = self.wrap_lines {
+ handler = handler.with_wrap_lines(b)
+ }
+ if let Some(s) = self.word_separator {
+ handler = handler.with_word_separator(s)
+ }
+ if let Some(s) = self.word_splitter {
+ handler = handler.with_word_splitter(s)
+ }
+
MietteHandler {
inner: Box::new(handler),
}
diff --git a/crates/miette/src/handlers/graphical.rs b/crates/miette/src/handlers/graphical.rs
index b5dd754..d7e1883 100644
--- a/crates/miette/src/handlers/graphical.rs
+++ b/crates/miette/src/handlers/graphical.rs
@@ -1,12 +1,13 @@
use std::fmt::{self, Write};
-use owo_colors::{OwoColorize, Style};
+use owo_colors::{OwoColorize, Style, StyledList};
use unicode_width::UnicodeWidthChar;
use crate::diagnostic_chain::{DiagnosticChain, ErrorKind};
use crate::handlers::theme::*;
+use crate::highlighters::{Highlighter, MietteHighlighter};
use crate::protocol::{Diagnostic, Severity};
-use crate::{LabeledSpan, MietteError, ReportHandler, SourceCode, SourceSpan, SpanContents};
+use crate::{LabeledSpan, ReportHandler, SourceCode, SourceSpan, SpanContents};
/**
A [`ReportHandler`] that displays a given [`Report`](crate::Report) in a
@@ -30,6 +31,11 @@
pub(crate) context_lines: usize,
pub(crate) tab_width: usize,
pub(crate) with_cause_chain: bool,
+ pub(crate) wrap_lines: bool,
+ pub(crate) break_words: bool,
+ pub(crate) word_separator: Option<textwrap::WordSeparator>,
+ pub(crate) word_splitter: Option<textwrap::WordSplitter>,
+ pub(crate) highlighter: MietteHighlighter,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
@@ -51,6 +57,11 @@
context_lines: 1,
tab_width: 4,
with_cause_chain: true,
+ wrap_lines: true,
+ break_words: true,
+ word_separator: None,
+ word_splitter: None,
+ highlighter: MietteHighlighter::default(),
}
}
@@ -63,7 +74,12 @@
footer: None,
context_lines: 1,
tab_width: 4,
+ wrap_lines: true,
with_cause_chain: true,
+ break_words: true,
+ word_separator: None,
+ word_splitter: None,
+ highlighter: MietteHighlighter::default(),
}
}
@@ -122,6 +138,30 @@
self
}
+ /// Enables or disables wrapping of lines to fit the width.
+ pub fn with_wrap_lines(mut self, wrap_lines: bool) -> Self {
+ self.wrap_lines = wrap_lines;
+ self
+ }
+
+ /// Enables or disables breaking of words during wrapping.
+ pub fn with_break_words(mut self, break_words: bool) -> Self {
+ self.break_words = break_words;
+ self
+ }
+
+ /// Sets the word separator to use when wrapping.
+ pub fn with_word_separator(mut self, word_separator: textwrap::WordSeparator) -> Self {
+ self.word_separator = Some(word_separator);
+ self
+ }
+
+ /// Sets the word splitter to usewhen wrapping.
+ pub fn with_word_splitter(mut self, word_splitter: textwrap::WordSplitter) -> Self {
+ self.word_splitter = Some(word_splitter);
+ self
+ }
+
/// Sets the 'global' footer for this handler.
pub fn with_footer(mut self, footer: String) -> Self {
self.footer = Some(footer);
@@ -133,6 +173,23 @@
self.context_lines = lines;
self
}
+
+ /// Enable syntax highlighting for source code snippets, using the given
+ /// [`Highlighter`]. See the [crate::highlighters] crate for more details.
+ pub fn with_syntax_highlighting(
+ mut self,
+ highlighter: impl Highlighter + Send + Sync + 'static,
+ ) -> Self {
+ self.highlighter = MietteHighlighter::from(highlighter);
+ self
+ }
+
+ /// Disable syntax highlighting. This uses the
+ /// [`crate::highlighters::BlankHighlighter`] as a no-op highlighter.
+ pub fn without_syntax_highlighting(mut self) -> Self {
+ self.highlighter = MietteHighlighter::nocolor();
+ self
+ }
}
impl Default for GraphicalReportHandler {
@@ -159,10 +216,18 @@
if let Some(footer) = &self.footer {
writeln!(f)?;
let width = self.termwidth.saturating_sub(4);
- let opts = textwrap::Options::new(width)
+ let mut opts = textwrap::Options::new(width)
.initial_indent(" ")
- .subsequent_indent(" ");
- writeln!(f, "{}", textwrap::fill(footer, opts))?;
+ .subsequent_indent(" ")
+ .break_words(self.break_words);
+ if let Some(word_separator) = self.word_separator {
+ opts = opts.word_separator(word_separator);
+ }
+ if let Some(word_splitter) = self.word_splitter.clone() {
+ opts = opts.word_splitter(word_splitter);
+ }
+
+ writeln!(f, "{}", self.wrap(footer, opts))?;
}
Ok(())
}
@@ -212,11 +277,18 @@
let initial_indent = format!(" {} ", severity_icon.style(severity_style));
let rest_indent = format!(" {} ", self.theme.characters.vbar.style(severity_style));
let width = self.termwidth.saturating_sub(2);
- let opts = textwrap::Options::new(width)
+ let mut opts = textwrap::Options::new(width)
.initial_indent(&initial_indent)
- .subsequent_indent(&rest_indent);
+ .subsequent_indent(&rest_indent)
+ .break_words(self.break_words);
+ if let Some(word_separator) = self.word_separator {
+ opts = opts.word_separator(word_separator);
+ }
+ if let Some(word_splitter) = self.word_splitter.clone() {
+ opts = opts.word_splitter(word_splitter);
+ }
- writeln!(f, "{}", textwrap::fill(&diagnostic.to_string(), opts))?;
+ writeln!(f, "{}", self.wrap(&diagnostic.to_string(), opts))?;
if !self.with_cause_chain {
return Ok(());
@@ -251,23 +323,32 @@
)
.style(severity_style)
.to_string();
- let opts = textwrap::Options::new(width)
+ let mut opts = textwrap::Options::new(width)
.initial_indent(&initial_indent)
- .subsequent_indent(&rest_indent);
+ .subsequent_indent(&rest_indent)
+ .break_words(self.break_words);
+ if let Some(word_separator) = self.word_separator {
+ opts = opts.word_separator(word_separator);
+ }
+ if let Some(word_splitter) = self.word_splitter.clone() {
+ opts = opts.word_splitter(word_splitter);
+ }
+
match error {
ErrorKind::Diagnostic(diag) => {
let mut inner = String::new();
- // Don't print footer for inner errors
let mut inner_renderer = self.clone();
+ // Don't print footer for inner errors
inner_renderer.footer = None;
+ // Cause chains are already flattened, so don't double-print the nested error
inner_renderer.with_cause_chain = false;
inner_renderer.render_report(&mut inner, diag)?;
- writeln!(f, "{}", textwrap::fill(&inner, opts))?;
+ writeln!(f, "{}", self.wrap(&inner, opts))?;
}
ErrorKind::StdError(err) => {
- writeln!(f, "{}", textwrap::fill(&err.to_string(), opts))?;
+ writeln!(f, "{}", self.wrap(&err.to_string(), opts))?;
}
}
}
@@ -280,10 +361,18 @@
if let Some(help) = diagnostic.help() {
let width = self.termwidth.saturating_sub(4);
let initial_indent = " help: ".style(self.theme.styles.help).to_string();
- let opts = textwrap::Options::new(width)
+ let mut opts = textwrap::Options::new(width)
.initial_indent(&initial_indent)
- .subsequent_indent(" ");
- writeln!(f, "{}", textwrap::fill(&help.to_string(), opts))?;
+ .subsequent_indent(" ")
+ .break_words(self.break_words);
+ if let Some(word_separator) = self.word_separator {
+ opts = opts.word_separator(word_separator);
+ }
+ if let Some(word_splitter) = self.word_splitter.clone() {
+ opts = opts.word_splitter(word_splitter);
+ }
+
+ writeln!(f, "{}", self.wrap(&help.to_string(), opts))?;
}
Ok(())
}
@@ -295,6 +384,9 @@
parent_src: Option<&dyn SourceCode>,
) -> fmt::Result {
if let Some(related) = diagnostic.related() {
+ let mut inner_renderer = self.clone();
+ // Re-enable the printing of nested cause chains for related errors
+ inner_renderer.with_cause_chain = true;
writeln!(f)?;
for rel in related {
match rel.severity() {
@@ -302,12 +394,12 @@
Some(Severity::Warning) => write!(f, "Warning: ")?,
Some(Severity::Advice) => write!(f, "Advice: ")?,
};
- self.render_header(f, rel)?;
- self.render_causes(f, rel)?;
+ inner_renderer.render_header(f, rel)?;
+ inner_renderer.render_causes(f, rel)?;
let src = rel.source_code().or(parent_src);
- self.render_snippets(f, rel, src)?;
- self.render_footer(f, rel)?;
- self.render_related(f, rel, src)?;
+ inner_renderer.render_snippets(f, rel, src)?;
+ inner_renderer.render_footer(f, rel)?;
+ inner_renderer.render_related(f, rel, src)?;
}
}
Ok(())
@@ -319,78 +411,81 @@
diagnostic: &(dyn Diagnostic),
opt_source: Option<&dyn SourceCode>,
) -> fmt::Result {
- if let Some(source) = opt_source {
- if let Some(labels) = diagnostic.labels() {
- let mut labels = labels.collect::<Vec<_>>();
- labels.sort_unstable_by_key(|l| l.inner().offset());
- if !labels.is_empty() {
- let contents = labels
- .iter()
- .map(|label| {
- source.read_span(label.inner(), self.context_lines, self.context_lines)
- })
- .collect::<Result<Vec<Box<dyn SpanContents<'_>>>, MietteError>>()
- .map_err(|_| fmt::Error)?;
- let mut contexts = Vec::with_capacity(contents.len());
- for (right, right_conts) in labels.iter().cloned().zip(contents.iter()) {
- if contexts.is_empty() {
- contexts.push((right, right_conts));
- } else {
- let (left, left_conts) = contexts.last().unwrap().clone();
- let left_end = left.offset() + left.len();
- let right_end = right.offset() + right.len();
- if left_conts.line() + left_conts.line_count() >= right_conts.line() {
- // The snippets will overlap, so we create one Big Chunky Boi
- let new_span = LabeledSpan::new(
- left.label().map(String::from),
- left.offset(),
- if right_end >= left_end {
- // Right end goes past left end
- right_end - left.offset()
- } else {
- // right is contained inside left
- left.len()
- },
- );
- if source
- .read_span(
- new_span.inner(),
- self.context_lines,
- self.context_lines,
- )
- .is_ok()
- {
- contexts.pop();
- contexts.push((
- // We'll throw this away later
- new_span, left_conts,
- ));
- } else {
- contexts.push((right, right_conts));
- }
- } else {
- contexts.push((right, right_conts));
- }
- }
- }
- for (ctx, _) in contexts {
- self.render_context(f, source, &ctx, &labels[..])?;
- }
+ let source = match opt_source {
+ Some(source) => source,
+ None => return Ok(()),
+ };
+ let labels = match diagnostic.labels() {
+ Some(labels) => labels,
+ None => return Ok(()),
+ };
+
+ let mut labels = labels.collect::<Vec<_>>();
+ labels.sort_unstable_by_key(|l| l.inner().offset());
+
+ let mut contexts = Vec::with_capacity(labels.len());
+ for right in labels.iter().cloned() {
+ let right_conts = source
+ .read_span(right.inner(), self.context_lines, self.context_lines)
+ .map_err(|_| fmt::Error)?;
+
+ if contexts.is_empty() {
+ contexts.push((right, right_conts));
+ continue;
+ }
+
+ let (left, left_conts) = contexts.last().unwrap();
+ if left_conts.line() + left_conts.line_count() >= right_conts.line() {
+ // The snippets will overlap, so we create one Big Chunky Boi
+ let left_end = left.offset() + left.len();
+ let right_end = right.offset() + right.len();
+ let new_end = std::cmp::max(left_end, right_end);
+
+ let new_span = LabeledSpan::new(
+ left.label().map(String::from),
+ left.offset(),
+ new_end - left.offset(),
+ );
+ // Check that the two contexts can be combined
+ if let Ok(new_conts) =
+ source.read_span(new_span.inner(), self.context_lines, self.context_lines)
+ {
+ contexts.pop();
+ // We'll throw the contents away later
+ contexts.push((new_span, new_conts));
+ continue;
}
}
+
+ contexts.push((right, right_conts));
}
+ for (ctx, _) in contexts {
+ self.render_context(f, source, &ctx, &labels[..])?;
+ }
+
Ok(())
}
- fn render_context<'a>(
+ fn render_context(
&self,
f: &mut impl fmt::Write,
- source: &'a dyn SourceCode,
+ source: &dyn SourceCode,
context: &LabeledSpan,
labels: &[LabeledSpan],
) -> fmt::Result {
let (contents, lines) = self.get_lines(source, context.inner())?;
+ // only consider labels from the context as primary label
+ let ctx_labels = labels.iter().filter(|l| {
+ context.inner().offset() <= l.inner().offset()
+ && l.inner().offset() + l.inner().len()
+ <= context.inner().offset() + context.inner().len()
+ });
+ let primary_label = ctx_labels
+ .clone()
+ .find(|label| label.primary())
+ .or_else(|| ctx_labels.clone().next());
+
// sorting is your friend
let labels = labels
.iter()
@@ -398,6 +493,8 @@
.map(|(label, st)| FancySpan::new(label.label().map(String::from), *label.inner(), st))
.collect::<Vec<_>>();
+ let mut highlighter_state = self.highlighter.start_highlighter_state(&*contents);
+
// The max number of gutter-lines that will be active at any given
// point. We need this to figure out indentation, so we do one loop
// over the lines to see what the damage is gonna be.
@@ -405,7 +502,7 @@
for line in &lines {
let mut num_highlights = 0;
for hl in &labels {
- if !line.span_line_only(hl) && line.span_applies(hl) {
+ if !line.span_line_only(hl) && line.span_applies_gutter(hl) {
num_highlights += 1;
}
}
@@ -431,19 +528,33 @@
self.theme.characters.hbar,
)?;
- if let Some(source_name) = contents.name() {
+ // If there is a primary label, then use its span
+ // as the reference point for line/column information.
+ let primary_contents = match primary_label {
+ Some(label) => source
+ .read_span(label.inner(), 0, 0)
+ .map_err(|_| fmt::Error)?,
+ None => contents,
+ };
+
+ if let Some(source_name) = primary_contents.name() {
let source_name = source_name.style(self.theme.styles.link);
writeln!(
f,
"[{}:{}:{}]",
source_name,
- contents.line() + 1,
- contents.column() + 1
+ primary_contents.line() + 1,
+ primary_contents.column() + 1
)?;
} else if lines.len() <= 1 {
writeln!(f, "{}", self.theme.characters.hbar.to_string().repeat(3))?;
} else {
- writeln!(f, "[{}:{}]", contents.line() + 1, contents.column() + 1)?;
+ writeln!(
+ f,
+ "[{}:{}]",
+ primary_contents.line() + 1,
+ primary_contents.column() + 1
+ )?;
}
// Now it's time for the fun part--actually rendering everything!
@@ -457,7 +568,9 @@
self.render_line_gutter(f, max_gutter, line, &labels)?;
// And _now_ we can print out the line text itself!
- self.render_line_text(f, &line.text)?;
+ let styled_text =
+ StyledList::from(highlighter_state.highlight_line(&line.text)).to_string();
+ self.render_line_text(f, &styled_text)?;
// Next, we write all the highlights that apply to this particular line.
let (single_line, multi_line): (Vec<_>, Vec<_>) = labels
@@ -468,7 +581,13 @@
// no line number!
self.write_no_linum(f, linum_width)?;
// gutter _again_
- self.render_highlight_gutter(f, max_gutter, line, &labels)?;
+ self.render_highlight_gutter(
+ f,
+ max_gutter,
+ line,
+ &labels,
+ LabelRenderMode::SingleLine,
+ )?;
self.render_single_line_highlights(
f,
line,
@@ -480,11 +599,7 @@
}
for hl in multi_line {
if hl.label().is_some() && line.span_ends(hl) && !line.span_starts(hl) {
- // no line number!
- self.write_no_linum(f, linum_width)?;
- // gutter _again_
- self.render_highlight_gutter(f, max_gutter, line, &labels)?;
- self.render_multi_line_end(f, hl)?;
+ self.render_multi_line_end(f, &labels, max_gutter, linum_width, line, hl)?;
}
}
}
@@ -498,6 +613,91 @@
Ok(())
}
+ fn render_multi_line_end(
+ &self,
+ f: &mut impl fmt::Write,
+ labels: &[FancySpan],
+ max_gutter: usize,
+ linum_width: usize,
+ line: &Line,
+ label: &FancySpan,
+ ) -> fmt::Result {
+ // no line number!
+ self.write_no_linum(f, linum_width)?;
+
+ if let Some(label_parts) = label.label_parts() {
+ // if it has a label, how long is it?
+ let (first, rest) = label_parts
+ .split_first()
+ .expect("cannot crash because rest would have been None, see docs on the `label` field of FancySpan");
+
+ if rest.is_empty() {
+ // gutter _again_
+ self.render_highlight_gutter(
+ f,
+ max_gutter,
+ line,
+ &labels,
+ LabelRenderMode::SingleLine,
+ )?;
+
+ self.render_multi_line_end_single(
+ f,
+ first,
+ label.style,
+ LabelRenderMode::SingleLine,
+ )?;
+ } else {
+ // gutter _again_
+ self.render_highlight_gutter(
+ f,
+ max_gutter,
+ line,
+ &labels,
+ LabelRenderMode::MultiLineFirst,
+ )?;
+
+ self.render_multi_line_end_single(
+ f,
+ first,
+ label.style,
+ LabelRenderMode::MultiLineFirst,
+ )?;
+ for label_line in rest {
+ // no line number!
+ self.write_no_linum(f, linum_width)?;
+ // gutter _again_
+ self.render_highlight_gutter(
+ f,
+ max_gutter,
+ line,
+ &labels,
+ LabelRenderMode::MultiLineRest,
+ )?;
+ self.render_multi_line_end_single(
+ f,
+ label_line,
+ label.style,
+ LabelRenderMode::MultiLineRest,
+ )?;
+ }
+ }
+ } else {
+ // gutter _again_
+ self.render_highlight_gutter(
+ f,
+ max_gutter,
+ line,
+ &labels,
+ LabelRenderMode::SingleLine,
+ )?;
+ // has no label
+ writeln!(f, "{}", self.theme.characters.hbar.style(label.style))?;
+ }
+
+ Ok(())
+ }
+
fn render_line_gutter(
&self,
f: &mut impl fmt::Write,
@@ -510,7 +710,7 @@
}
let chars = &self.theme.characters;
let mut gutter = String::new();
- let applicable = highlights.iter().filter(|hl| line.span_applies(hl));
+ let applicable = highlights.iter().filter(|hl| line.span_applies_gutter(hl));
let mut arrow = false;
for (i, hl) in applicable.enumerate() {
if line.span_starts(hl) {
@@ -566,33 +766,121 @@
max_gutter: usize,
line: &Line,
highlights: &[FancySpan],
+ render_mode: LabelRenderMode,
) -> fmt::Result {
if max_gutter == 0 {
return Ok(());
}
+
+ // keeps track of how many colums wide the gutter is
+ // important for ansi since simply measuring the size of the final string
+ // gives the wrong result when the string contains ansi codes.
+ let mut gutter_cols = 0;
+
let chars = &self.theme.characters;
let mut gutter = String::new();
- let applicable = highlights.iter().filter(|hl| line.span_applies(hl));
+ let applicable = highlights.iter().filter(|hl| line.span_applies_gutter(hl));
for (i, hl) in applicable.enumerate() {
if !line.span_line_only(hl) && line.span_ends(hl) {
- gutter.push_str(&chars.lbot.style(hl.style).to_string());
- gutter.push_str(
- &chars
- .hbar
- .to_string()
- .repeat(max_gutter.saturating_sub(i) + 2)
- .style(hl.style)
- .to_string(),
- );
+ if render_mode == LabelRenderMode::MultiLineRest {
+ // this is to make multiline labels work. We want to make the right amount
+ // of horizontal space for them, but not actually draw the lines
+ let horizontal_space = max_gutter.saturating_sub(i) + 2;
+ for _ in 0..horizontal_space {
+ gutter.push(' ');
+ }
+ // account for one more horizontal space, since in multiline mode
+ // we also add in the vertical line before the label like this:
+ // 2 │ â•─▶ text
+ // 3 │ ├─▶ here
+ // · ╰──┤ these two lines
+ // · │ are the problem
+ // ^this
+ gutter_cols += horizontal_space + 1;
+ } else {
+ let num_repeat = max_gutter.saturating_sub(i) + 2;
+
+ gutter.push_str(&chars.lbot.style(hl.style).to_string());
+
+ gutter.push_str(
+ &chars
+ .hbar
+ .to_string()
+ .repeat(
+ num_repeat
+ // if we are rendering a multiline label, then leave a bit of space for the
+ // rcross character
+ - if render_mode == LabelRenderMode::MultiLineFirst {
+ 1
+ } else {
+ 0
+ },
+ )
+ .style(hl.style)
+ .to_string(),
+ );
+
+ // we count 1 for the lbot char, and then a few more, the same number
+ // as we just repeated for. For each repeat we only add 1, even though
+ // due to ansi escape codes the number of bytes in the string could grow
+ // a lot each time.
+ gutter_cols += num_repeat + 1;
+ }
break;
} else {
gutter.push_str(&chars.vbar.style(hl.style).to_string());
+
+ // we may push many bytes for the ansi escape codes style adds,
+ // but we still only add a single character-width to the string in a terminal
+ gutter_cols += 1;
}
}
- write!(f, "{:width$}", gutter, width = max_gutter + 1)?;
+
+ // now calculate how many spaces to add based on how many columns we just created.
+ // it's the max width of the gutter, minus how many character-widths we just generated
+ // capped at 0 (though this should never go below in reality), and then we add 3 to
+ // account for arrowheads when a gutter line ends
+ let num_spaces = (max_gutter + 3).saturating_sub(gutter_cols);
+ // we then write the gutter and as many spaces as we need
+ write!(f, "{}{:width$}", gutter, "", width = num_spaces)?;
Ok(())
}
+ fn wrap(&self, text: &str, opts: textwrap::Options<'_>) -> String {
+ if self.wrap_lines {
+ textwrap::fill(text, opts)
+ } else {
+ // Format without wrapping, but retain the indentation options
+ // Implementation based on `textwrap::indent`
+ let mut result = String::with_capacity(2 * text.len());
+ let trimmed_indent = opts.subsequent_indent.trim_end();
+ for (idx, line) in text.split_terminator('\n').enumerate() {
+ if idx > 0 {
+ result.push('\n');
+ }
+ if idx == 0 {
+ if line.trim().is_empty() {
+ result.push_str(opts.initial_indent.trim_end());
+ } else {
+ result.push_str(opts.initial_indent);
+ }
+ } else {
+ if line.trim().is_empty() {
+ result.push_str(trimmed_indent);
+ } else {
+ result.push_str(opts.subsequent_indent);
+ }
+ }
+ result.push_str(line);
+ }
+ if text.ends_with('\n') {
+ // split_terminator will have eaten the final '\n'.
+ result.push('\n');
+ }
+ result
+ }
+ }
+
fn write_linum(&self, f: &mut impl fmt::Write, width: usize, linum: usize) -> fmt::Result {
write!(
f,
@@ -618,13 +906,26 @@
/// Returns an iterator over the visual width of each character in a line.
fn line_visual_char_width<'a>(&self, text: &'a str) -> impl Iterator<Item = usize> + 'a {
let mut column = 0;
+ let mut escaped = false;
let tab_width = self.tab_width;
text.chars().map(move |c| {
- let width = if c == '\t' {
+ let width = match (escaped, c) {
// Round up to the next multiple of tab_width
- tab_width - column % tab_width
- } else {
- c.width().unwrap_or(0)
+ (false, '\t') => tab_width - column % tab_width,
+ // start of ANSI escape
+ (false, '\x1b') => {
+ escaped = true;
+ 0
+ }
+ // use Unicode width for all other characters
+ (false, c) => c.width().unwrap_or(0),
+ // end of ANSI escape
+ (true, 'm') => {
+ escaped = false;
+ 0
+ }
+ // characters are zero width within escape sequence
+ (true, _) => 0,
};
column += width;
width
@@ -632,11 +933,22 @@
}
/// Returns the visual column position of a byte offset on a specific line.
- fn visual_offset(&self, line: &Line, offset: usize) -> usize {
+ ///
+ /// If the offset occurs in the middle of a character, the returned column
+ /// corresponds to that character's first column in `start` is true, or its
+ /// last column if `start` is false.
+ fn visual_offset(&self, line: &Line, offset: usize, start: bool) -> usize {
let line_range = line.offset..=(line.offset + line.length);
assert!(line_range.contains(&offset));
- let text_index = offset - line.offset;
+ let mut text_index = offset - line.offset;
+ while text_index <= line.text.len() && !line.text.is_char_boundary(text_index) {
+ if start {
+ text_index -= 1;
+ } else {
+ text_index += 1;
+ }
+ }
let text = &line.text[..text_index.min(line.text.len())];
let text_width = self.line_visual_char_width(text).sum();
if text_index > line.text.len() {
@@ -659,10 +971,10 @@
for (c, width) in text.chars().zip(self.line_visual_char_width(text)) {
if c == '\t' {
for _ in 0..width {
- f.write_char(' ')?
+ f.write_char(' ')?;
}
} else {
- f.write_char(c)?
+ f.write_char(c)?;
}
}
f.write_char('\n')?;
@@ -687,32 +999,34 @@
.map(|hl| {
let byte_start = hl.offset();
let byte_end = hl.offset() + hl.len();
- let start = self.visual_offset(line, byte_start).max(highest);
- let end = self.visual_offset(line, byte_end).max(start + 1);
+ let start = self.visual_offset(line, byte_start, true).max(highest);
+ let end = if hl.len() == 0 {
+ start + 1
+ } else {
+ self.visual_offset(line, byte_end, false).max(start + 1)
+ };
let vbar_offset = (start + end) / 2;
let num_left = vbar_offset - start;
let num_right = end - vbar_offset - 1;
- if start < end {
- underlines.push_str(
- &format!(
- "{:width$}{}{}{}",
- "",
- chars.underline.to_string().repeat(num_left),
- if hl.len() == 0 {
- chars.uarrow
- } else if hl.label().is_some() {
- chars.underbar
- } else {
- chars.underline
- },
- chars.underline.to_string().repeat(num_right),
- width = start.saturating_sub(highest),
- )
- .style(hl.style)
- .to_string(),
- );
- }
+ underlines.push_str(
+ &format!(
+ "{:width$}{}{}{}",
+ "",
+ chars.underline.to_string().repeat(num_left),
+ if hl.len() == 0 {
+ chars.uarrow
+ } else if hl.label().is_some() {
+ chars.underbar
+ } else {
+ chars.underline
+ },
+ chars.underline.to_string().repeat(num_right),
+ width = start.saturating_sub(highest),
+ )
+ .style(hl.style)
+ .to_string(),
+ );
highest = std::cmp::max(highest, end);
(hl, vbar_offset)
@@ -721,27 +1035,40 @@
writeln!(f, "{}", underlines)?;
for hl in single_liners.iter().rev() {
- if let Some(label) = hl.label() {
- self.write_no_linum(f, linum_width)?;
- self.render_highlight_gutter(f, max_gutter, line, all_highlights)?;
- let mut curr_offset = 1usize;
- for (offset_hl, vbar_offset) in &vbar_offsets {
- while curr_offset < *vbar_offset + 1 {
- write!(f, " ")?;
- curr_offset += 1;
- }
- if *offset_hl != hl {
- write!(f, "{}", chars.vbar.to_string().style(offset_hl.style))?;
- curr_offset += 1;
- } else {
- let lines = format!(
- "{}{} {}",
- chars.lbot,
- chars.hbar.to_string().repeat(2),
- label,
- );
- writeln!(f, "{}", lines.style(hl.style))?;
- break;
+ if let Some(label) = hl.label_parts() {
+ if label.len() == 1 {
+ self.write_label_text(
+ f,
+ line,
+ linum_width,
+ max_gutter,
+ all_highlights,
+ chars,
+ &vbar_offsets,
+ hl,
+ &label[0],
+ LabelRenderMode::SingleLine,
+ )?;
+ } else {
+ let mut first = true;
+ for label_line in &label {
+ self.write_label_text(
+ f,
+ line,
+ linum_width,
+ max_gutter,
+ all_highlights,
+ chars,
+ &vbar_offsets,
+ hl,
+ label_line,
+ if first {
+ LabelRenderMode::MultiLineFirst
+ } else {
+ LabelRenderMode::MultiLineRest
+ },
+ )?;
+ first = false;
}
}
}
@@ -749,13 +1076,80 @@
Ok(())
}
- fn render_multi_line_end(&self, f: &mut impl fmt::Write, hl: &FancySpan) -> fmt::Result {
- writeln!(
+ // I know it's not good practice, but making this a function makes a lot of sense
+ // and making a struct for this does not...
+ #[allow(clippy::too_many_arguments)]
+ fn write_label_text(
+ &self,
+ f: &mut impl fmt::Write,
+ line: &Line,
+ linum_width: usize,
+ max_gutter: usize,
+ all_highlights: &[FancySpan],
+ chars: &ThemeCharacters,
+ vbar_offsets: &[(&&FancySpan, usize)],
+ hl: &&FancySpan,
+ label: &str,
+ render_mode: LabelRenderMode,
+ ) -> fmt::Result {
+ self.write_no_linum(f, linum_width)?;
+ self.render_highlight_gutter(
f,
- "{} {}",
- self.theme.characters.hbar.style(hl.style),
- hl.label().unwrap_or_else(|| "".into()),
+ max_gutter,
+ line,
+ all_highlights,
+ LabelRenderMode::SingleLine,
)?;
+ let mut curr_offset = 1usize;
+ for (offset_hl, vbar_offset) in vbar_offsets {
+ while curr_offset < *vbar_offset + 1 {
+ write!(f, " ")?;
+ curr_offset += 1;
+ }
+ if *offset_hl != hl {
+ write!(f, "{}", chars.vbar.to_string().style(offset_hl.style))?;
+ curr_offset += 1;
+ } else {
+ let lines = match render_mode {
+ LabelRenderMode::SingleLine => format!(
+ "{}{} {}",
+ chars.lbot,
+ chars.hbar.to_string().repeat(2),
+ label,
+ ),
+ LabelRenderMode::MultiLineFirst => {
+ format!("{}{}{} {}", chars.lbot, chars.hbar, chars.rcross, label,)
+ }
+ LabelRenderMode::MultiLineRest => {
+ format!(" {} {}", chars.vbar, label,)
+ }
+ };
+ writeln!(f, "{}", lines.style(hl.style))?;
+ break;
+ }
+ }
+ Ok(())
+ }
+
+ fn render_multi_line_end_single(
+ &self,
+ f: &mut impl fmt::Write,
+ label: &str,
+ style: Style,
+ render_mode: LabelRenderMode,
+ ) -> fmt::Result {
+ match render_mode {
+ LabelRenderMode::SingleLine => {
+ writeln!(f, "{} {}", self.theme.characters.hbar.style(style), label)?;
+ }
+ LabelRenderMode::MultiLineFirst => {
+ writeln!(f, "{} {}", self.theme.characters.rcross.style(style), label)?;
+ }
+ LabelRenderMode::MultiLineRest => {
+ writeln!(f, "{} {}", self.theme.characters.vbar.style(style), label)?;
+ }
+ }
+
Ok(())
}
@@ -834,6 +1228,16 @@
Support types
*/
+#[derive(PartialEq, Debug)]
+enum LabelRenderMode {
+ /// we're rendering a single line label (or not rendering in any special way)
+ SingleLine,
+ /// we're rendering a multiline label
+ MultiLineFirst,
+ /// we're rendering the rest of a multiline label
+ MultiLineRest,
+}
+
#[derive(Debug)]
struct Line {
line_number: usize,
@@ -847,14 +1251,31 @@
span.offset() >= self.offset && span.offset() + span.len() <= self.offset + self.length
}
+ /// Returns whether `span` should be visible on this line, either in the gutter or under the
+ /// text on this line
fn span_applies(&self, span: &FancySpan) -> bool {
let spanlen = if span.len() == 0 { 1 } else { span.len() };
// Span starts in this line
+
(span.offset() >= self.offset && span.offset() < self.offset + self.length)
- // Span passes through this line
- || (span.offset() < self.offset && span.offset() + spanlen > self.offset + self.length) //todo
- // Span ends on this line
- || (span.offset() + spanlen > self.offset && span.offset() + spanlen <= self.offset + self.length)
+ // Span passes through this line
+ || (span.offset() < self.offset && span.offset() + spanlen > self.offset + self.length) //todo
+ // Span ends on this line
+ || (span.offset() + spanlen > self.offset && span.offset() + spanlen <= self.offset + self.length)
+ }
+
+ /// Returns whether `span` should be visible on this line in the gutter (so this excludes spans
+ /// that are only visible on this line and do not span multiple lines)
+ fn span_applies_gutter(&self, span: &FancySpan) -> bool {
+ let spanlen = if span.len() == 0 { 1 } else { span.len() };
+ // Span starts in this line
+ self.span_applies(span)
+ && !(
+ // as long as it doesn't start *and* end on this line
+ (span.offset() >= self.offset && span.offset() < self.offset + self.length)
+ && (span.offset() + spanlen > self.offset
+ && span.offset() + spanlen <= self.offset + self.length)
+ )
}
// A 'flyby' is a multi-line span that technically covers this line, but
@@ -884,7 +1305,10 @@
#[derive(Debug, Clone)]
struct FancySpan {
- label: Option<String>,
+ /// this is deliberately an option of a vec because I wanted to be very explicit
+ /// that there can also be *no* label. If there is a label, it can have multiple
+ /// lines which is what the vec is for.
+ label: Option<Vec<String>>,
span: SourceSpan,
style: Style,
}
@@ -895,9 +1319,17 @@
}
}
+fn split_label(v: String) -> Vec<String> {
+ v.split('\n').map(|i| i.to_string()).collect()
+}
+
impl FancySpan {
fn new(label: Option<String>, span: SourceSpan, style: Style) -> Self {
- FancySpan { label, span, style }
+ FancySpan {
+ label: label.map(split_label),
+ span,
+ style,
+ }
}
fn style(&self) -> Style {
@@ -907,7 +1339,15 @@
fn label(&self) -> Option<String> {
self.label
.as_ref()
- .map(|l| l.style(self.style()).to_string())
+ .map(|l| l.join("\n").style(self.style()).to_string())
+ }
+
+ fn label_parts(&self) -> Option<Vec<String>> {
+ self.label.as_ref().map(|l| {
+ l.iter()
+ .map(|i| i.style(self.style()).to_string())
+ .collect()
+ })
}
fn offset(&self) -> usize {
diff --git a/crates/miette/src/handlers/json.rs b/crates/miette/src/handlers/json.rs
index 29e21a0..0b4a405 100644
--- a/crates/miette/src/handlers/json.rs
+++ b/crates/miette/src/handlers/json.rs
@@ -96,7 +96,7 @@
}
write!(f, r#""{}""#, escape(&error.to_string()))?;
}
- write!(f, "],")?
+ write!(f, "],")?;
} else {
write!(f, r#""causes": [],"#)?;
}
diff --git a/crates/miette/src/handlers/theme.rs b/crates/miette/src/handlers/theme.rs
index 1f5236a..892ffc2 100644
--- a/crates/miette/src/handlers/theme.rs
+++ b/crates/miette/src/handlers/theme.rs
@@ -1,4 +1,5 @@
-use is_terminal::IsTerminal;
+use std::io::IsTerminal;
+
use owo_colors::Style;
/**
@@ -55,9 +56,9 @@
/// A "basic" graphical theme that skips colors and unicode characters and
/// just does monochrome ascii art. If you want a completely non-graphical
- /// rendering of your `Diagnostic`s, check out
- /// [crate::NarratableReportHandler], or write your own
- /// [crate::ReportHandler]!
+ /// rendering of your [`Diagnostic`](crate::Diagnostic)s, check out
+ /// [`NarratableReportHandler`](crate::NarratableReportHandler), or write
+ /// your own [`ReportHandler`](crate::ReportHandler)
pub fn none() -> Self {
Self {
characters: ThemeCharacters::ascii(),
@@ -79,7 +80,8 @@
}
/**
-Styles for various parts of graphical rendering for the [crate::GraphicalReportHandler].
+Styles for various parts of graphical rendering for the
+[`GraphicalReportHandler`](crate::GraphicalReportHandler).
*/
#[derive(Debug, Clone)]
pub struct ThemeStyles {
@@ -159,7 +161,7 @@
// https://github.com/zesterer/ariadne/blob/e3cb394cb56ecda116a0a1caecd385a49e7f6662/src/draw.rs
/// Characters to be used when drawing when using
-/// [crate::GraphicalReportHandler].
+/// [`GraphicalReportHandler`](crate::GraphicalReportHandler).
#[allow(missing_docs)]
#[derive(Debug, Clone, Eq, PartialEq)]
pub struct ThemeCharacters {
diff --git a/crates/miette/src/highlighters/blank.rs b/crates/miette/src/highlighters/blank.rs
new file mode 100644
index 0000000..50a9c65
--- /dev/null
+++ b/crates/miette/src/highlighters/blank.rs
@@ -0,0 +1,36 @@
+use owo_colors::Style;
+
+use crate::SpanContents;
+
+use super::{Highlighter, HighlighterState};
+
+/// The default syntax highlighter. It applies `Style::default()` to input text.
+/// This is used by default when no syntax highlighting features are enabled.
+#[derive(Debug, Clone)]
+pub struct BlankHighlighter;
+
+impl Highlighter for BlankHighlighter {
+ fn start_highlighter_state<'h>(
+ &'h self,
+ _source: &dyn SpanContents<'_>,
+ ) -> Box<dyn super::HighlighterState + 'h> {
+ Box::new(BlankHighlighterState)
+ }
+}
+
+impl Default for BlankHighlighter {
+ fn default() -> Self {
+ BlankHighlighter
+ }
+}
+
+/// The default highlighter state. It applies `Style::default()` to input text.
+/// This is used by default when no syntax highlighting features are enabled.
+#[derive(Debug, Clone)]
+pub struct BlankHighlighterState;
+
+impl HighlighterState for BlankHighlighterState {
+ fn highlight_line<'s>(&mut self, line: &'s str) -> Vec<owo_colors::Styled<&'s str>> {
+ vec![Style::default().style(line)]
+ }
+}
diff --git a/crates/miette/src/highlighters/mod.rs b/crates/miette/src/highlighters/mod.rs
new file mode 100644
index 0000000..d605c1c
--- /dev/null
+++ b/crates/miette/src/highlighters/mod.rs
@@ -0,0 +1,116 @@
+//! This module provides a trait for creating custom syntax highlighters that
+//! highlight [`Diagnostic`](crate::Diagnostic) source code with ANSI escape
+//! sequences when rendering with the [`GraphicalReportHighlighter`](crate::GraphicalReportHandler).
+//!
+//! It also provides built-in highlighter implementations that you can use out of the box.
+//! By default, there are no syntax highlighters exported by miette
+//! (except for the no-op [`BlankHighlighter`]).
+//! To enable support for specific highlighters, you should enable their associated feature flag.
+//!
+//! Currently supported syntax highlighters and their feature flags:
+//! * `syntect-highlighter` - Enables [`syntect`](https://docs.rs/syntect/latest/syntect/) syntax highlighting support via the [`SyntectHighlighter`]
+//!
+
+use std::{ops::Deref, sync::Arc};
+
+use crate::SpanContents;
+use owo_colors::Styled;
+
+#[cfg(feature = "syntect-highlighter")]
+pub use self::syntect::*;
+pub use blank::*;
+
+mod blank;
+#[cfg(feature = "syntect-highlighter")]
+mod syntect;
+
+/// A syntax highlighter for highlighting miette [`SourceCode`](crate::SourceCode) snippets.
+pub trait Highlighter {
+ /// Creates a new [HighlighterState] to begin parsing and highlighting
+ /// a [SpanContents].
+ ///
+ /// The [GraphicalReportHandler](crate::GraphicalReportHandler) will call
+ /// this method at the start of rendering a [SpanContents].
+ ///
+ /// The [SpanContents] is provided as input only so that the [Highlighter]
+ /// can detect language syntax and make other initialization decisions prior
+ /// to highlighting, but it is not intended that the Highlighter begin
+ /// highlighting at this point. The returned [HighlighterState] is
+ /// responsible for the actual rendering.
+ fn start_highlighter_state<'h>(
+ &'h self,
+ source: &dyn SpanContents<'_>,
+ ) -> Box<dyn HighlighterState + 'h>;
+}
+
+/// A stateful highlighter that incrementally highlights lines of a particular
+/// source code.
+///
+/// The [GraphicalReportHandler](crate::GraphicalReportHandler)
+/// will create a highlighter state by calling
+/// [start_highlighter_state](Highlighter::start_highlighter_state) at the
+/// start of rendering, then it will iteratively call
+/// [highlight_line](HighlighterState::highlight_line) to render individual
+/// highlighted lines. This allows [Highlighter] implementations to maintain
+/// mutable parsing and highlighting state.
+pub trait HighlighterState {
+ /// Highlight an individual line from the source code by returning a vector of [Styled]
+ /// regions.
+ fn highlight_line<'s>(&mut self, line: &'s str) -> Vec<Styled<&'s str>>;
+}
+
+/// Arcified trait object for Highlighter. Used internally by [GraphicalReportHandler]
+///
+/// Wrapping the trait object in this way allows us to implement Debug and Clone.
+#[derive(Clone)]
+#[repr(transparent)]
+pub(crate) struct MietteHighlighter(Arc<dyn Highlighter + Send + Sync>);
+
+impl MietteHighlighter {
+ pub(crate) fn nocolor() -> Self {
+ Self::from(BlankHighlighter)
+ }
+
+ #[cfg(feature = "syntect-highlighter")]
+ pub(crate) fn syntect_truecolor() -> Self {
+ Self::from(SyntectHighlighter::default())
+ }
+}
+
+impl Default for MietteHighlighter {
+ #[cfg(feature = "syntect-highlighter")]
+ fn default() -> Self {
+ use std::io::IsTerminal;
+ match std::env::var("NO_COLOR") {
+ _ if !std::io::stdout().is_terminal() || !std::io::stderr().is_terminal() => {
+ //TODO: should use ANSI styling instead of 24-bit truecolor here
+ Self(Arc::new(SyntectHighlighter::default()))
+ }
+ Ok(string) if string != "0" => MietteHighlighter::nocolor(),
+ _ => Self(Arc::new(SyntectHighlighter::default())),
+ }
+ }
+ #[cfg(not(feature = "syntect-highlighter"))]
+ fn default() -> Self {
+ return MietteHighlighter::nocolor();
+ }
+}
+
+impl<T: Highlighter + Send + Sync + 'static> From<T> for MietteHighlighter {
+ fn from(value: T) -> Self {
+ Self(Arc::new(value))
+ }
+}
+
+impl std::fmt::Debug for MietteHighlighter {
+ fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
+ write!(f, "MietteHighlighter(...)")
+ }
+}
+
+impl Deref for MietteHighlighter {
+ type Target = dyn Highlighter + Send + Sync;
+ fn deref(&self) -> &Self::Target {
+ &*self.0
+ }
+}
diff --git a/crates/miette/src/highlighters/syntect.rs b/crates/miette/src/highlighters/syntect.rs
new file mode 100644
index 0000000..57ebadf
--- /dev/null
+++ b/crates/miette/src/highlighters/syntect.rs
@@ -0,0 +1,170 @@
+use std::path::Path;
+
+// all syntect imports are explicitly qualified, but their paths are shortened for convenience
+mod syntect {
+ pub(super) use syntect::{
+ highlighting::{
+ Color, HighlightIterator, HighlightState, Highlighter, Style, Theme, ThemeSet,
+ },
+ parsing::{ParseState, ScopeStack, SyntaxReference, SyntaxSet},
+ };
+}
+
+use owo_colors::{Rgb, Style, Styled};
+
+use crate::{
+ highlighters::{Highlighter, HighlighterState},
+ SpanContents,
+};
+
+use super::BlankHighlighterState;
+
+/// Highlights miette [SourceCode] with the [syntect](https://docs.rs/syntect/latest/syntect/) highlighting crate.
+///
+/// Currently only 24-bit truecolor output is supported due to syntect themes
+/// representing color as RGBA.
+#[derive(Debug, Clone)]
+pub struct SyntectHighlighter {
+ theme: syntect::Theme,
+ syntax_set: syntect::SyntaxSet,
+ use_bg_color: bool,
+}
+
+impl Default for SyntectHighlighter {
+ fn default() -> Self {
+ let theme_set = syntect::ThemeSet::load_defaults();
+ let theme = theme_set.themes["base16-ocean.dark"].clone();
+ Self::new_themed(theme, false)
+ }
+}
+
+impl Highlighter for SyntectHighlighter {
+ fn start_highlighter_state<'h>(
+ &'h self,
+ source: &dyn SpanContents<'_>,
+ ) -> Box<dyn HighlighterState + 'h> {
+ if let Some(syntax) = self.detect_syntax(source) {
+ let highlighter = syntect::Highlighter::new(&self.theme);
+ let parse_state = syntect::ParseState::new(syntax);
+ let highlight_state =
+ syntect::HighlightState::new(&highlighter, syntect::ScopeStack::new());
+ Box::new(SyntectHighlighterState {
+ syntax_set: &self.syntax_set,
+ highlighter,
+ parse_state,
+ highlight_state,
+ use_bg_color: self.use_bg_color,
+ })
+ } else {
+ Box::new(BlankHighlighterState)
+ }
+ }
+}
+
+impl SyntectHighlighter {
+ /// Create a syntect highlighter with the given theme and syntax set.
+ pub fn new(syntax_set: syntect::SyntaxSet, theme: syntect::Theme, use_bg_color: bool) -> Self {
+ Self {
+ theme,
+ syntax_set,
+ use_bg_color,
+ }
+ }
+
+ /// Create a syntect highlighter with the given theme and the default syntax set.
+ pub fn new_themed(theme: syntect::Theme, use_bg_color: bool) -> Self {
+ Self::new(
+ syntect::SyntaxSet::load_defaults_nonewlines(),
+ theme,
+ use_bg_color,
+ )
+ }
+
+ /// Determine syntect SyntaxReference to use for given SourceCode
+ fn detect_syntax(&self, contents: &dyn SpanContents<'_>) -> Option<&syntect::SyntaxReference> {
+ // use language if given
+ if let Some(language) = contents.language() {
+ return self.syntax_set.find_syntax_by_name(language);
+ }
+ // otherwise try to use any file extension provided in the name
+ if let Some(name) = contents.name() {
+ if let Some(ext) = Path::new(name).extension() {
+ return self
+ .syntax_set
+ .find_syntax_by_extension(ext.to_string_lossy().as_ref());
+ }
+ }
+ // finally, attempt to guess syntax based on first line
+ return self.syntax_set.find_syntax_by_first_line(
+ &std::str::from_utf8(contents.data())
+ .ok()?
+ .split('\n')
+ .next()?,
+ );
+ }
+}
+
+/// Stateful highlighting iterator for [SyntectHighlighter]
+#[derive(Debug)]
+pub(crate) struct SyntectHighlighterState<'h> {
+ syntax_set: &'h syntect::SyntaxSet,
+ highlighter: syntect::Highlighter<'h>,
+ parse_state: syntect::ParseState,
+ highlight_state: syntect::HighlightState,
+ use_bg_color: bool,
+}
+
+impl<'h> HighlighterState for SyntectHighlighterState<'h> {
+ fn highlight_line<'s>(&mut self, line: &'s str) -> Vec<Styled<&'s str>> {
+ if let Ok(ops) = self.parse_state.parse_line(line, &self.syntax_set) {
+ let use_bg_color = self.use_bg_color;
+ syntect::HighlightIterator::new(
+ &mut self.highlight_state,
+ &ops,
+ line,
+ &mut self.highlighter,
+ )
+ .map(|(style, str)| (convert_style(style, use_bg_color).style(str)))
+ .collect()
+ } else {
+ vec![Style::default().style(line)]
+ }
+ }
+}
+
+/// Convert syntect [syntect::Style] into owo_colors [Style] */
+#[inline]
+fn convert_style(syntect_style: syntect::Style, use_bg_color: bool) -> Style {
+ if use_bg_color {
+ let fg = blend_fg_color(syntect_style);
+ let bg = convert_color(syntect_style.background);
+ Style::new().color(fg).on_color(bg)
+ } else {
+ let fg = convert_color(syntect_style.foreground);
+ Style::new().color(fg)
+ }
+}
+
+/// Blend foreground RGB into background RGB according to alpha channel
+#[inline]
+fn blend_fg_color(syntect_style: syntect::Style) -> Rgb {
+ let fg = syntect_style.foreground;
+ if fg.a == 0xff {
+ return convert_color(fg);
+ }
+ let bg = syntect_style.background;
+ let ratio = fg.a as u32;
+ let r = (fg.r as u32 * ratio + bg.r as u32 * (255 - ratio)) / 255;
+ let g = (fg.g as u32 * ratio + bg.g as u32 * (255 - ratio)) / 255;
+ let b = (fg.b as u32 * ratio + bg.b as u32 * (255 - ratio)) / 255;
+ Rgb(r as u8, g as u8, b as u8)
+}
+
+/// Convert syntect color into owo color.
+///
+/// Note: ignores alpha channel. use [`blend_fg_color`] if you need that
+///
+#[inline]
+fn convert_color(color: syntect::Color) -> Rgb {
+ Rgb(color.r, color.g, color.b)
+}
diff --git a/crates/miette/src/lib.rs b/crates/miette/src/lib.rs
index 20589ef..7674f2b 100644
--- a/crates/miette/src/lib.rs
+++ b/crates/miette/src/lib.rs
@@ -47,6 +47,7 @@
//! - [... delayed source code](#-delayed-source-code)
//! - [... handler options](#-handler-options)
//! - [... dynamic diagnostics](#-dynamic-diagnostics)
+//! - [... syntax highlighting](#-syntax-highlighting)
//! - [Acknowledgements](#acknowledgements)
//! - [License](#license)
//!
@@ -109,7 +110,7 @@
//! // The Source that we're gonna be printing snippets out of.
//! // This can be a String if you don't have or care about file names.
//! #[source_code]
-//! src: NamedSource,
+//! src: NamedSource<String>,
//! // Snippets and highlights can be included in the diagnostic!
//! #[label("This bit here")]
//! bad_bit: SourceSpan,
@@ -304,6 +305,23 @@
//! miette = { version = "X.Y.Z", features = ["fancy"] }
//! ```
//!
+//! Another way to display a diagnostic is by printing them using the debug formatter.
+//! This is, in fact, what returning diagnostics from main ends up doing.
+//! To do it yourself, you can write the following:
+//!
+//! ```rust
+//! use miette::{IntoDiagnostic, Result};
+//! use semver::Version;
+//!
+//! fn just_a_random_function() {
+//! let version_result: Result<Version> = "1.2.x".parse().into_diagnostic();
+//! match version_result {
+//! Err(e) => println!("{:?}", e),
+//! Ok(version) => println!("{}", version),
+//! }
+//! }
+//! ```
+//!
//! ### ... diagnostic code URLs
//!
//! `miette` supports providing a URL for individual diagnostics. This URL will
@@ -593,6 +611,7 @@
//! .unicode(false)
//! .context_lines(3)
//! .tab_width(4)
+//! .break_words(true)
//! .build(),
//! )
//! }))
@@ -625,6 +644,38 @@
//! println!("{:?}", report)
//! ```
//!
+//! ### ... syntax highlighting
+//!
+//! `miette` can be configured to highlight syntax in source code snippets.
+//!
+//! <!-- TODO: screenshot goes here once default Theme is decided -->
+//!
+//! To use the built-in highlighting functionality, you must enable the
+//! `syntect-highlighter` crate feature. When this feature is enabled, `miette` will
+//! automatically use the [`syntect`] crate to highlight the `#[source_code]`
+//! field of your [`Diagnostic`].
+//!
+//! Syntax detection with [`syntect`] is handled by checking 2 methods on the [`SpanContents`] trait, in order:
+//! * [language()](SpanContents::language) - Provides the name of the language
+//! as a string. For example `"Rust"` will indicate Rust syntax highlighting.
+//! You can set the language of the [`SpanContents`] produced by a
+//! [`NamedSource`] via the [`with_language`](NamedSource::with_language)
+//! method.
+//! * [name()](SpanContents::name) - In the absence of an explicitly set
+//! language, the name is assumed to contain a file name or file path.
+//! The highlighter will check for a file extension at the end of the name and
+//! try to guess the syntax from that.
+//!
+//! If you want to use a custom highlighter, you can provide a custom
+//! implementation of the [`Highlighter`](highlighters::Highlighter)
+//! trait to [`MietteHandlerOpts`] by calling the
+//! [`with_syntax_highlighting`](MietteHandlerOpts::with_syntax_highlighting)
+//! method. See the [`highlighters`] module docs for more details.
+//!
+//! ## MSRV
+//!
+//! This crate requires rustc 1.70.0 or later.
+//!
//! ## Acknowledgements
//!
//! `miette` was not developed in a void. It owes enormous credit to various
@@ -652,6 +703,7 @@
//! and some from [`thiserror`](https://github.com/dtolnay/thiserror), also
//! under the Apache License. Some code is taken from
//! [`ariadne`](https://github.com/zesterer/ariadne), which is MIT licensed.
+#[cfg(feature = "derive")]
pub use miette_derive::*;
pub use error::*;
@@ -672,6 +724,8 @@
#[cfg(feature = "fancy-no-backtrace")]
mod handler;
mod handlers;
+#[cfg(feature = "fancy-no-backtrace")]
+pub mod highlighters;
#[doc(hidden)]
pub mod macro_helpers;
mod miette_diagnostic;
diff --git a/crates/miette/src/miette_diagnostic.rs b/crates/miette/src/miette_diagnostic.rs
index 67b75d0..9863e88 100644
--- a/crates/miette/src/miette_diagnostic.rs
+++ b/crates/miette/src/miette_diagnostic.rs
@@ -252,7 +252,7 @@
/// ```
pub fn and_labels(mut self, labels: impl IntoIterator<Item = LabeledSpan>) -> Self {
let mut all_labels = self.labels.unwrap_or_default();
- all_labels.extend(labels.into_iter());
+ all_labels.extend(labels);
self.labels = Some(all_labels);
self
}
@@ -292,14 +292,16 @@
"offset": 0,
"length": 0
},
- "label": "label1"
+ "label": "label1",
+ "primary": false
},
{
"span": {
"offset": 1,
"length": 2
},
- "label": "label2"
+ "label": "label2",
+ "primary": false
}
]
});
@@ -350,14 +352,16 @@
"offset": 0,
"length": 0
},
- "label": "label1"
+ "label": "label1",
+ "primary": false
},
{
"span": {
"offset": 1,
"length": 2
},
- "label": "label2"
+ "label": "label2",
+ "primary": false
}
]
});
diff --git a/crates/miette/src/named_source.rs b/crates/miette/src/named_source.rs
index 31ad1d1..99e74a8 100644
--- a/crates/miette/src/named_source.rs
+++ b/crates/miette/src/named_source.rs
@@ -3,27 +3,33 @@
/// Utility struct for when you have a regular [`SourceCode`] type that doesn't
/// implement `name`. For example [`String`]. Or if you want to override the
/// `name` returned by the `SourceCode`.
-pub struct NamedSource {
- source: Box<dyn SourceCode + 'static>,
+pub struct NamedSource<S: SourceCode + 'static> {
+ source: S,
name: String,
+ language: Option<String>,
}
-impl std::fmt::Debug for NamedSource {
+impl<S: SourceCode> std::fmt::Debug for NamedSource<S> {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
f.debug_struct("NamedSource")
.field("name", &self.name)
- .field("source", &"<redacted>");
+ .field("source", &"<redacted>")
+ .field("language", &self.language);
Ok(())
}
}
-impl NamedSource {
+impl<S: SourceCode + 'static> NamedSource<S> {
/// Create a new `NamedSource` using a regular [`SourceCode`] and giving
/// its returned [`SpanContents`] a name.
- pub fn new(name: impl AsRef<str>, source: impl SourceCode + Send + Sync + 'static) -> Self {
+ pub fn new(name: impl AsRef<str>, source: S) -> Self
+ where
+ S: Send + Sync,
+ {
Self {
- source: Box::new(source),
+ source,
name: name.as_ref().to_string(),
+ language: None,
}
}
@@ -34,28 +40,38 @@
/// Returns a reference the inner [`SourceCode`] type for this
/// `NamedSource`.
- pub fn inner(&self) -> &(dyn SourceCode + 'static) {
- &*self.source
+ pub fn inner(&self) -> &S {
+ &self.source
+ }
+
+ /// Sets the [`language`](SpanContents::language) for this source code.
+ pub fn with_language(mut self, language: impl Into<String>) -> Self {
+ self.language = Some(language.into());
+ self
}
}
-impl SourceCode for NamedSource {
+impl<S: SourceCode + 'static> SourceCode for NamedSource<S> {
fn read_span<'a>(
&'a self,
span: &crate::SourceSpan,
context_lines_before: usize,
context_lines_after: usize,
) -> Result<Box<dyn SpanContents<'a> + 'a>, MietteError> {
- let contents = self
- .inner()
- .read_span(span, context_lines_before, context_lines_after)?;
- Ok(Box::new(MietteSpanContents::new_named(
+ let inner_contents =
+ self.inner()
+ .read_span(span, context_lines_before, context_lines_after)?;
+ let mut contents = MietteSpanContents::new_named(
self.name.clone(),
- contents.data(),
- *contents.span(),
- contents.line(),
- contents.column(),
- contents.line_count(),
- )))
+ inner_contents.data(),
+ *inner_contents.span(),
+ inner_contents.line(),
+ inner_contents.column(),
+ inner_contents.line_count(),
+ );
+ if let Some(language) = &self.language {
+ contents = contents.with_language(language);
+ }
+ Ok(Box::new(contents))
}
}
diff --git a/crates/miette/src/protocol.rs b/crates/miette/src/protocol.rs
index f516984..6b98a97 100644
--- a/crates/miette/src/protocol.rs
+++ b/crates/miette/src/protocol.rs
@@ -179,6 +179,7 @@
*/
#[derive(Copy, Clone, Debug, Eq, Ord, PartialEq, PartialOrd)]
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
+#[derive(Default)]
pub enum Severity {
/// Just some help. Here's how you could be doing it better.
Advice,
@@ -186,15 +187,10 @@
Warning,
/// Critical failure. The program cannot continue.
/// This is the default severity, if you don't specify another one.
+ #[default]
Error,
}
-impl Default for Severity {
- fn default() -> Self {
- Severity::Error
- }
-}
-
#[cfg(feature = "serde")]
#[test]
fn test_serialize_severity() {
@@ -232,7 +228,7 @@
gigabytes or larger in size.
*/
pub trait SourceCode: Send + Sync {
- /// Read the bytes for a specific span from this SourceCode, keeping a
+ /// Read the bytes for a specific span from this `SourceCode`, keeping a
/// certain number of lines before and after the span as context.
fn read_span<'a>(
&'a self,
@@ -249,6 +245,7 @@
#[cfg_attr(feature = "serde", serde(skip_serializing_if = "Option::is_none"))]
label: Option<String>,
span: SourceSpan,
+ primary: bool,
}
impl LabeledSpan {
@@ -256,7 +253,8 @@
pub const fn new(label: Option<String>, offset: ByteOffset, len: usize) -> Self {
Self {
label,
- span: SourceSpan::new(SourceOffset(offset), SourceOffset(len)),
+ span: SourceSpan::new(SourceOffset(offset), len),
+ primary: false,
}
}
@@ -265,6 +263,16 @@
Self {
label,
span: span.into(),
+ primary: false,
+ }
+ }
+
+ /// Makes a new labeled primary span using an existing span.
+ pub fn new_primary_with_span(label: Option<String>, span: impl Into<SourceSpan>) -> Self {
+ Self {
+ label,
+ span: span.into(),
+ primary: true,
}
}
@@ -340,6 +348,11 @@
pub const fn is_empty(&self) -> bool {
self.span.is_empty()
}
+
+ /// True if this `LabeledSpan` is a primary span.
+ pub const fn primary(&self) -> bool {
+ self.primary
+ }
}
#[cfg(feature = "serde")]
@@ -350,7 +363,8 @@
assert_eq!(
json!(LabeledSpan::new(None, 0, 0)),
json!({
- "span": { "offset": 0, "length": 0 }
+ "span": { "offset": 0, "length": 0, },
+ "primary": false,
})
);
@@ -358,9 +372,10 @@
json!(LabeledSpan::new(Some("label".to_string()), 0, 0)),
json!({
"label": "label",
- "span": { "offset": 0, "length": 0 }
+ "span": { "offset": 0, "length": 0, },
+ "primary": false,
})
- )
+ );
}
#[cfg(feature = "serde")]
@@ -370,23 +385,26 @@
let span: LabeledSpan = serde_json::from_value(json!({
"label": null,
- "span": { "offset": 0, "length": 0 }
+ "span": { "offset": 0, "length": 0, },
+ "primary": false,
}))
.unwrap();
assert_eq!(span, LabeledSpan::new(None, 0, 0));
let span: LabeledSpan = serde_json::from_value(json!({
- "span": { "offset": 0, "length": 0 }
+ "span": { "offset": 0, "length": 0, },
+ "primary": false
}))
.unwrap();
assert_eq!(span, LabeledSpan::new(None, 0, 0));
let span: LabeledSpan = serde_json::from_value(json!({
"label": "label",
- "span": { "offset": 0, "length": 0 }
+ "span": { "offset": 0, "length": 0, },
+ "primary": false
}))
.unwrap();
- assert_eq!(span, LabeledSpan::new(Some("label".to_string()), 0, 0))
+ assert_eq!(span, LabeledSpan::new(Some("label".to_string()), 0, 0));
}
/**
@@ -411,6 +429,15 @@
fn column(&self) -> usize;
/// Total number of lines covered by this `SpanContents`.
fn line_count(&self) -> usize;
+
+ /// Optional method. The language name for this source code, if any.
+ /// This is used to drive syntax highlighting.
+ ///
+ /// Examples: Rust, TOML, C
+ ///
+ fn language(&self) -> Option<&str> {
+ None
+ }
}
/**
@@ -430,6 +457,8 @@
line_count: usize,
// Optional filename
name: Option<String>,
+ // Optional language
+ language: Option<String>,
}
impl<'a> MietteSpanContents<'a> {
@@ -448,6 +477,7 @@
column,
line_count,
name: None,
+ language: None,
}
}
@@ -467,8 +497,15 @@
column,
line_count,
name: Some(name),
+ language: None,
}
}
+
+ /// Sets the [`language`](SourceCode::language) for syntax highlighting.
+ pub fn with_language(mut self, language: impl Into<String>) -> Self {
+ self.language = Some(language.into());
+ self
+ }
}
impl<'a> SpanContents<'a> for MietteSpanContents<'a> {
@@ -490,6 +527,9 @@
fn name(&self) -> Option<&str> {
self.name.as_deref()
}
+ fn language(&self) -> Option<&str> {
+ self.language.as_deref()
+ }
}
/// Span within a [`SourceCode`]
@@ -504,10 +544,10 @@
impl SourceSpan {
/// Create a new [`SourceSpan`].
- pub const fn new(start: SourceOffset, length: SourceOffset) -> Self {
+ pub const fn new(start: SourceOffset, length: usize) -> Self {
Self {
offset: start,
- length: length.offset(),
+ length,
}
}
@@ -537,8 +577,8 @@
}
}
-impl From<(SourceOffset, SourceOffset)> for SourceSpan {
- fn from((start, len): (SourceOffset, SourceOffset)) -> Self {
+impl From<(SourceOffset, usize)> for SourceSpan {
+ fn from((start, len): (SourceOffset, usize)) -> Self {
Self::new(start, len)
}
}
@@ -575,7 +615,7 @@
assert_eq!(
json!(SourceSpan::from(0)),
json!({ "offset": 0, "length": 0})
- )
+ );
}
#[cfg(feature = "serde")]
@@ -584,7 +624,7 @@
use serde_json::json;
let span: SourceSpan = serde_json::from_value(json!({ "offset": 0, "length": 0})).unwrap();
- assert_eq!(span, SourceSpan::from(0))
+ assert_eq!(span, SourceSpan::from(0));
}
/**
@@ -686,12 +726,12 @@
fn test_serialize_source_offset() {
use serde_json::json;
- assert_eq!(json!(SourceOffset::from(0)), 0)
+ assert_eq!(json!(SourceOffset::from(0)), 0);
}
#[cfg(feature = "serde")]
#[test]
fn test_deserialize_source_offset() {
let offset: SourceOffset = serde_json::from_str("0").unwrap();
- assert_eq!(offset, SourceOffset::from(0))
+ assert_eq!(offset, SourceOffset::from(0));
}
diff --git a/pseudo_crate/Cargo.lock b/pseudo_crate/Cargo.lock
index 6864911..ea1f5ea 100644
--- a/pseudo_crate/Cargo.lock
+++ b/pseudo_crate/Cargo.lock
@@ -3877,21 +3877,20 @@
[[package]]
name = "miette"
-version = "5.10.0"
+version = "6.0.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "59bb584eaeeab6bd0226ccf3509a69d7936d148cf3d036ad350abe35e8c6856e"
+checksum = "337e1043bbc086dac9d9674983bef52ac991ce150e09b5b8e35c5a73dd83f66c"
dependencies = [
"miette-derive",
- "once_cell",
"thiserror 1.0.49",
"unicode-width 0.1.11",
]
[[package]]
name = "miette-derive"
-version = "5.10.0"
+version = "6.0.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "49e7bc1560b95a3c4a25d03de42fe76ca718ab92d1a22a55b9b4cf67b3ae635c"
+checksum = "71e622f2a0dd84cbca79bc6c3c33f4fd7dc69faf992216516aacc1d136102800"
dependencies = [
"proc-macro2 1.0.93",
"quote 1.0.38",
diff --git a/pseudo_crate/Cargo.toml b/pseudo_crate/Cargo.toml
index 0faa577..d9551a0 100644
--- a/pseudo_crate/Cargo.toml
+++ b/pseudo_crate/Cargo.toml
@@ -214,8 +214,8 @@
memoffset = "=0.9.1"
merge = "=0.1.0"
merge_derive = "=0.1.0"
-miette = "=5.10.0"
-miette-derive = "=5.10.0"
+miette = "=6.0.1"
+miette-derive = "=6.0.1"
mime = "=0.3.17"
minimal-lexical = "=0.2.1"
mio = "=1.0.3"