vendor pretty-print dep
This commit is contained in:
parent
4a6c1020f4
commit
52a80cfb0e
40 changed files with 2766 additions and 6 deletions
35
Cargo.lock
generated
35
Cargo.lock
generated
|
|
@ -73,6 +73,12 @@ version = "1.0.102"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "7f202df86484c868dbad7eaa557ef785d5c66295e41b460ef922eca0723b842c"
|
checksum = "7f202df86484c868dbad7eaa557ef785d5c66295e41b460ef922eca0723b842c"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "arrayvec"
|
||||||
|
version = "0.5.2"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "23b62fc65de8e4e7f52534fb52b0f3ed04746ae267519eef2a83941e8085068b"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "atomic"
|
name = "atomic"
|
||||||
version = "0.6.1"
|
version = "0.6.1"
|
||||||
|
|
@ -1170,15 +1176,32 @@ dependencies = [
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "pretty-print"
|
name = "pretty"
|
||||||
version = "0.1.9"
|
version = "0.12.5"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "c5ad516586f2191e7ce412b9b164e61ee6403638aa70e98672b978c1f448e63f"
|
checksum = "0d22152487193190344590e4f30e219cf3fe140d9e7a3fdb683d82aa2c5f4156"
|
||||||
|
dependencies = [
|
||||||
|
"arrayvec",
|
||||||
|
"typed-arena",
|
||||||
|
"unicode-width",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "pretty-print"
|
||||||
|
version = "0.1.8"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"color-ansi",
|
"color-ansi",
|
||||||
"unicode-segmentation",
|
"unicode-segmentation",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "pretty-test"
|
||||||
|
version = "0.0.0"
|
||||||
|
dependencies = [
|
||||||
|
"pretty",
|
||||||
|
"pretty-print",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "prettyplease"
|
name = "prettyplease"
|
||||||
version = "0.2.37"
|
version = "0.2.37"
|
||||||
|
|
@ -1898,6 +1921,12 @@ dependencies = [
|
||||||
"ratatui-widgets",
|
"ratatui-widgets",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "typed-arena"
|
||||||
|
version = "2.0.2"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "6af6ae20167a9ece4bcb41af5b80f8a1f1df981f6391189ce00fd257af04126a"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "typenum"
|
name = "typenum"
|
||||||
version = "1.19.0"
|
version = "1.19.0"
|
||||||
|
|
|
||||||
|
|
@ -8,8 +8,8 @@ name = "lv"
|
||||||
path = "./src/main.rs"
|
path = "./src/main.rs"
|
||||||
|
|
||||||
[workspace]
|
[workspace]
|
||||||
members = [".", "./logparse/"]
|
members = [".", "./logparse/", "./pretty-print/projects/*"]
|
||||||
default-members = [".", "./logparse/"]
|
default-members = [".", "./logparse/", "./pretty-print/projects/pretty-print"]
|
||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
clap = {version="4.5", features=["derive", "string"]}
|
clap = {version="4.5", features=["derive", "string"]}
|
||||||
|
|
@ -26,4 +26,4 @@ regex = "1"
|
||||||
crossterm = "*"
|
crossterm = "*"
|
||||||
dumpster = "2.1"
|
dumpster = "2.1"
|
||||||
logparse = {path = "./logparse/", version="0.2.0"}
|
logparse = {path = "./logparse/", version="0.2.0"}
|
||||||
pretty-print = "0.1"
|
pretty-print = {path = "./pretty-print/projects/pretty-print"}
|
||||||
|
|
|
||||||
11
pretty-print/.editorconfig
Normal file
11
pretty-print/.editorconfig
Normal file
|
|
@ -0,0 +1,11 @@
|
||||||
|
[*]
|
||||||
|
charset = utf-8
|
||||||
|
|
||||||
|
|
||||||
|
[*.pest]
|
||||||
|
indent_style = space
|
||||||
|
indent_size = 4
|
||||||
|
|
||||||
|
[*.toml]
|
||||||
|
indent_style = space
|
||||||
|
indent_size = 4
|
||||||
27
pretty-print/.github/workflows/rust.yml
vendored
Normal file
27
pretty-print/.github/workflows/rust.yml
vendored
Normal file
|
|
@ -0,0 +1,27 @@
|
||||||
|
name: Rust
|
||||||
|
|
||||||
|
on:
|
||||||
|
push:
|
||||||
|
branches: [ master, dev ]
|
||||||
|
pull_request:
|
||||||
|
branches: [ master, dev ]
|
||||||
|
|
||||||
|
jobs:
|
||||||
|
build:
|
||||||
|
runs-on: ${{ matrix.os }}
|
||||||
|
strategy:
|
||||||
|
matrix:
|
||||||
|
os: [ubuntu-latest, macos-latest, windows-latest]
|
||||||
|
steps:
|
||||||
|
- run: git config --global core.autocrlf false
|
||||||
|
- uses: actions/checkout@v2
|
||||||
|
- name: Rust Nightly
|
||||||
|
uses: actions-rs/toolchain@v1
|
||||||
|
with:
|
||||||
|
toolchain: nightly
|
||||||
|
override: true
|
||||||
|
components: rustfmt, clippy
|
||||||
|
- name: Build
|
||||||
|
run: cargo build --release
|
||||||
|
# - name: Tests
|
||||||
|
# run: cargo test --release
|
||||||
14
pretty-print/.gitignore
vendored
Normal file
14
pretty-print/.gitignore
vendored
Normal file
|
|
@ -0,0 +1,14 @@
|
||||||
|
# OS
|
||||||
|
.DS_Store/
|
||||||
|
thumbs.db
|
||||||
|
time-travel.*
|
||||||
|
|
||||||
|
# IDE
|
||||||
|
.vscode/
|
||||||
|
.vs/
|
||||||
|
.idea/
|
||||||
|
*.iml
|
||||||
|
|
||||||
|
# Rust
|
||||||
|
target/
|
||||||
|
Cargo.lock
|
||||||
19
pretty-print/.run/Test All.run.xml
Normal file
19
pretty-print/.run/Test All.run.xml
Normal file
|
|
@ -0,0 +1,19 @@
|
||||||
|
<component name="ProjectRunConfigurationManager">
|
||||||
|
<configuration default="false" name="Test All" type="CargoCommandRunConfiguration" factoryName="Cargo Command">
|
||||||
|
<option name="command" value="test" />
|
||||||
|
<option name="workingDirectory" value="file://$PROJECT_DIR$/projects/pretty-print" />
|
||||||
|
<option name="emulateTerminal" value="false" />
|
||||||
|
<option name="channel" value="DEFAULT" />
|
||||||
|
<option name="requiredFeatures" value="true" />
|
||||||
|
<option name="allFeatures" value="true" />
|
||||||
|
<option name="withSudo" value="false" />
|
||||||
|
<option name="buildTarget" value="REMOTE" />
|
||||||
|
<option name="backtrace" value="SHORT" />
|
||||||
|
<envs />
|
||||||
|
<option name="isRedirectInput" value="false" />
|
||||||
|
<option name="redirectInputPath" value="" />
|
||||||
|
<method v="2">
|
||||||
|
<option name="CARGO.BUILD_TASK_PROVIDER" enabled="true" />
|
||||||
|
</method>
|
||||||
|
</configuration>
|
||||||
|
</component>
|
||||||
356
pretty-print/License.md
Normal file
356
pretty-print/License.md
Normal file
|
|
@ -0,0 +1,356 @@
|
||||||
|
Mozilla Public License Version 2.0
|
||||||
|
==================================
|
||||||
|
|
||||||
|
### 1. Definitions
|
||||||
|
|
||||||
|
**1.1. “Contributor”**
|
||||||
|
means each individual or legal entity that creates, contributes to
|
||||||
|
the creation of, or owns Covered Software.
|
||||||
|
|
||||||
|
**1.2. “Contributor Version”**
|
||||||
|
means the combination of the Contributions of others (if any) used
|
||||||
|
by a Contributor and that particular Contributor's Contribution.
|
||||||
|
|
||||||
|
**1.3. “Contribution”**
|
||||||
|
means Covered Software of a particular Contributor.
|
||||||
|
|
||||||
|
**1.4. “Covered Software”**
|
||||||
|
means Source Code Form to which the initial Contributor has attached
|
||||||
|
the notice in Exhibit A, the Executable Form of such Source Code
|
||||||
|
Form, and Modifications of such Source Code Form, in each case
|
||||||
|
including portions thereof.
|
||||||
|
|
||||||
|
**1.5. “Incompatible With Secondary Licenses”**
|
||||||
|
means
|
||||||
|
|
||||||
|
* **(a)** that the initial Contributor has attached the notice described
|
||||||
|
in Exhibit B to the Covered Software; or
|
||||||
|
* **(b)** that the Covered Software was made available under the terms of
|
||||||
|
version 1.1 or earlier of the License, but not also under the
|
||||||
|
terms of a Secondary License.
|
||||||
|
|
||||||
|
**1.6. “Executable Form”**
|
||||||
|
means any form of the work other than Source Code Form.
|
||||||
|
|
||||||
|
**1.7. “Larger Work”**
|
||||||
|
means a work that combines Covered Software with other material, in
|
||||||
|
a separate file or files, that is not Covered Software.
|
||||||
|
|
||||||
|
**1.8. “License”**
|
||||||
|
means this document.
|
||||||
|
|
||||||
|
**1.9. “Licensable”**
|
||||||
|
means having the right to grant, to the maximum extent possible,
|
||||||
|
whether at the time of the initial grant or subsequently, any and
|
||||||
|
all of the rights conveyed by this License.
|
||||||
|
|
||||||
|
**1.10. “Modifications”**
|
||||||
|
means any of the following:
|
||||||
|
|
||||||
|
* **(a)** any file in Source Code Form that results from an addition to,
|
||||||
|
deletion from, or modification of the contents of Covered
|
||||||
|
Software; or
|
||||||
|
* **(b)** any new file in Source Code Form that contains any Covered
|
||||||
|
Software.
|
||||||
|
|
||||||
|
**1.11. “Patent Claims” of a Contributor**
|
||||||
|
means any patent claim(s), including without limitation, method,
|
||||||
|
process, and apparatus claims, in any patent Licensable by such
|
||||||
|
Contributor that would be infringed, but for the grant of the
|
||||||
|
License, by the making, using, selling, offering for sale, having
|
||||||
|
made, import, or transfer of either its Contributions or its
|
||||||
|
Contributor Version.
|
||||||
|
|
||||||
|
**1.12. “Secondary License”**
|
||||||
|
means either the GNU General Public License, Version 2.0, the GNU
|
||||||
|
Lesser General Public License, Version 2.1, the GNU Affero General
|
||||||
|
Public License, Version 3.0, or any later versions of those
|
||||||
|
licenses.
|
||||||
|
|
||||||
|
**1.13. “Source Code Form”**
|
||||||
|
means the form of the work preferred for making modifications.
|
||||||
|
|
||||||
|
**1.14. “You” (or “Your”)**
|
||||||
|
means an individual or a legal entity exercising rights under this
|
||||||
|
License. For legal entities, “You” includes any entity that
|
||||||
|
controls, is controlled by, or is under common control with You. For
|
||||||
|
purposes of this definition, “control” means **(a)** the power, direct
|
||||||
|
or indirect, to cause the direction or management of such entity,
|
||||||
|
whether by contract or otherwise, or **(b)** ownership of more than
|
||||||
|
fifty percent (50%) of the outstanding shares or beneficial
|
||||||
|
ownership of such entity.
|
||||||
|
|
||||||
|
|
||||||
|
### 2. License Grants and Conditions
|
||||||
|
|
||||||
|
#### 2.1. Grants
|
||||||
|
|
||||||
|
Each Contributor hereby grants You a world-wide, royalty-free,
|
||||||
|
non-exclusive license:
|
||||||
|
|
||||||
|
* **(a)** under intellectual property rights (other than patent or trademark)
|
||||||
|
Licensable by such Contributor to use, reproduce, make available,
|
||||||
|
modify, display, perform, distribute, and otherwise exploit its
|
||||||
|
Contributions, either on an unmodified basis, with Modifications, or
|
||||||
|
as part of a Larger Work; and
|
||||||
|
* **(b)** under Patent Claims of such Contributor to make, use, sell, offer
|
||||||
|
for sale, have made, import, and otherwise transfer either its
|
||||||
|
Contributions or its Contributor Version.
|
||||||
|
|
||||||
|
#### 2.2. Effective Date
|
||||||
|
|
||||||
|
The licenses granted in Section 2.1 with respect to any Contribution
|
||||||
|
become effective for each Contribution on the date the Contributor first
|
||||||
|
distributes such Contribution.
|
||||||
|
|
||||||
|
#### 2.3. Limitations on Grant Scope
|
||||||
|
|
||||||
|
The licenses granted in this Section 2 are the only rights granted under
|
||||||
|
this License. No additional rights or licenses will be implied from the
|
||||||
|
distribution or licensing of Covered Software under this License.
|
||||||
|
Notwithstanding Section 2.1(b) above, no patent license is granted by a
|
||||||
|
Contributor:
|
||||||
|
|
||||||
|
* **(a)** for any code that a Contributor has removed from Covered Software;
|
||||||
|
or
|
||||||
|
* **(b)** for infringements caused by: **(i)** Your and any other third party's
|
||||||
|
modifications of Covered Software, or **(ii)** the combination of its
|
||||||
|
Contributions with other software (except as part of its Contributor
|
||||||
|
Version); or
|
||||||
|
* **(c)** under Patent Claims infringed by Covered Software in the absence of
|
||||||
|
its Contributions.
|
||||||
|
|
||||||
|
This License does not grant any rights in the trademarks, service marks,
|
||||||
|
or logos of any Contributor (except as may be necessary to comply with
|
||||||
|
the notice requirements in Section 3.4).
|
||||||
|
|
||||||
|
#### 2.4. Subsequent Licenses
|
||||||
|
|
||||||
|
No Contributor makes additional grants as a result of Your choice to
|
||||||
|
distribute the Covered Software under a subsequent version of this
|
||||||
|
License (see Section 10.2) or under the terms of a Secondary License (if
|
||||||
|
permitted under the terms of Section 3.3).
|
||||||
|
|
||||||
|
#### 2.5. Representation
|
||||||
|
|
||||||
|
Each Contributor represents that the Contributor believes its
|
||||||
|
Contributions are its original creation(s) or it has sufficient rights
|
||||||
|
to grant the rights to its Contributions conveyed by this License.
|
||||||
|
|
||||||
|
#### 2.6. Fair Use
|
||||||
|
|
||||||
|
This License is not intended to limit any rights You have under
|
||||||
|
applicable copyright doctrines of fair use, fair dealing, or other
|
||||||
|
equivalents.
|
||||||
|
|
||||||
|
#### 2.7. Conditions
|
||||||
|
|
||||||
|
Sections 3.1, 3.2, 3.3, and 3.4 are conditions of the licenses granted
|
||||||
|
in Section 2.1.
|
||||||
|
|
||||||
|
|
||||||
|
### 3. Responsibilities
|
||||||
|
|
||||||
|
#### 3.1. Distribution of Source Form
|
||||||
|
|
||||||
|
All distribution of Covered Software in Source Code Form, including any
|
||||||
|
Modifications that You create or to which You contribute, must be under
|
||||||
|
the terms of this License. You must inform recipients that the Source
|
||||||
|
Code Form of the Covered Software is governed by the terms of this
|
||||||
|
License, and how they can obtain a copy of this License. You may not
|
||||||
|
attempt to alter or restrict the recipients' rights in the Source Code
|
||||||
|
Form.
|
||||||
|
|
||||||
|
#### 3.2. Distribution of Executable Form
|
||||||
|
|
||||||
|
If You distribute Covered Software in Executable Form then:
|
||||||
|
|
||||||
|
* **(a)** such Covered Software must also be made available in Source Code
|
||||||
|
Form, as described in Section 3.1, and You must inform recipients of
|
||||||
|
the Executable Form how they can obtain a copy of such Source Code
|
||||||
|
Form by reasonable means in a timely manner, at a charge no more
|
||||||
|
than the cost of distribution to the recipient; and
|
||||||
|
|
||||||
|
* **(b)** You may distribute such Executable Form under the terms of this
|
||||||
|
License, or sublicense it under different terms, provided that the
|
||||||
|
license for the Executable Form does not attempt to limit or alter
|
||||||
|
the recipients' rights in the Source Code Form under this License.
|
||||||
|
|
||||||
|
#### 3.3. Distribution of a Larger Work
|
||||||
|
|
||||||
|
You may create and distribute a Larger Work under terms of Your choice,
|
||||||
|
provided that You also comply with the requirements of this License for
|
||||||
|
the Covered Software. If the Larger Work is a combination of Covered
|
||||||
|
Software with a work governed by one or more Secondary Licenses, and the
|
||||||
|
Covered Software is not Incompatible With Secondary Licenses, this
|
||||||
|
License permits You to additionally distribute such Covered Software
|
||||||
|
under the terms of such Secondary License(s), so that the recipient of
|
||||||
|
the Larger Work may, at their option, further distribute the Covered
|
||||||
|
Software under the terms of either this License or such Secondary
|
||||||
|
License(s).
|
||||||
|
|
||||||
|
#### 3.4. Notices
|
||||||
|
|
||||||
|
You may not remove or alter the substance of any license notices
|
||||||
|
(including copyright notices, patent notices, disclaimers of warranty,
|
||||||
|
or limitations of liability) contained within the Source Code Form of
|
||||||
|
the Covered Software, except that You may alter any license notices to
|
||||||
|
the extent required to remedy known factual inaccuracies.
|
||||||
|
|
||||||
|
#### 3.5. Application of Additional Terms
|
||||||
|
|
||||||
|
You may choose to offer, and to charge a fee for, warranty, support,
|
||||||
|
indemnity or liability obligations to one or more recipients of Covered
|
||||||
|
Software. However, You may do so only on Your own behalf, and not on
|
||||||
|
behalf of any Contributor. You must make it absolutely clear that any
|
||||||
|
such warranty, support, indemnity, or liability obligation is offered by
|
||||||
|
You alone, and You hereby agree to indemnify every Contributor for any
|
||||||
|
liability incurred by such Contributor as a result of warranty, support,
|
||||||
|
indemnity or liability terms You offer. You may include additional
|
||||||
|
disclaimers of warranty and limitations of liability specific to any
|
||||||
|
jurisdiction.
|
||||||
|
|
||||||
|
|
||||||
|
### 4. Inability to Comply Due to Statute or Regulation
|
||||||
|
|
||||||
|
If it is impossible for You to comply with any of the terms of this
|
||||||
|
License with respect to some or all of the Covered Software due to
|
||||||
|
statute, judicial order, or regulation then You must: **(a)** comply with
|
||||||
|
the terms of this License to the maximum extent possible; and **(b)**
|
||||||
|
describe the limitations and the code they affect. Such description must
|
||||||
|
be placed in a text file included with all distributions of the Covered
|
||||||
|
Software under this License. Except to the extent prohibited by statute
|
||||||
|
or regulation, such description must be sufficiently detailed for a
|
||||||
|
recipient of ordinary skill to be able to understand it.
|
||||||
|
|
||||||
|
|
||||||
|
### 5. Termination
|
||||||
|
|
||||||
|
**5.1.** The rights granted under this License will terminate automatically
|
||||||
|
if You fail to comply with any of its terms. However, if You become
|
||||||
|
compliant, then the rights granted under this License from a particular
|
||||||
|
Contributor are reinstated **(a)** provisionally, unless and until such
|
||||||
|
Contributor explicitly and finally terminates Your grants, and **(b)** on an
|
||||||
|
ongoing basis, if such Contributor fails to notify You of the
|
||||||
|
non-compliance by some reasonable means prior to 60 days after You have
|
||||||
|
come back into compliance. Moreover, Your grants from a particular
|
||||||
|
Contributor are reinstated on an ongoing basis if such Contributor
|
||||||
|
notifies You of the non-compliance by some reasonable means, this is the
|
||||||
|
first time You have received notice of non-compliance with this License
|
||||||
|
from such Contributor, and You become compliant prior to 30 days after
|
||||||
|
Your receipt of the notice.
|
||||||
|
|
||||||
|
**5.2.** If You initiate litigation against any entity by asserting a patent
|
||||||
|
infringement claim (excluding declaratory judgment actions,
|
||||||
|
counter-claims, and cross-claims) alleging that a Contributor Version
|
||||||
|
directly or indirectly infringes any patent, then the rights granted to
|
||||||
|
You by any and all Contributors for the Covered Software under Section
|
||||||
|
2.1 of this License shall terminate.
|
||||||
|
|
||||||
|
**5.3.** In the event of termination under Sections 5.1 or 5.2 above, all
|
||||||
|
end user license agreements (excluding distributors and resellers) which
|
||||||
|
have been validly granted by You or Your distributors under this License
|
||||||
|
prior to termination shall survive termination.
|
||||||
|
|
||||||
|
|
||||||
|
### 6. Disclaimer of Warranty
|
||||||
|
|
||||||
|
> Covered Software is provided under this License on an “as is”
|
||||||
|
> basis, without warranty of any kind, either expressed, implied, or
|
||||||
|
> statutory, including, without limitation, warranties that the
|
||||||
|
> Covered Software is free of defects, merchantable, fit for a
|
||||||
|
> particular purpose or non-infringing. The entire risk as to the
|
||||||
|
> quality and performance of the Covered Software is with You.
|
||||||
|
> Should any Covered Software prove defective in any respect, You
|
||||||
|
> (not any Contributor) assume the cost of any necessary servicing,
|
||||||
|
> repair, or correction. This disclaimer of warranty constitutes an
|
||||||
|
> essential part of this License. No use of any Covered Software is
|
||||||
|
> authorized under this License except under this disclaimer.
|
||||||
|
|
||||||
|
### 7. Limitation of Liability
|
||||||
|
|
||||||
|
> Under no circumstances and under no legal theory, whether tort
|
||||||
|
> (including negligence), contract, or otherwise, shall any
|
||||||
|
> Contributor, or anyone who distributes Covered Software as
|
||||||
|
> permitted above, be liable to You for any direct, indirect,
|
||||||
|
> special, incidental, or consequential damages of any character
|
||||||
|
> including, without limitation, damages for lost profits, loss of
|
||||||
|
> goodwill, work stoppage, computer failure or malfunction, or any
|
||||||
|
> and all other commercial damages or losses, even if such party
|
||||||
|
> shall have been informed of the possibility of such damages. This
|
||||||
|
> limitation of liability shall not apply to liability for death or
|
||||||
|
> personal injury resulting from such party's negligence to the
|
||||||
|
> extent applicable law prohibits such limitation. Some
|
||||||
|
> jurisdictions do not allow the exclusion or limitation of
|
||||||
|
> incidental or consequential damages, so this exclusion and
|
||||||
|
> limitation may not apply to You.
|
||||||
|
|
||||||
|
|
||||||
|
### 8. Litigation
|
||||||
|
|
||||||
|
Any litigation relating to this License may be brought only in the
|
||||||
|
courts of a jurisdiction where the defendant maintains its principal
|
||||||
|
place of business and such litigation shall be governed by laws of that
|
||||||
|
jurisdiction, without reference to its conflict-of-law provisions.
|
||||||
|
Nothing in this Section shall prevent a party's ability to bring
|
||||||
|
cross-claims or counter-claims.
|
||||||
|
|
||||||
|
|
||||||
|
### 9. Miscellaneous
|
||||||
|
|
||||||
|
This License represents the complete agreement concerning the subject
|
||||||
|
matter hereof. If any provision of this License is held to be
|
||||||
|
unenforceable, such provision shall be reformed only to the extent
|
||||||
|
necessary to make it enforceable. Any law or regulation which provides
|
||||||
|
that the language of a contract shall be construed against the drafter
|
||||||
|
shall not be used to construe this License against a Contributor.
|
||||||
|
|
||||||
|
|
||||||
|
### 10. Versions of the License
|
||||||
|
|
||||||
|
#### 10.1. New Versions
|
||||||
|
|
||||||
|
Mozilla Foundation is the license steward. Except as provided in Section
|
||||||
|
10.3, no one other than the license steward has the right to modify or
|
||||||
|
publish new versions of this License. Each version will be given a
|
||||||
|
distinguishing version number.
|
||||||
|
|
||||||
|
#### 10.2. Effect of New Versions
|
||||||
|
|
||||||
|
You may distribute the Covered Software under the terms of the version
|
||||||
|
of the License under which You originally received the Covered Software,
|
||||||
|
or under the terms of any subsequent version published by the license
|
||||||
|
steward.
|
||||||
|
|
||||||
|
#### 10.3. Modified Versions
|
||||||
|
|
||||||
|
If you create software not governed by this License, and you want to
|
||||||
|
create a new license for such software, you may create and use a
|
||||||
|
modified version of this License if you rename the license and remove
|
||||||
|
any references to the name of the license steward (except to note that
|
||||||
|
such modified license differs from this License).
|
||||||
|
|
||||||
|
#### 10.4. Distributing Source Code Form that is Incompatible With Secondary Licenses
|
||||||
|
|
||||||
|
If You choose to distribute Source Code Form that is Incompatible With
|
||||||
|
Secondary Licenses under the terms of this version of the License, the
|
||||||
|
notice described in Exhibit B of this License must be attached.
|
||||||
|
|
||||||
|
## Exhibit A - Source Code Form License Notice
|
||||||
|
|
||||||
|
This Source Code Form is subject to the terms of the Mozilla Public
|
||||||
|
License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||||
|
file, You can obtain one at http://mozilla.org/MPL/2.0/.
|
||||||
|
|
||||||
|
If it is not possible or desirable to put the notice in a particular
|
||||||
|
file, then You may include the notice in a location (such as a LICENSE
|
||||||
|
file in a relevant directory) where a recipient would be likely to look
|
||||||
|
for such a notice.
|
||||||
|
|
||||||
|
You may add additional accurate notices of copyright ownership.
|
||||||
|
|
||||||
|
## Exhibit B - “Incompatible With Secondary Licenses” Notice
|
||||||
|
|
||||||
|
This Source Code Form is "Incompatible With Secondary Licenses", as
|
||||||
|
defined by the Mozilla Public License, v. 2.0.
|
||||||
|
|
||||||
68
pretty-print/Readme.md
Normal file
68
pretty-print/Readme.md
Normal file
|
|
@ -0,0 +1,68 @@
|
||||||
|
# Pretty Printer
|
||||||
|
|
||||||
|
This crate defines a
|
||||||
|
[Wadler-style](http://homepages.inf.ed.ac.uk/wadler/papers/prettier/prettier.pdf)
|
||||||
|
pretty-printing API.
|
||||||
|
|
||||||
|
Start with the static functions of [PrettyTree](enum.Doc.html).
|
||||||
|
|
||||||
|
## Quick start
|
||||||
|
|
||||||
|
Let's pretty-print simple sexps! We want to pretty print sexps like
|
||||||
|
|
||||||
|
```lisp
|
||||||
|
(1 2 3)
|
||||||
|
```
|
||||||
|
or, if the line would be too long, like
|
||||||
|
|
||||||
|
```lisp
|
||||||
|
((1)
|
||||||
|
(2 3)
|
||||||
|
(4 5 6))
|
||||||
|
```
|
||||||
|
|
||||||
|
A _simple symbolic expression_ consists of a numeric _atom_ or a nested ordered _list_ of
|
||||||
|
symbolic expression children.
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
```rust
|
||||||
|
use pretty_print::*;
|
||||||
|
use SExp::*;
|
||||||
|
enum SExp {
|
||||||
|
Atom(u32),
|
||||||
|
List(Vec<SExp>),
|
||||||
|
}
|
||||||
|
|
||||||
|
impl SExp {
|
||||||
|
/// Return a pretty printed format of self.
|
||||||
|
pub fn to_doc(&self) -> PrettyTree {
|
||||||
|
match self {
|
||||||
|
Atom(x) => PrettyTree::text(x.to_string()),
|
||||||
|
List(xs) => PrettyTree::text("(")
|
||||||
|
.append(PrettyTree::join(xs.into_iter().map(|x| x.to_doc()), PrettyTree::line_or_space()).nest(1).group())
|
||||||
|
.append(PrettyTree::text(")")),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
/// Return a pretty printed format of self.
|
||||||
|
pub fn to_pretty(&self, width: usize) -> String {
|
||||||
|
let mut w = Vec::new();
|
||||||
|
self.to_doc().render(width, &mut w).unwrap();
|
||||||
|
String::from_utf8(w).unwrap()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn main() {
|
||||||
|
let atom = Atom(5);
|
||||||
|
assert_eq!("5", atom.to_pretty(10));
|
||||||
|
let list = List(vec![Atom(1), Atom(2), Atom(3)]);
|
||||||
|
assert_eq!("(1 2 3)", list.to_pretty(10));
|
||||||
|
assert_eq!(
|
||||||
|
"\
|
||||||
|
(1
|
||||||
|
2
|
||||||
|
3)",
|
||||||
|
list.to_pretty(5)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
```
|
||||||
11
pretty-print/package.json
Normal file
11
pretty-print/package.json
Normal file
|
|
@ -0,0 +1,11 @@
|
||||||
|
{
|
||||||
|
"private": true,
|
||||||
|
"scripts": {
|
||||||
|
"doc": "cargo doc --no-deps --all-features",
|
||||||
|
"test": "cargo test --release",
|
||||||
|
"fmt": "cargo fmt --all",
|
||||||
|
"p": "git push && git push --tags --prune",
|
||||||
|
"reset": "git reset Head~ --soft",
|
||||||
|
"u": "cargo upgrade --workspace"
|
||||||
|
}
|
||||||
|
}
|
||||||
25
pretty-print/projects/pretty-print/Cargo.toml
Normal file
25
pretty-print/projects/pretty-print/Cargo.toml
Normal file
|
|
@ -0,0 +1,25 @@
|
||||||
|
[package]
|
||||||
|
name = "pretty-print"
|
||||||
|
version = "0.1.8"
|
||||||
|
authors = ["Aster <192607617@qq.com>"]
|
||||||
|
description = "pretty print tree"
|
||||||
|
repository = "https://github.com/oovm/pretty-print"
|
||||||
|
documentation = "https://docs.rs/pretty-print"
|
||||||
|
readme = "Readme.md"
|
||||||
|
license = "MPL-2.0"
|
||||||
|
edition = "2021"
|
||||||
|
|
||||||
|
[dependencies]
|
||||||
|
unicode-segmentation = "1.10.1"
|
||||||
|
|
||||||
|
[dependencies.color-ansi]
|
||||||
|
version = "0.1.0"
|
||||||
|
#default-features = false
|
||||||
|
#path = 'C:\Users\Dell\CLionProjects\color-rs\projects\color-ansi'
|
||||||
|
|
||||||
|
[dev-dependencies]
|
||||||
|
|
||||||
|
|
||||||
|
[features]
|
||||||
|
default = ["std"]
|
||||||
|
std = []
|
||||||
6
pretty-print/projects/pretty-print/package.json
Normal file
6
pretty-print/projects/pretty-print/package.json
Normal file
|
|
@ -0,0 +1,6 @@
|
||||||
|
{
|
||||||
|
"private": true,
|
||||||
|
"scripts": {
|
||||||
|
"p": "cargo publish --allow-dirty"
|
||||||
|
}
|
||||||
|
}
|
||||||
24
pretty-print/projects/pretty-print/readme.md
Normal file
24
pretty-print/projects/pretty-print/readme.md
Normal file
|
|
@ -0,0 +1,24 @@
|
||||||
|
This crate defines a
|
||||||
|
[Wadler-style](http://homepages.inf.ed.ac.uk/wadler/papers/prettier/prettier.pdf)
|
||||||
|
pretty-printing API.
|
||||||
|
|
||||||
|
Start with the static functions of [Doc](enum.Doc.html).
|
||||||
|
|
||||||
|
## Quick start
|
||||||
|
|
||||||
|
Let's pretty-print simple sexps! We want to pretty print sexps like
|
||||||
|
|
||||||
|
```lisp
|
||||||
|
(1 2 3)
|
||||||
|
```
|
||||||
|
or, if the line would be too long, like
|
||||||
|
|
||||||
|
```lisp
|
||||||
|
((1)
|
||||||
|
(2 3)
|
||||||
|
(4 5 6))
|
||||||
|
```
|
||||||
|
|
||||||
|
A _simple symbolic expression_ consists of a numeric _atom_ or a nested ordered _list_ of
|
||||||
|
symbolic expression children.
|
||||||
|
|
||||||
193
pretty-print/projects/pretty-print/src/helpers/affixes.rs
Normal file
193
pretty-print/projects/pretty-print/src/helpers/affixes.rs
Normal file
|
|
@ -0,0 +1,193 @@
|
||||||
|
//! Document formatting of "helpers" such as where some number of prefixes and suffixes would
|
||||||
|
//! ideally be layed out onto a single line instead of breaking them up into multiple lines. See
|
||||||
|
//! `BlockDoc` for an example
|
||||||
|
|
||||||
|
use crate::{docs, DocumentTree, DocumentTree};
|
||||||
|
|
||||||
|
pub struct Affixes<'doc, D, A>
|
||||||
|
{
|
||||||
|
prefix: DocumentTree<'doc, D, A>,
|
||||||
|
suffix: DocumentTree<'doc, D, A>,
|
||||||
|
nest: bool,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<'a, D> Clone for Affixes<'a, D>
|
||||||
|
{
|
||||||
|
fn clone(&self) -> Self {
|
||||||
|
Affixes {
|
||||||
|
prefix: self.prefix.clone(),
|
||||||
|
suffix: self.suffix.clone(),
|
||||||
|
nest: self.nest,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<'doc, D, A> Affixes<'doc, D, A>
|
||||||
|
{
|
||||||
|
pub fn new(prefix: DocumentTree<'doc, D, A>, suffix: DocumentTree<'doc, D, A>) -> Self {
|
||||||
|
Affixes {
|
||||||
|
prefix,
|
||||||
|
suffix,
|
||||||
|
nest: false,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn nest(mut self) -> Self {
|
||||||
|
self.nest = true;
|
||||||
|
self
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Formats a set of `prefix` and `suffix` documents around a `body`
|
||||||
|
///
|
||||||
|
/// The following document split into the prefixes [\x y ->, \z ->, {], suffixes [nil, nil, }] and
|
||||||
|
/// body [result: x + y - z] will try to be formatted
|
||||||
|
///
|
||||||
|
/// ```gluon
|
||||||
|
/// \x y -> \z -> { result: x + y - z }
|
||||||
|
/// ```
|
||||||
|
///
|
||||||
|
/// ```gluon
|
||||||
|
/// \x y -> \z -> {
|
||||||
|
/// result: x + y - z
|
||||||
|
/// }
|
||||||
|
/// ```
|
||||||
|
///
|
||||||
|
/// ```gluon
|
||||||
|
/// \x y -> \z ->
|
||||||
|
/// {
|
||||||
|
/// result: x + y - z
|
||||||
|
/// }
|
||||||
|
/// ```
|
||||||
|
///
|
||||||
|
/// ```gluon
|
||||||
|
/// \x y ->
|
||||||
|
/// \z ->
|
||||||
|
/// {
|
||||||
|
/// result: x + y - z
|
||||||
|
/// }
|
||||||
|
/// ```
|
||||||
|
pub struct BlockDoc<'doc, D, A>
|
||||||
|
where
|
||||||
|
D: DocAllocator<'doc, A>,
|
||||||
|
{
|
||||||
|
pub affixes: Vec<Affixes<'doc, D, A>>,
|
||||||
|
pub body: DocumentTree<'doc, D, A>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<'doc, D, A> BlockDoc<'doc, D, A>
|
||||||
|
where
|
||||||
|
D: DocAllocator<'doc, A>,
|
||||||
|
D::Doc: Clone,
|
||||||
|
A: Clone,
|
||||||
|
{
|
||||||
|
pub fn format(self, nest: isize) -> DocumentTree<'doc, D, A> {
|
||||||
|
let arena = self.body.0;
|
||||||
|
|
||||||
|
let fail_on_multi_line = arena.fail().flat_alt(arena.nil());
|
||||||
|
|
||||||
|
(1..self.affixes.len() + 1)
|
||||||
|
.rev()
|
||||||
|
.map(|split| {
|
||||||
|
let (before, after) = self.affixes.split_at(split);
|
||||||
|
let last = before.len() == 1;
|
||||||
|
docs![
|
||||||
|
arena,
|
||||||
|
docs![
|
||||||
|
arena,
|
||||||
|
arena.concat(before.iter().map(|affixes| affixes.prefix.clone())),
|
||||||
|
if last {
|
||||||
|
arena.nil()
|
||||||
|
} else {
|
||||||
|
fail_on_multi_line.clone()
|
||||||
|
}
|
||||||
|
]
|
||||||
|
.group(),
|
||||||
|
docs![
|
||||||
|
arena,
|
||||||
|
after.iter().rev().cloned().fold(
|
||||||
|
docs![
|
||||||
|
arena,
|
||||||
|
self.body.clone(),
|
||||||
|
// If there is no prefix then we must not allow the body to laid out on multiple
|
||||||
|
// lines without nesting
|
||||||
|
if !last
|
||||||
|
&& before
|
||||||
|
.iter()
|
||||||
|
.all(|affixes| matches!(&*affixes.prefix.1, DocumentTree::Nil))
|
||||||
|
{
|
||||||
|
fail_on_multi_line.clone()
|
||||||
|
} else {
|
||||||
|
arena.nil()
|
||||||
|
},
|
||||||
|
]
|
||||||
|
.nest(nest)
|
||||||
|
.append(
|
||||||
|
arena.concat(after.iter().map(|affixes| affixes.suffix.clone()))
|
||||||
|
),
|
||||||
|
|acc, affixes| {
|
||||||
|
let mut doc = affixes.prefix.append(acc);
|
||||||
|
if affixes.nest {
|
||||||
|
doc = doc.nest(nest);
|
||||||
|
}
|
||||||
|
doc.group()
|
||||||
|
},
|
||||||
|
),
|
||||||
|
arena.concat(before.iter().map(|affixes| affixes.suffix.clone())),
|
||||||
|
]
|
||||||
|
.group(),
|
||||||
|
]
|
||||||
|
})
|
||||||
|
.fold(None::<DocumentTree<_, _>>, |acc, doc| {
|
||||||
|
Some(match acc {
|
||||||
|
None => doc,
|
||||||
|
Some(acc) => acc.union(doc),
|
||||||
|
})
|
||||||
|
})
|
||||||
|
.unwrap_or(self.body)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(test)]
|
||||||
|
mod tests {
|
||||||
|
use super::*;
|
||||||
|
|
||||||
|
use crate::Arena;
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn format_block() {
|
||||||
|
let arena = &Arena::<()>::new();
|
||||||
|
let mk_doc = || BlockDoc {
|
||||||
|
affixes: vec![
|
||||||
|
Affixes::new(docs![arena, "\\x y ->"], arena.nil()).nest(),
|
||||||
|
Affixes::new(docs![arena, arena.line(), "\\z ->"], arena.nil()).nest(),
|
||||||
|
Affixes::new(
|
||||||
|
docs![arena, arena.line(), "{"],
|
||||||
|
docs![arena, arena.line(), "}"],
|
||||||
|
)
|
||||||
|
.nest(),
|
||||||
|
],
|
||||||
|
body: docs![arena, arena.line(), "result"],
|
||||||
|
};
|
||||||
|
expect_test::expect![[r#"\x y -> \z -> { result }"#]]
|
||||||
|
.assert_eq(&mk_doc().format(4).1.pretty(40).to_string());
|
||||||
|
expect_test::expect![[r#"
|
||||||
|
\x y -> \z -> {
|
||||||
|
result
|
||||||
|
}"#]]
|
||||||
|
.assert_eq(&mk_doc().format(4).1.pretty(15).to_string());
|
||||||
|
expect_test::expect![[r#"
|
||||||
|
\x y -> \z ->
|
||||||
|
{
|
||||||
|
result
|
||||||
|
}"#]]
|
||||||
|
.assert_eq(&mk_doc().format(4).1.pretty(14).to_string());
|
||||||
|
expect_test::expect![[r#"
|
||||||
|
\x y ->
|
||||||
|
\z ->
|
||||||
|
{
|
||||||
|
result
|
||||||
|
}"#]]
|
||||||
|
.assert_eq(&mk_doc().format(4).1.pretty(12).to_string());
|
||||||
|
}
|
||||||
|
}
|
||||||
66
pretty-print/projects/pretty-print/src/helpers/hard_block.rs
Normal file
66
pretty-print/projects/pretty-print/src/helpers/hard_block.rs
Normal file
|
|
@ -0,0 +1,66 @@
|
||||||
|
use super::*;
|
||||||
|
|
||||||
|
/// A soft block is a block that is not required to be on a new line.
|
||||||
|
///
|
||||||
|
/// ```vk
|
||||||
|
/// {a, b, c}
|
||||||
|
///
|
||||||
|
/// {
|
||||||
|
/// a,
|
||||||
|
/// b,
|
||||||
|
/// }
|
||||||
|
/// ```
|
||||||
|
#[derive(Clone, Debug)]
|
||||||
|
pub struct HardBlock {
|
||||||
|
/// The indentation of the soft block
|
||||||
|
pub indent: usize,
|
||||||
|
/// The left hand side of the soft block
|
||||||
|
pub lhs: &'static str,
|
||||||
|
/// The right hand side of the soft block
|
||||||
|
pub rhs: &'static str,
|
||||||
|
/// The joint node of the soft block
|
||||||
|
pub joint: PrettyTree,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl HardBlock {
|
||||||
|
/// Build a new soft block
|
||||||
|
pub fn new(lhs: &'static str, rhs: &'static str) -> Self {
|
||||||
|
Self { lhs, rhs, indent: 4, joint: PrettyTree::line_or_space() }
|
||||||
|
}
|
||||||
|
/// Build a new soft block with the parentheses syntax
|
||||||
|
pub fn parentheses() -> Self {
|
||||||
|
Self::new("(", ")")
|
||||||
|
}
|
||||||
|
/// Build a new soft block with the brackets syntax
|
||||||
|
pub fn brackets() -> Self {
|
||||||
|
Self::new("[", "]")
|
||||||
|
}
|
||||||
|
/// Build a new soft block with the curly braces syntax
|
||||||
|
pub fn curly_braces() -> Self {
|
||||||
|
Self::new("{", "}")
|
||||||
|
}
|
||||||
|
/// Set the joint node of the soft block
|
||||||
|
pub fn with_joint(self, joint: PrettyTree) -> Self {
|
||||||
|
Self { joint, ..self }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl HardBlock {
|
||||||
|
/// Join a slice of pretty printables with the soft block
|
||||||
|
pub fn join_slice<T: PrettyPrint>(&self, slice: &[T], theme: &PrettyProvider) -> PrettyTree {
|
||||||
|
let mut outer = PrettySequence::new(5);
|
||||||
|
outer += self.lhs;
|
||||||
|
outer += PrettyTree::Hardline;
|
||||||
|
let mut inner = PrettySequence::new(slice.len() * 2);
|
||||||
|
for (idx, term) in slice.iter().enumerate() {
|
||||||
|
if idx != 0 {
|
||||||
|
inner += self.joint.clone();
|
||||||
|
}
|
||||||
|
inner += term.pretty(theme);
|
||||||
|
}
|
||||||
|
outer += inner.indent(self.indent);
|
||||||
|
outer += PrettyTree::Hardline;
|
||||||
|
outer += self.rhs;
|
||||||
|
outer.into()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,61 @@
|
||||||
|
use super::*;
|
||||||
|
use crate::PrettyBuilder;
|
||||||
|
|
||||||
|
/// `K & R` style brackets
|
||||||
|
///
|
||||||
|
/// ```vk
|
||||||
|
/// a {}
|
||||||
|
/// ```
|
||||||
|
///
|
||||||
|
/// ```vk
|
||||||
|
/// a {
|
||||||
|
///
|
||||||
|
/// }
|
||||||
|
/// ```
|
||||||
|
#[derive(Copy, Clone, Debug)]
|
||||||
|
pub struct KAndRBracket {
|
||||||
|
/// Whether to add a space after the keyword
|
||||||
|
pub head_space: bool,
|
||||||
|
/// The left bracket
|
||||||
|
pub bracket_l: &'static str,
|
||||||
|
/// The right bracket
|
||||||
|
pub bracket_r: &'static str,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl KAndRBracket {
|
||||||
|
/// Build a bracketed block
|
||||||
|
pub fn curly_braces() -> Self {
|
||||||
|
Self { head_space: true, bracket_l: "{", bracket_r: "}" }
|
||||||
|
}
|
||||||
|
/// Build a bracketed block
|
||||||
|
pub fn build<'a, I>(
|
||||||
|
&self,
|
||||||
|
items: &[I],
|
||||||
|
allocator: &'a PrettyProvider,
|
||||||
|
inline_join: PrettyTree,
|
||||||
|
block_join: PrettyTree,
|
||||||
|
) -> PrettyTree
|
||||||
|
where
|
||||||
|
I: PrettyPrint,
|
||||||
|
{
|
||||||
|
let mut output = PrettySequence::new(5);
|
||||||
|
if self.head_space {
|
||||||
|
output.push(" ");
|
||||||
|
}
|
||||||
|
output.push(self.bracket_l);
|
||||||
|
// inline
|
||||||
|
let mut inline = PrettySequence::new(3);
|
||||||
|
inline.push(" ");
|
||||||
|
inline.push(allocator.join_slice(items, inline_join));
|
||||||
|
inline.push(" ");
|
||||||
|
// block
|
||||||
|
let mut block = PrettySequence::new(3);
|
||||||
|
block.push(PrettyTree::Hardline);
|
||||||
|
block.push(allocator.join_slice(items, block_join).indent(4));
|
||||||
|
block.push(PrettyTree::Hardline);
|
||||||
|
//
|
||||||
|
output.push(block.flat_alt(inline));
|
||||||
|
output.push(self.bracket_r);
|
||||||
|
output.into()
|
||||||
|
}
|
||||||
|
}
|
||||||
13
pretty-print/projects/pretty-print/src/helpers/mod.rs
Normal file
13
pretty-print/projects/pretty-print/src/helpers/mod.rs
Normal file
|
|
@ -0,0 +1,13 @@
|
||||||
|
#![doc = include_str!("readme.md")]
|
||||||
|
|
||||||
|
use crate::{PrettyBuilder, PrettyPrint, PrettyProvider, PrettyTree};
|
||||||
|
use alloc::vec::Vec;
|
||||||
|
use core::ops::AddAssign;
|
||||||
|
|
||||||
|
mod hard_block;
|
||||||
|
mod k_and_r_bracket;
|
||||||
|
mod sequence;
|
||||||
|
mod soft_block;
|
||||||
|
// mod affixes;
|
||||||
|
|
||||||
|
pub use self::{hard_block::HardBlock, k_and_r_bracket::KAndRBracket, sequence::PrettySequence, soft_block::SoftBlock};
|
||||||
0
pretty-print/projects/pretty-print/src/helpers/readme.md
Normal file
0
pretty-print/projects/pretty-print/src/helpers/readme.md
Normal file
60
pretty-print/projects/pretty-print/src/helpers/sequence.rs
Normal file
60
pretty-print/projects/pretty-print/src/helpers/sequence.rs
Normal file
|
|
@ -0,0 +1,60 @@
|
||||||
|
use super::*;
|
||||||
|
|
||||||
|
/// The document sequence type.
|
||||||
|
#[derive(Clone, Debug, Default)]
|
||||||
|
pub struct PrettySequence {
|
||||||
|
items: Vec<PrettyTree>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl PrettySequence {
|
||||||
|
/// Create a new sequence with the given capacity.
|
||||||
|
pub fn new(capacity: usize) -> Self {
|
||||||
|
Self { items: Vec::with_capacity(capacity) }
|
||||||
|
}
|
||||||
|
/// Create a new sequence with the given capacity.
|
||||||
|
pub fn push<T>(&mut self, item: T)
|
||||||
|
where
|
||||||
|
T: Into<PrettyTree>,
|
||||||
|
{
|
||||||
|
self.items.push(item.into());
|
||||||
|
}
|
||||||
|
/// Create a new sequence with the given capacity.
|
||||||
|
pub fn extend<I, T>(&mut self, items: I)
|
||||||
|
where
|
||||||
|
I: IntoIterator<Item = T>,
|
||||||
|
T: Into<PrettyTree>,
|
||||||
|
{
|
||||||
|
self.items.extend(items.into_iter().map(|x| x.into()));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl PrettyBuilder for PrettySequence {
|
||||||
|
fn flat_alt<E>(self, flat: E) -> PrettyTree
|
||||||
|
where
|
||||||
|
E: Into<PrettyTree>,
|
||||||
|
{
|
||||||
|
PrettyTree::from(self).flat_alt(flat)
|
||||||
|
}
|
||||||
|
fn indent(self, indent: usize) -> PrettyTree {
|
||||||
|
PrettyTree::from(self).indent(indent)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn nest(self, offset: isize) -> PrettyTree {
|
||||||
|
PrettyTree::from(self).nest(offset)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<T> AddAssign<T> for PrettySequence
|
||||||
|
where
|
||||||
|
T: Into<PrettyTree>,
|
||||||
|
{
|
||||||
|
fn add_assign(&mut self, rhs: T) {
|
||||||
|
self.push(rhs);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl From<PrettySequence> for PrettyTree {
|
||||||
|
fn from(value: PrettySequence) -> Self {
|
||||||
|
Self::concat(value.items)
|
||||||
|
}
|
||||||
|
}
|
||||||
72
pretty-print/projects/pretty-print/src/helpers/soft_block.rs
Normal file
72
pretty-print/projects/pretty-print/src/helpers/soft_block.rs
Normal file
|
|
@ -0,0 +1,72 @@
|
||||||
|
use super::*;
|
||||||
|
|
||||||
|
/// A soft block is a block that is not required to be on a new line.
|
||||||
|
///
|
||||||
|
/// ```vk
|
||||||
|
/// {a, b, c}
|
||||||
|
///
|
||||||
|
/// {
|
||||||
|
/// a,
|
||||||
|
/// b,
|
||||||
|
/// }
|
||||||
|
/// ```
|
||||||
|
#[derive(Clone, Debug)]
|
||||||
|
pub struct SoftBlock {
|
||||||
|
/// The indentation of the soft block
|
||||||
|
pub indent: usize,
|
||||||
|
/// The left hand side of the soft block
|
||||||
|
pub lhs: &'static str,
|
||||||
|
/// The right hand side of the soft block
|
||||||
|
pub rhs: &'static str,
|
||||||
|
/// The joint node of the soft block
|
||||||
|
pub joint: PrettyTree,
|
||||||
|
/// The tail node of the soft block
|
||||||
|
pub tail: PrettyTree,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl SoftBlock {
|
||||||
|
/// Build a new soft block
|
||||||
|
pub fn new(lhs: &'static str, rhs: &'static str) -> Self {
|
||||||
|
Self { lhs, rhs, indent: 4, joint: PrettyTree::line_or_space(), tail: PrettyTree::Nil }
|
||||||
|
}
|
||||||
|
/// Build a new soft block with the tuple syntax
|
||||||
|
pub fn tuple() -> Self {
|
||||||
|
Self::new("(", ")")
|
||||||
|
}
|
||||||
|
/// Build a new soft block with the parentheses syntax
|
||||||
|
pub fn parentheses() -> Self {
|
||||||
|
Self::new("(", ")")
|
||||||
|
}
|
||||||
|
/// Build a new soft block with the brackets syntax
|
||||||
|
pub fn brackets() -> Self {
|
||||||
|
Self::new("[", "]")
|
||||||
|
}
|
||||||
|
/// Build a new soft block with the curly braces syntax
|
||||||
|
pub fn curly_braces() -> Self {
|
||||||
|
Self::new("{", "}")
|
||||||
|
}
|
||||||
|
/// Set the joint node of the soft block
|
||||||
|
pub fn with_joint(self, joint: PrettyTree) -> Self {
|
||||||
|
Self { joint, ..self }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl SoftBlock {
|
||||||
|
/// Join a slice of pretty printables with the soft block
|
||||||
|
pub fn join_slice<T: PrettyPrint>(&self, slice: &[T], theme: &PrettyProvider) -> PrettyTree {
|
||||||
|
let mut outer = PrettySequence::new(5);
|
||||||
|
outer += self.lhs;
|
||||||
|
outer += PrettyTree::line_or_space();
|
||||||
|
let mut inner = PrettySequence::new(slice.len() * 2);
|
||||||
|
for (idx, term) in slice.iter().enumerate() {
|
||||||
|
if idx != 0 {
|
||||||
|
inner += self.joint.clone();
|
||||||
|
}
|
||||||
|
inner += term.pretty(theme);
|
||||||
|
}
|
||||||
|
outer += inner.indent(self.indent);
|
||||||
|
outer += PrettyTree::line_or_space();
|
||||||
|
outer += self.rhs;
|
||||||
|
outer.into()
|
||||||
|
}
|
||||||
|
}
|
||||||
51
pretty-print/projects/pretty-print/src/lib.rs
Normal file
51
pretty-print/projects/pretty-print/src/lib.rs
Normal file
|
|
@ -0,0 +1,51 @@
|
||||||
|
#![cfg_attr(not(feature = "std"), no_std)]
|
||||||
|
#![deny(missing_debug_implementations, missing_copy_implementations)]
|
||||||
|
#![warn(missing_docs, rustdoc::missing_crate_level_docs)]
|
||||||
|
#![doc = include_str!("../readme.md")]
|
||||||
|
#![doc(html_logo_url = "https://raw.githubusercontent.com/oovm/shape-rs/dev/projects/images/Trapezohedron.svg")]
|
||||||
|
#![doc(html_favicon_url = "https://raw.githubusercontent.com/oovm/shape-rs/dev/projects/images/Trapezohedron.svg")]
|
||||||
|
|
||||||
|
extern crate alloc;
|
||||||
|
extern crate core;
|
||||||
|
|
||||||
|
pub mod helpers;
|
||||||
|
mod providers;
|
||||||
|
mod render;
|
||||||
|
mod traits;
|
||||||
|
mod tree;
|
||||||
|
|
||||||
|
pub use self::render::{
|
||||||
|
write_fmt::{BufferWrite, FmtWrite},
|
||||||
|
PrettyFormatter, Render, RenderAnnotated,
|
||||||
|
};
|
||||||
|
#[cfg(feature = "std")]
|
||||||
|
pub use crate::render::write_io::{IoWrite, TerminalWriter};
|
||||||
|
pub use crate::{
|
||||||
|
providers::PrettyProvider,
|
||||||
|
traits::{printer::PrettyPrint, PrettyBuilder},
|
||||||
|
tree::PrettyTree,
|
||||||
|
};
|
||||||
|
pub use color_ansi::*;
|
||||||
|
|
||||||
|
/// Concatenates a number of documents (or values that can be converted into a document via the
|
||||||
|
/// `Pretty` trait, like `&str`)
|
||||||
|
///
|
||||||
|
/// ```
|
||||||
|
/// use pretty_print::docs;
|
||||||
|
/// let doc =
|
||||||
|
/// docs!["let", arena.softline(), "x", arena.softline(), "=", arena.softline(), Some("123"),];
|
||||||
|
/// assert_eq!(doc.1.pretty(80).to_string(), "let x = 123");
|
||||||
|
/// ```
|
||||||
|
#[macro_export]
|
||||||
|
macro_rules! docs {
|
||||||
|
($alloc: expr, $first: expr $(,)?) => {
|
||||||
|
$crate::Pretty::pretty($first, $alloc)
|
||||||
|
};
|
||||||
|
($alloc: expr, $first: expr $(, $rest: expr)+ $(,)?) => {{
|
||||||
|
let mut doc = $crate::Pretty::pretty($first, $alloc);
|
||||||
|
$(
|
||||||
|
doc = doc.append($rest);
|
||||||
|
)*
|
||||||
|
doc
|
||||||
|
}}
|
||||||
|
}
|
||||||
236
pretty-print/projects/pretty-print/src/providers/mod.rs
Normal file
236
pretty-print/projects/pretty-print/src/providers/mod.rs
Normal file
|
|
@ -0,0 +1,236 @@
|
||||||
|
use crate::{PrettyPrint, PrettyTree};
|
||||||
|
use alloc::{borrow::Cow, rc::Rc};
|
||||||
|
use color_ansi::AnsiStyle;
|
||||||
|
use core::fmt::{Debug, Formatter};
|
||||||
|
|
||||||
|
/// Represents a pretty-printable tree provider.
|
||||||
|
pub struct PrettyProvider {
|
||||||
|
width: usize,
|
||||||
|
keyword: Rc<AnsiStyle>,
|
||||||
|
string: Rc<AnsiStyle>,
|
||||||
|
number: Rc<AnsiStyle>,
|
||||||
|
macros: Rc<AnsiStyle>,
|
||||||
|
argument: Rc<AnsiStyle>,
|
||||||
|
argument_mut: Rc<AnsiStyle>,
|
||||||
|
local: Rc<AnsiStyle>,
|
||||||
|
local_mut: Rc<AnsiStyle>,
|
||||||
|
operator: Rc<AnsiStyle>,
|
||||||
|
structure: Rc<AnsiStyle>,
|
||||||
|
variant: Rc<AnsiStyle>,
|
||||||
|
interface: Rc<AnsiStyle>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Debug for PrettyProvider {
|
||||||
|
fn fmt(&self, f: &mut Formatter<'_>) -> core::fmt::Result {
|
||||||
|
f.debug_struct("PrettyProvider").finish()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl PrettyProvider {
|
||||||
|
/// Creates a new pretty-printable tree provider.
|
||||||
|
pub fn new(width: usize) -> Self {
|
||||||
|
Self {
|
||||||
|
width,
|
||||||
|
keyword: AnsiStyle::rgb(197, 119, 207).into(),
|
||||||
|
string: AnsiStyle::rgb(152, 195, 121).into(),
|
||||||
|
number: AnsiStyle::rgb(206, 153, 100).into(),
|
||||||
|
macros: AnsiStyle::rgb(87, 182, 194).into(),
|
||||||
|
argument: AnsiStyle::rgb(239, 112, 117).into(),
|
||||||
|
argument_mut: AnsiStyle::rgb(239, 112, 117).with_underline().into(),
|
||||||
|
local: AnsiStyle::rgb(152, 195, 121).into(),
|
||||||
|
local_mut: AnsiStyle::rgb(152, 195, 121).with_underline().into(),
|
||||||
|
operator: AnsiStyle::rgb(90, 173, 238).into(),
|
||||||
|
structure: AnsiStyle::rgb(197, 119, 207).into(),
|
||||||
|
variant: AnsiStyle::rgb(239, 112, 117).into(),
|
||||||
|
interface: AnsiStyle::rgb(197, 119, 207).into(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl PrettyProvider {
|
||||||
|
/// Gets the width of the document.
|
||||||
|
pub fn get_width(&self) -> usize {
|
||||||
|
self.width
|
||||||
|
}
|
||||||
|
/// Sets the width of the document.
|
||||||
|
pub fn set_width(&mut self, width: usize) {
|
||||||
|
self.width = width;
|
||||||
|
}
|
||||||
|
/// Gets the width of the document.
|
||||||
|
pub fn text<S>(&self, text: S) -> PrettyTree
|
||||||
|
where
|
||||||
|
S: Into<Cow<'static, str>>,
|
||||||
|
{
|
||||||
|
PrettyTree::text(text)
|
||||||
|
}
|
||||||
|
/// Gets the width of the document.
|
||||||
|
pub fn custom<S>(&self, text: S, style: Rc<AnsiStyle>) -> PrettyTree
|
||||||
|
where
|
||||||
|
S: Into<Cow<'static, str>>,
|
||||||
|
{
|
||||||
|
PrettyTree::text(text).annotate(style)
|
||||||
|
}
|
||||||
|
/// Allocate a document containing the given text.
|
||||||
|
pub fn keyword<S>(&self, text: S) -> PrettyTree
|
||||||
|
where
|
||||||
|
S: Into<Cow<'static, str>>,
|
||||||
|
{
|
||||||
|
PrettyTree::text(text).annotate(self.keyword.clone())
|
||||||
|
}
|
||||||
|
/// Allocate a document containing the given text.
|
||||||
|
pub fn identifier<S>(&self, text: S) -> PrettyTree
|
||||||
|
where
|
||||||
|
S: Into<Cow<'static, str>>,
|
||||||
|
{
|
||||||
|
PrettyTree::text(text).annotate(self.operator.clone())
|
||||||
|
}
|
||||||
|
/// Allocate a document containing the given text.
|
||||||
|
pub fn generic<S>(&self, text: S) -> PrettyTree
|
||||||
|
where
|
||||||
|
S: Into<Cow<'static, str>>,
|
||||||
|
{
|
||||||
|
PrettyTree::text(text).annotate(self.macros.clone())
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Allocate a document containing the given text.
|
||||||
|
pub fn variable<S>(&self, text: S, mutable: bool) -> PrettyTree
|
||||||
|
where
|
||||||
|
S: Into<Cow<'static, str>>,
|
||||||
|
{
|
||||||
|
if mutable {
|
||||||
|
PrettyTree::text(text).annotate(self.local_mut.clone())
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
PrettyTree::text(text).annotate(self.local.clone())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
/// Allocate a document containing the given text.
|
||||||
|
pub fn argument<S>(&self, text: S, mutable: bool) -> PrettyTree
|
||||||
|
where
|
||||||
|
S: Into<Cow<'static, str>>,
|
||||||
|
{
|
||||||
|
if mutable {
|
||||||
|
PrettyTree::text(text).annotate(self.argument_mut.clone())
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
PrettyTree::text(text).annotate(self.argument.clone())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
/// Allocate a document containing the given text.
|
||||||
|
pub fn operator<S>(&self, text: S) -> PrettyTree
|
||||||
|
where
|
||||||
|
S: Into<Cow<'static, str>>,
|
||||||
|
{
|
||||||
|
PrettyTree::text(text).annotate(self.operator.clone())
|
||||||
|
}
|
||||||
|
/// Allocate a document containing the given text.
|
||||||
|
pub fn string<S>(&self, text: S) -> PrettyTree
|
||||||
|
where
|
||||||
|
S: Into<Cow<'static, str>>,
|
||||||
|
{
|
||||||
|
PrettyTree::text(text).annotate(self.string.clone())
|
||||||
|
}
|
||||||
|
/// Allocate a document containing the given text.
|
||||||
|
pub fn annotation<S>(&self, text: S) -> PrettyTree
|
||||||
|
where
|
||||||
|
S: Into<Cow<'static, str>>,
|
||||||
|
{
|
||||||
|
PrettyTree::text(text).annotate(self.macros.clone())
|
||||||
|
}
|
||||||
|
/// Allocate a document containing the given text.
|
||||||
|
pub fn number<S>(&self, text: S) -> PrettyTree
|
||||||
|
where
|
||||||
|
S: Into<Cow<'static, str>>,
|
||||||
|
{
|
||||||
|
PrettyTree::text(text).annotate(self.number.clone())
|
||||||
|
}
|
||||||
|
/// Allocate a document containing the given text.
|
||||||
|
pub fn structure<S>(&self, text: S) -> PrettyTree
|
||||||
|
where
|
||||||
|
S: Into<Cow<'static, str>>,
|
||||||
|
{
|
||||||
|
PrettyTree::text(text).annotate(self.structure.clone())
|
||||||
|
}
|
||||||
|
/// Allocate a document containing the given text.
|
||||||
|
pub fn variant<S>(&self, text: S) -> PrettyTree
|
||||||
|
where
|
||||||
|
S: Into<Cow<'static, str>>,
|
||||||
|
{
|
||||||
|
PrettyTree::text(text).annotate(self.variant.clone())
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Allocate a document containing the given text.
|
||||||
|
pub fn interface<S>(&self, text: S) -> PrettyTree
|
||||||
|
where
|
||||||
|
S: Into<Cow<'static, str>>,
|
||||||
|
{
|
||||||
|
PrettyTree::text(text).annotate(self.interface.clone())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl PrettyProvider {
|
||||||
|
/// Allocate a document containing the given text.
|
||||||
|
///
|
||||||
|
/// # Examples
|
||||||
|
///
|
||||||
|
/// ```
|
||||||
|
/// # use pretty_print::PrettyProvider;
|
||||||
|
/// let theme = PrettyProvider::new(80);
|
||||||
|
/// theme.join(vec!["a", "b", "c"], ", ");
|
||||||
|
/// ```
|
||||||
|
pub fn join<I, T1, T2>(&self, iter: I, joint: T2) -> PrettyTree
|
||||||
|
where
|
||||||
|
I: IntoIterator<Item = T1>,
|
||||||
|
T1: PrettyPrint,
|
||||||
|
T2: PrettyPrint,
|
||||||
|
{
|
||||||
|
PrettyTree::join(iter.into_iter().map(|x| x.pretty(self)), joint.pretty(self))
|
||||||
|
}
|
||||||
|
/// Allocate a document containing the given text.
|
||||||
|
///
|
||||||
|
/// # Examples
|
||||||
|
///
|
||||||
|
/// ```
|
||||||
|
/// # use pretty_print::PrettyProvider;
|
||||||
|
/// let theme = PrettyProvider::new(80);
|
||||||
|
/// theme.join(&["a", "b", "c"], ", ");
|
||||||
|
/// ```
|
||||||
|
pub fn join_slice<I, T>(&self, iter: &[I], joint: T) -> PrettyTree
|
||||||
|
where
|
||||||
|
I: PrettyPrint,
|
||||||
|
T: PrettyPrint,
|
||||||
|
{
|
||||||
|
PrettyTree::join(iter.iter().map(|s| s.pretty(self)), joint.pretty(self))
|
||||||
|
}
|
||||||
|
/// Allocate a document containing the given text.
|
||||||
|
///
|
||||||
|
/// # Examples
|
||||||
|
///
|
||||||
|
/// ```
|
||||||
|
/// # use pretty_print::PrettyProvider;
|
||||||
|
/// let theme = PrettyProvider::new(80);
|
||||||
|
/// theme.concat(vec!["1", "2", "3"]);
|
||||||
|
/// ```
|
||||||
|
pub fn concat<I, T>(&self, iter: I) -> PrettyTree
|
||||||
|
where
|
||||||
|
I: IntoIterator<Item = T>,
|
||||||
|
T: PrettyPrint,
|
||||||
|
{
|
||||||
|
PrettyTree::concat(iter.into_iter().map(|x| x.pretty(self)))
|
||||||
|
}
|
||||||
|
/// Allocate a document containing the given text.
|
||||||
|
///
|
||||||
|
/// # Examples
|
||||||
|
///
|
||||||
|
/// ```
|
||||||
|
/// # use pretty_print::PrettyProvider;
|
||||||
|
/// let theme = PrettyProvider::new(80);
|
||||||
|
/// theme.concat_slice(&["1", "2", "3"]);
|
||||||
|
/// ```
|
||||||
|
pub fn concat_slice<T>(&self, iter: &[T]) -> PrettyTree
|
||||||
|
where
|
||||||
|
T: PrettyPrint,
|
||||||
|
{
|
||||||
|
PrettyTree::concat(iter.iter().map(|s| s.pretty(self)))
|
||||||
|
}
|
||||||
|
}
|
||||||
372
pretty-print/projects/pretty-print/src/render/mod.rs
Normal file
372
pretty-print/projects/pretty-print/src/render/mod.rs
Normal file
|
|
@ -0,0 +1,372 @@
|
||||||
|
use crate::{BufferWrite, PrettyTree};
|
||||||
|
use alloc::{rc::Rc, vec, vec::Vec};
|
||||||
|
use color_ansi::AnsiStyle;
|
||||||
|
use core::fmt::{Debug, Display, Formatter};
|
||||||
|
|
||||||
|
#[cfg(feature = "std")]
|
||||||
|
pub mod write_io;
|
||||||
|
|
||||||
|
pub mod write_fmt;
|
||||||
|
|
||||||
|
/// Trait representing the operations necessary to render a document
|
||||||
|
pub trait Render {
|
||||||
|
/// The type of the output
|
||||||
|
type Error;
|
||||||
|
|
||||||
|
/// Write a string to the output
|
||||||
|
fn write_str(&mut self, s: &str) -> Result<usize, Self::Error>;
|
||||||
|
|
||||||
|
/// Write a character to the output
|
||||||
|
fn write_str_all(&mut self, mut s: &str) -> Result<(), Self::Error> {
|
||||||
|
while !s.is_empty() {
|
||||||
|
let count = self.write_str(s)?;
|
||||||
|
s = &s[count..];
|
||||||
|
}
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Write a character to the output
|
||||||
|
fn fail_doc(&self) -> Self::Error;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// The given text, which must not contain line breaks.
|
||||||
|
#[derive(Debug)]
|
||||||
|
pub struct PrettyFormatter<'a> {
|
||||||
|
tree: &'a PrettyTree,
|
||||||
|
width: usize,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<'a> Display for PrettyFormatter<'a> {
|
||||||
|
fn fmt(&self, f: &mut Formatter<'_>) -> core::fmt::Result {
|
||||||
|
self.tree.render_fmt(self.width, f)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl PrettyTree {
|
||||||
|
/// Returns a value which implements `std::fmt::Display`
|
||||||
|
///
|
||||||
|
/// ```
|
||||||
|
/// use pretty::{BoxDoc, Doc};
|
||||||
|
/// let doc =
|
||||||
|
/// BoxDoc::<()>::group(BoxDoc::text("hello").append(Doc::line()).append(Doc::text("world")));
|
||||||
|
/// assert_eq!(format!("{}", doc.pretty(80)), "hello world");
|
||||||
|
/// ```
|
||||||
|
#[inline]
|
||||||
|
pub fn pretty(&self, width: usize) -> PrettyFormatter<'_> {
|
||||||
|
PrettyFormatter { tree: self, width }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Trait representing the operations necessary to write an annotated document.
|
||||||
|
pub trait RenderAnnotated: Render {
|
||||||
|
/// Push an annotation onto the stack
|
||||||
|
fn push_annotation(&mut self, annotation: Rc<AnsiStyle>) -> Result<(), Self::Error>;
|
||||||
|
/// Pop an annotation from the stack
|
||||||
|
fn pop_annotation(&mut self) -> Result<(), Self::Error>;
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug)]
|
||||||
|
enum Annotation<A> {
|
||||||
|
Push(Rc<A>),
|
||||||
|
Pop,
|
||||||
|
}
|
||||||
|
|
||||||
|
macro_rules! make_spaces {
|
||||||
|
() => { "" };
|
||||||
|
($s: tt $($t: tt)*) => { concat!(" ", make_spaces!($($t)*)) };
|
||||||
|
}
|
||||||
|
|
||||||
|
pub(crate) const SPACES: &str = make_spaces!(,,,,,,,,,,);
|
||||||
|
|
||||||
|
fn append_docs2(ldoc: Rc<PrettyTree>, rdoc: Rc<PrettyTree>, mut consumer: impl FnMut(Rc<PrettyTree>)) -> Rc<PrettyTree> {
|
||||||
|
let d = append_docs(rdoc, &mut consumer);
|
||||||
|
consumer(d);
|
||||||
|
append_docs(ldoc, &mut consumer)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn append_docs(mut doc: Rc<PrettyTree>, consumer: &mut impl FnMut(Rc<PrettyTree>)) -> Rc<PrettyTree> {
|
||||||
|
loop {
|
||||||
|
// Since appended documents often appear in sequence on the left side we
|
||||||
|
// gain a slight performance increase by batching these pushes (avoiding
|
||||||
|
// to push and directly pop `Append` documents)
|
||||||
|
match doc.as_ref() {
|
||||||
|
PrettyTree::Append { lhs, rhs } => {
|
||||||
|
let d = append_docs(rhs.clone(), consumer);
|
||||||
|
consumer(d);
|
||||||
|
doc = lhs.clone();
|
||||||
|
}
|
||||||
|
_ => return doc,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn best<W>(doc: Rc<PrettyTree>, width: usize, out: &mut W) -> Result<(), W::Error>
|
||||||
|
where
|
||||||
|
W: RenderAnnotated,
|
||||||
|
W: ?Sized,
|
||||||
|
{
|
||||||
|
Best {
|
||||||
|
pos: 0,
|
||||||
|
back_cmds: vec![RenderCommand { indent: 0, mode: Mode::Break, node: doc }],
|
||||||
|
front_cmds: vec![],
|
||||||
|
annotation_levels: vec![],
|
||||||
|
width,
|
||||||
|
}
|
||||||
|
.best(0, out)?;
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Clone, Copy, Debug, Eq, Ord, PartialEq, PartialOrd)]
|
||||||
|
enum Mode {
|
||||||
|
Break,
|
||||||
|
Flat,
|
||||||
|
}
|
||||||
|
|
||||||
|
struct RenderCommand {
|
||||||
|
indent: usize,
|
||||||
|
mode: Mode,
|
||||||
|
node: Rc<PrettyTree>,
|
||||||
|
}
|
||||||
|
|
||||||
|
fn write_newline<W>(ind: usize, out: &mut W) -> Result<(), W::Error>
|
||||||
|
where
|
||||||
|
W: ?Sized + Render,
|
||||||
|
{
|
||||||
|
out.write_str_all("\n")?;
|
||||||
|
write_spaces(ind, out)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn write_spaces<W>(spaces: usize, out: &mut W) -> Result<(), W::Error>
|
||||||
|
where
|
||||||
|
W: ?Sized + Render,
|
||||||
|
{
|
||||||
|
let mut inserted = 0;
|
||||||
|
while inserted < spaces {
|
||||||
|
let insert = core::cmp::min(SPACES.len(), spaces - inserted);
|
||||||
|
inserted += out.write_str(&SPACES[..insert])?;
|
||||||
|
}
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
struct Best {
|
||||||
|
pos: usize,
|
||||||
|
back_cmds: Vec<RenderCommand>,
|
||||||
|
front_cmds: Vec<Rc<PrettyTree>>,
|
||||||
|
annotation_levels: Vec<usize>,
|
||||||
|
width: usize,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Best {
|
||||||
|
fn fitting(&mut self, next: Rc<PrettyTree>, mut pos: usize, ind: usize) -> bool {
|
||||||
|
let mut bidx = self.back_cmds.len();
|
||||||
|
self.front_cmds.clear(); // clear from previous calls from best
|
||||||
|
self.front_cmds.push(next);
|
||||||
|
let mut mode = Mode::Flat;
|
||||||
|
|
||||||
|
loop {
|
||||||
|
let mut doc = match self.front_cmds.pop() {
|
||||||
|
None => {
|
||||||
|
if bidx == 0 {
|
||||||
|
// All commands have been processed
|
||||||
|
return true;
|
||||||
|
} else {
|
||||||
|
bidx -= 1;
|
||||||
|
mode = Mode::Break;
|
||||||
|
self.back_cmds[bidx].node.clone()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Some(cmd) => cmd,
|
||||||
|
};
|
||||||
|
|
||||||
|
loop {
|
||||||
|
match doc.as_ref() {
|
||||||
|
PrettyTree::Nil => {}
|
||||||
|
PrettyTree::Append { lhs, rhs } => {
|
||||||
|
doc = append_docs2(lhs.clone(), rhs.clone(), |send| self.front_cmds.push(send));
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
// Newlines inside the group makes it not fit, but those outside lets it
|
||||||
|
// fit on the current line
|
||||||
|
PrettyTree::Hardline => return mode == Mode::Break,
|
||||||
|
PrettyTree::RenderLength { length: len, body: _ } => {
|
||||||
|
pos += len;
|
||||||
|
if pos > self.width {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
PrettyTree::StaticText(str) => {
|
||||||
|
pos += str.len();
|
||||||
|
if pos > self.width {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
PrettyTree::Text(ref str) => {
|
||||||
|
pos += str.len();
|
||||||
|
if pos > self.width {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
PrettyTree::MaybeInline { block: flat, inline } => {
|
||||||
|
doc = match mode {
|
||||||
|
Mode::Break => flat.clone(),
|
||||||
|
Mode::Flat => inline.clone(),
|
||||||
|
};
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
PrettyTree::Column { invoke: function } => {
|
||||||
|
doc = Rc::new(function(pos));
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
PrettyTree::Nesting { invoke: function } => {
|
||||||
|
doc = Rc::new(function(ind));
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
PrettyTree::Nest { space: _, doc: next }
|
||||||
|
| PrettyTree::Group { items: next }
|
||||||
|
| PrettyTree::Annotated { style: _, body: next }
|
||||||
|
| PrettyTree::Union { lhs: _, rhs: next } => {
|
||||||
|
doc = next.clone();
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
PrettyTree::Fail => return false,
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn best<W>(&mut self, top: usize, out: &mut W) -> Result<bool, W::Error>
|
||||||
|
where
|
||||||
|
W: RenderAnnotated,
|
||||||
|
W: ?Sized,
|
||||||
|
{
|
||||||
|
let mut fits = true;
|
||||||
|
|
||||||
|
while top < self.back_cmds.len() {
|
||||||
|
let mut cmd = self.back_cmds.pop().unwrap();
|
||||||
|
loop {
|
||||||
|
let RenderCommand { indent: ind, mode, node } = cmd;
|
||||||
|
match node.as_ref() {
|
||||||
|
PrettyTree::Nil => {}
|
||||||
|
PrettyTree::Append { lhs, rhs } => {
|
||||||
|
cmd.node = append_docs2(lhs.clone(), rhs.clone(), |send| {
|
||||||
|
self.back_cmds.push(RenderCommand { indent: ind, mode, node: send })
|
||||||
|
});
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
PrettyTree::MaybeInline { block, inline } => {
|
||||||
|
cmd.node = match mode {
|
||||||
|
Mode::Break => block.clone(),
|
||||||
|
Mode::Flat => inline.clone(),
|
||||||
|
};
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
PrettyTree::Group { items } => {
|
||||||
|
match mode {
|
||||||
|
Mode::Break if self.fitting(items.clone(), self.pos, ind) => {
|
||||||
|
cmd.mode = Mode::Flat;
|
||||||
|
}
|
||||||
|
_ => {}
|
||||||
|
}
|
||||||
|
cmd.node = items.clone();
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
PrettyTree::Nest { space, doc } => {
|
||||||
|
// Once https://doc.rust-lang.org/std/primitive.usize.html#method.saturating_add_signed is stable
|
||||||
|
// this can be replaced
|
||||||
|
let new_ind = if *space >= 0 {
|
||||||
|
ind.saturating_add(*space as usize)
|
||||||
|
} else {
|
||||||
|
ind.saturating_sub(space.unsigned_abs())
|
||||||
|
};
|
||||||
|
cmd = RenderCommand { indent: new_ind, mode, node: doc.clone() };
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
PrettyTree::Hardline => {
|
||||||
|
// The next document may have different indentation so we should use it if we can
|
||||||
|
match self.back_cmds.pop() {
|
||||||
|
Some(next) => {
|
||||||
|
write_newline(next.indent, out)?;
|
||||||
|
self.pos = next.indent;
|
||||||
|
cmd = next;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
None => {
|
||||||
|
write_newline(ind, out)?;
|
||||||
|
self.pos = ind;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
PrettyTree::RenderLength { length: len, body: doc } => match doc.as_ref() {
|
||||||
|
PrettyTree::Text(s) => {
|
||||||
|
out.write_str_all(s)?;
|
||||||
|
self.pos += len;
|
||||||
|
fits &= self.pos <= self.width;
|
||||||
|
}
|
||||||
|
PrettyTree::StaticText(s) => {
|
||||||
|
out.write_str_all(s)?;
|
||||||
|
self.pos += len;
|
||||||
|
fits &= self.pos <= self.width;
|
||||||
|
}
|
||||||
|
_ => unreachable!(),
|
||||||
|
},
|
||||||
|
PrettyTree::Text(ref s) => {
|
||||||
|
out.write_str_all(s)?;
|
||||||
|
self.pos += s.len();
|
||||||
|
fits &= self.pos <= self.width;
|
||||||
|
}
|
||||||
|
PrettyTree::StaticText(s) => {
|
||||||
|
out.write_str_all(s)?;
|
||||||
|
self.pos += s.len();
|
||||||
|
fits &= self.pos <= self.width;
|
||||||
|
}
|
||||||
|
PrettyTree::Annotated { style: color, body: doc } => {
|
||||||
|
out.push_annotation(color.clone())?;
|
||||||
|
self.annotation_levels.push(self.back_cmds.len());
|
||||||
|
cmd.node = doc.clone();
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
PrettyTree::Union { lhs: left, rhs: right } => {
|
||||||
|
let pos = self.pos;
|
||||||
|
let annotation_levels = self.annotation_levels.len();
|
||||||
|
let bcmds = self.back_cmds.len();
|
||||||
|
|
||||||
|
self.back_cmds.push(RenderCommand { indent: ind, mode, node: left.clone() });
|
||||||
|
|
||||||
|
let mut buffer = BufferWrite::new(0);
|
||||||
|
|
||||||
|
match self.best(bcmds, &mut buffer) {
|
||||||
|
Ok(true) => buffer.render(out)?,
|
||||||
|
Ok(false) | Err(_) => {
|
||||||
|
self.pos = pos;
|
||||||
|
self.back_cmds.truncate(bcmds);
|
||||||
|
self.annotation_levels.truncate(annotation_levels);
|
||||||
|
cmd.node = right.clone();
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
PrettyTree::Column { invoke: column } => {
|
||||||
|
cmd.node = Rc::new(column(self.pos));
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
PrettyTree::Nesting { invoke: nesting } => {
|
||||||
|
cmd.node = Rc::new(nesting(self.pos));
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
PrettyTree::Fail => return Err(out.fail_doc()),
|
||||||
|
}
|
||||||
|
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
while self.annotation_levels.last() == Some(&self.back_cmds.len()) {
|
||||||
|
self.annotation_levels.pop();
|
||||||
|
out.pop_annotation()?;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Ok(fits)
|
||||||
|
}
|
||||||
|
}
|
||||||
121
pretty-print/projects/pretty-print/src/render/write_fmt.rs
Normal file
121
pretty-print/projects/pretty-print/src/render/write_fmt.rs
Normal file
|
|
@ -0,0 +1,121 @@
|
||||||
|
use crate::{render::Annotation, Render, RenderAnnotated};
|
||||||
|
use alloc::rc::Rc;
|
||||||
|
use color_ansi::AnsiStyle;
|
||||||
|
use core::fmt::{Debug, Formatter};
|
||||||
|
|
||||||
|
/// Writes to something implementing `std::fmt::Write`
|
||||||
|
pub struct FmtWrite<W> {
|
||||||
|
upstream: W,
|
||||||
|
}
|
||||||
|
/// Represents a terminal writer.
|
||||||
|
#[derive(Debug)]
|
||||||
|
pub struct BufferWrite {
|
||||||
|
buffer: String,
|
||||||
|
annotations: Vec<(usize, Annotation<AnsiStyle>)>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl BufferWrite {
|
||||||
|
/// Creates a new terminal writer.
|
||||||
|
pub fn new(capacity: usize) -> Self {
|
||||||
|
BufferWrite { buffer: String::with_capacity(capacity), annotations: Vec::new() }
|
||||||
|
}
|
||||||
|
/// Creates a new terminal writer.
|
||||||
|
pub fn render<W>(&mut self, render: &mut W) -> Result<(), W::Error>
|
||||||
|
where
|
||||||
|
W: RenderAnnotated,
|
||||||
|
W: ?Sized,
|
||||||
|
{
|
||||||
|
let mut start = 0;
|
||||||
|
for (end, annotation) in &self.annotations {
|
||||||
|
let s = &self.buffer[start..*end];
|
||||||
|
if !s.is_empty() {
|
||||||
|
render.write_str_all(s)?;
|
||||||
|
}
|
||||||
|
start = *end;
|
||||||
|
match annotation {
|
||||||
|
Annotation::Push(a) => render.push_annotation(a.clone())?,
|
||||||
|
Annotation::Pop => render.pop_annotation()?,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
let s = &self.buffer[start..];
|
||||||
|
if !s.is_empty() {
|
||||||
|
render.write_str_all(s)?;
|
||||||
|
}
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Render for BufferWrite {
|
||||||
|
type Error = core::fmt::Error;
|
||||||
|
|
||||||
|
fn write_str(&mut self, s: &str) -> Result<usize, Self::Error> {
|
||||||
|
self.buffer.push_str(s);
|
||||||
|
Ok(s.len())
|
||||||
|
}
|
||||||
|
|
||||||
|
fn write_str_all(&mut self, s: &str) -> Result<(), Self::Error> {
|
||||||
|
self.buffer.push_str(s);
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
fn fail_doc(&self) -> Self::Error {
|
||||||
|
core::fmt::Error::default()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<W> RenderAnnotated for FmtWrite<W>
|
||||||
|
where
|
||||||
|
W: core::fmt::Write,
|
||||||
|
{
|
||||||
|
fn push_annotation(&mut self, _: Rc<AnsiStyle>) -> Result<(), Self::Error> {
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
fn pop_annotation(&mut self) -> Result<(), Self::Error> {
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl RenderAnnotated for BufferWrite {
|
||||||
|
fn push_annotation(&mut self, annotation: Rc<AnsiStyle>) -> Result<(), Self::Error> {
|
||||||
|
self.annotations.push((self.buffer.len(), Annotation::Push(annotation)));
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
fn pop_annotation(&mut self) -> Result<(), Self::Error> {
|
||||||
|
self.annotations.push((self.buffer.len(), Annotation::Pop));
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<W> Debug for FmtWrite<W> {
|
||||||
|
fn fmt(&self, f: &mut Formatter<'_>) -> core::fmt::Result {
|
||||||
|
f.debug_struct("IoWrite").finish()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<W> FmtWrite<W> {
|
||||||
|
/// Create a new `FmtWrite` from something implementing `std::fmt::Write`
|
||||||
|
pub fn new(upstream: W) -> FmtWrite<W> {
|
||||||
|
FmtWrite { upstream }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<W> Render for FmtWrite<W>
|
||||||
|
where
|
||||||
|
W: core::fmt::Write,
|
||||||
|
{
|
||||||
|
type Error = core::fmt::Error;
|
||||||
|
|
||||||
|
fn write_str(&mut self, s: &str) -> Result<usize, core::fmt::Error> {
|
||||||
|
self.write_str_all(s).map(|_| s.len())
|
||||||
|
}
|
||||||
|
|
||||||
|
fn write_str_all(&mut self, s: &str) -> core::fmt::Result {
|
||||||
|
self.upstream.write_str(s)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn fail_doc(&self) -> Self::Error {
|
||||||
|
core::fmt::Error
|
||||||
|
}
|
||||||
|
}
|
||||||
114
pretty-print/projects/pretty-print/src/render/write_io.rs
Normal file
114
pretty-print/projects/pretty-print/src/render/write_io.rs
Normal file
|
|
@ -0,0 +1,114 @@
|
||||||
|
use crate::{Render, RenderAnnotated};
|
||||||
|
use alloc::rc::Rc;
|
||||||
|
use color_ansi::{AnsiAbility, AnsiStyle, AnsiWriter};
|
||||||
|
use core::fmt::{Debug, Formatter};
|
||||||
|
use std::io::{Error, ErrorKind, Write};
|
||||||
|
/// Represents a terminal writer.
|
||||||
|
pub struct TerminalWriter<W> {
|
||||||
|
color_stack: Vec<Rc<AnsiStyle>>,
|
||||||
|
upstream: AnsiWriter<W>,
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Writes to something implementing `std::io::Write`
|
||||||
|
pub struct IoWrite<W> {
|
||||||
|
upstream: W,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<W> Debug for IoWrite<W> {
|
||||||
|
fn fmt(&self, f: &mut Formatter<'_>) -> core::fmt::Result {
|
||||||
|
f.debug_struct("IoWrite").finish()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<W> IoWrite<W> {
|
||||||
|
/// Creates a new terminal writer.
|
||||||
|
pub fn new(upstream: W) -> IoWrite<W> {
|
||||||
|
IoWrite { upstream }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(feature = "std")]
|
||||||
|
impl<W> Render for IoWrite<W>
|
||||||
|
where
|
||||||
|
W: std::io::Write,
|
||||||
|
{
|
||||||
|
type Error = std::io::Error;
|
||||||
|
|
||||||
|
fn write_str(&mut self, s: &str) -> std::io::Result<usize> {
|
||||||
|
self.upstream.write(s.as_bytes())
|
||||||
|
}
|
||||||
|
|
||||||
|
fn write_str_all(&mut self, s: &str) -> std::io::Result<()> {
|
||||||
|
self.upstream.write_all(s.as_bytes())
|
||||||
|
}
|
||||||
|
|
||||||
|
fn fail_doc(&self) -> Self::Error {
|
||||||
|
std::io::Error::new(std::io::ErrorKind::Other, "Document failed to render")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(feature = "std")]
|
||||||
|
impl<W> RenderAnnotated for IoWrite<W>
|
||||||
|
where
|
||||||
|
W: std::io::Write,
|
||||||
|
{
|
||||||
|
fn push_annotation(&mut self, _: Rc<AnsiStyle>) -> Result<(), Self::Error> {
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
fn pop_annotation(&mut self) -> Result<(), Self::Error> {
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<W> Debug for TerminalWriter<W> {
|
||||||
|
fn fmt(&self, f: &mut Formatter<'_>) -> core::fmt::Result {
|
||||||
|
f.debug_struct("TerminalWriter").finish()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<W: Write> TerminalWriter<W> {
|
||||||
|
/// Creates a new terminal writer.
|
||||||
|
pub fn new(upstream: W) -> Self {
|
||||||
|
TerminalWriter { color_stack: Vec::new(), upstream: AnsiWriter::new(upstream) }
|
||||||
|
}
|
||||||
|
/// Creates a new terminal writer with a specific color.
|
||||||
|
pub fn with_color(mut self, color: AnsiAbility) -> Self {
|
||||||
|
self.upstream.set_ability(color);
|
||||||
|
self
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<W> Render for TerminalWriter<W>
|
||||||
|
where
|
||||||
|
W: Write,
|
||||||
|
{
|
||||||
|
type Error = Error;
|
||||||
|
|
||||||
|
fn write_str(&mut self, s: &str) -> std::io::Result<usize> {
|
||||||
|
self.upstream.write(s.as_bytes())
|
||||||
|
}
|
||||||
|
|
||||||
|
fn write_str_all(&mut self, s: &str) -> std::io::Result<()> {
|
||||||
|
self.upstream.write_all(s.as_bytes())
|
||||||
|
}
|
||||||
|
|
||||||
|
fn fail_doc(&self) -> Self::Error {
|
||||||
|
Error::new(ErrorKind::Other, "Document failed to render")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<W: Write> RenderAnnotated for TerminalWriter<W> {
|
||||||
|
fn push_annotation(&mut self, color: Rc<AnsiStyle>) -> Result<(), Self::Error> {
|
||||||
|
self.color_stack.push(color.clone());
|
||||||
|
self.upstream.set_style(&color)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn pop_annotation(&mut self) -> Result<(), Self::Error> {
|
||||||
|
self.color_stack.pop();
|
||||||
|
match self.color_stack.last() {
|
||||||
|
Some(previous) => self.upstream.set_style(previous),
|
||||||
|
None => self.upstream.reset_style(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
34
pretty-print/projects/pretty-print/src/traits/mod.rs
Normal file
34
pretty-print/projects/pretty-print/src/traits/mod.rs
Normal file
|
|
@ -0,0 +1,34 @@
|
||||||
|
use crate::{providers::PrettyProvider, PrettyTree};
|
||||||
|
use alloc::string::String;
|
||||||
|
|
||||||
|
pub mod printer;
|
||||||
|
|
||||||
|
/// The `PrettyPrint` trait is implemented by types that can be pretty-printed.
|
||||||
|
pub trait PrettyBuilder {
|
||||||
|
/// Acts as `self` when laid out on multiple lines and acts as `that` when laid out on a single line.
|
||||||
|
///
|
||||||
|
/// ```
|
||||||
|
/// use pretty::{Arena, DocAllocator};
|
||||||
|
///
|
||||||
|
/// let arena = Arena::<()>::new();
|
||||||
|
/// let body = arena.line().append("x");
|
||||||
|
/// let doc = arena
|
||||||
|
/// .text("let")
|
||||||
|
/// .append(arena.line())
|
||||||
|
/// .append("x")
|
||||||
|
/// .group()
|
||||||
|
/// .append(body.clone().flat_alt(arena.line().append("in").append(body)))
|
||||||
|
/// .group();
|
||||||
|
///
|
||||||
|
/// assert_eq!(doc.1.pretty(100).to_string(), "let x in x");
|
||||||
|
/// assert_eq!(doc.1.pretty(8).to_string(), "let x\nx");
|
||||||
|
/// ```
|
||||||
|
fn flat_alt<E>(self, inline: E) -> PrettyTree
|
||||||
|
where
|
||||||
|
E: Into<PrettyTree>;
|
||||||
|
/// Acts as `self` when laid out on a single line and acts as `that` when laid out on multiple lines.
|
||||||
|
fn indent(self, indent: usize) -> PrettyTree;
|
||||||
|
|
||||||
|
/// Increase the indentation level of this document.
|
||||||
|
fn nest(self, offset: isize) -> PrettyTree;
|
||||||
|
}
|
||||||
37
pretty-print/projects/pretty-print/src/traits/printer.rs
Normal file
37
pretty-print/projects/pretty-print/src/traits/printer.rs
Normal file
|
|
@ -0,0 +1,37 @@
|
||||||
|
use super::*;
|
||||||
|
|
||||||
|
/// Marker trait for types that can be pretty printed.
|
||||||
|
pub trait PrettyPrint {
|
||||||
|
/// Build a pretty tree for this type.
|
||||||
|
fn pretty(&self, theme: &PrettyProvider) -> PrettyTree;
|
||||||
|
/// Get a pretty string for this type.
|
||||||
|
fn pretty_string(&self, theme: &PrettyProvider) -> String {
|
||||||
|
let mut buffer = String::new();
|
||||||
|
if let Err(e) = self.pretty(&theme).render_fmt(theme.get_width(), &mut buffer) {
|
||||||
|
panic!("Error: {}", e);
|
||||||
|
}
|
||||||
|
buffer
|
||||||
|
}
|
||||||
|
/// Print a pretty string for this type.
|
||||||
|
fn pretty_colorful(&self, theme: &PrettyProvider) -> String {
|
||||||
|
let mut buffer = vec![];
|
||||||
|
if let Err(e) = self.pretty(&theme).render_colored(theme.get_width(), &mut buffer) {
|
||||||
|
panic!("Error: {}", e);
|
||||||
|
}
|
||||||
|
match String::from_utf8(buffer) {
|
||||||
|
Ok(s) => s,
|
||||||
|
Err(e) => panic!("Error: {}", e),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl PrettyPrint for PrettyTree {
|
||||||
|
fn pretty(&self, _: &PrettyProvider) -> PrettyTree {
|
||||||
|
self.clone()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
impl PrettyPrint for &'static str {
|
||||||
|
fn pretty(&self, _: &PrettyProvider) -> PrettyTree {
|
||||||
|
PrettyTree::StaticText(*self)
|
||||||
|
}
|
||||||
|
}
|
||||||
89
pretty-print/projects/pretty-print/src/tree/display.rs
Normal file
89
pretty-print/projects/pretty-print/src/tree/display.rs
Normal file
|
|
@ -0,0 +1,89 @@
|
||||||
|
use super::*;
|
||||||
|
|
||||||
|
impl Default for PrettyTree {
|
||||||
|
fn default() -> Self {
|
||||||
|
Self::Nil
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Clone for PrettyTree {
|
||||||
|
fn clone(&self) -> Self {
|
||||||
|
match self {
|
||||||
|
Self::Nil => Self::Nil,
|
||||||
|
Self::Hardline => Self::Hardline,
|
||||||
|
Self::Text(s) => Self::Text(s.clone()),
|
||||||
|
Self::StaticText(s) => Self::StaticText(*s),
|
||||||
|
Self::Annotated { style: color, body: doc } => Self::Annotated { style: color.clone(), body: doc.clone() },
|
||||||
|
Self::Append { lhs, rhs } => Self::Append { lhs: lhs.clone(), rhs: rhs.clone() },
|
||||||
|
Self::Group { items } => Self::Group { items: items.clone() },
|
||||||
|
Self::MaybeInline { block, inline } => Self::MaybeInline { block: block.clone(), inline: inline.clone() },
|
||||||
|
Self::Nest { space, doc } => Self::Nest { space: *space, doc: doc.clone() },
|
||||||
|
Self::RenderLength { length: len, body: doc } => Self::RenderLength { length: *len, body: doc.clone() },
|
||||||
|
Self::Union { lhs: left, rhs: right } => Self::Union { lhs: left.clone(), rhs: right.clone() },
|
||||||
|
Self::Column { invoke: column } => Self::Column { invoke: column.clone() },
|
||||||
|
Self::Nesting { invoke: nesting } => Self::Nesting { invoke: nesting.clone() },
|
||||||
|
Self::Fail => Self::Fail,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Debug for PrettyTree {
|
||||||
|
fn fmt(&self, f: &mut Formatter<'_>) -> core::fmt::Result {
|
||||||
|
let is_line = |doc: &PrettyTree| match doc {
|
||||||
|
PrettyTree::MaybeInline { block: flat, inline: alt } => {
|
||||||
|
matches!((&**flat, &**alt), (PrettyTree::Hardline, PrettyTree::StaticText(" ")))
|
||||||
|
}
|
||||||
|
_ => false,
|
||||||
|
};
|
||||||
|
let is_line_ = |doc: &PrettyTree| match doc {
|
||||||
|
PrettyTree::MaybeInline { block: flat, inline: alt } => {
|
||||||
|
matches!((&**flat, &**alt), (PrettyTree::Hardline, PrettyTree::Nil))
|
||||||
|
}
|
||||||
|
_ => false,
|
||||||
|
};
|
||||||
|
match self {
|
||||||
|
PrettyTree::Nil => f.debug_tuple("Nil").finish(),
|
||||||
|
PrettyTree::Append { lhs: _, rhs: _ } => {
|
||||||
|
let mut f = f.debug_list();
|
||||||
|
append_docs(self, &mut |doc| {
|
||||||
|
f.entry(doc);
|
||||||
|
});
|
||||||
|
f.finish()
|
||||||
|
}
|
||||||
|
_ if is_line(self) => f.debug_tuple("Line").finish(),
|
||||||
|
_ if is_line_(self) => f.debug_tuple("Line?").finish(),
|
||||||
|
PrettyTree::MaybeInline { block, inline } => f.debug_tuple("FlatAlt").field(block).field(inline).finish(),
|
||||||
|
PrettyTree::Group { items } => {
|
||||||
|
if is_line(self) {
|
||||||
|
return f.debug_tuple("SoftLine").finish();
|
||||||
|
}
|
||||||
|
if is_line_(self) {
|
||||||
|
return f.debug_tuple("SoftLine?").finish();
|
||||||
|
}
|
||||||
|
f.debug_tuple("Group").field(items).finish()
|
||||||
|
}
|
||||||
|
PrettyTree::Nest { space, doc } => f.debug_tuple("Nest").field(&space).field(doc).finish(),
|
||||||
|
PrettyTree::Hardline => f.debug_tuple("Hardline").finish(),
|
||||||
|
PrettyTree::RenderLength { body: doc, .. } => doc.fmt(f),
|
||||||
|
PrettyTree::Text(s) => Debug::fmt(s, f),
|
||||||
|
PrettyTree::StaticText(s) => Debug::fmt(s, f),
|
||||||
|
PrettyTree::Annotated { style: color, body: doc } => f.debug_tuple("Annotated").field(color).field(doc).finish(),
|
||||||
|
PrettyTree::Union { lhs: left, rhs: right } => f.debug_tuple("Union").field(left).field(right).finish(),
|
||||||
|
PrettyTree::Column { .. } => f.debug_tuple("Column(..)").finish(),
|
||||||
|
PrettyTree::Nesting { .. } => f.debug_tuple("Nesting(..)").finish(),
|
||||||
|
PrettyTree::Fail => f.debug_tuple("Fail").finish(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn append_docs(mut doc: &PrettyTree, consumer: &mut impl FnMut(&PrettyTree)) {
|
||||||
|
loop {
|
||||||
|
match doc {
|
||||||
|
PrettyTree::Append { lhs, rhs } => {
|
||||||
|
append_docs(lhs, consumer);
|
||||||
|
doc = rhs;
|
||||||
|
}
|
||||||
|
_ => break consumer(doc),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
50
pretty-print/projects/pretty-print/src/tree/into.rs
Normal file
50
pretty-print/projects/pretty-print/src/tree/into.rs
Normal file
|
|
@ -0,0 +1,50 @@
|
||||||
|
use super::*;
|
||||||
|
|
||||||
|
impl<T> Add<T> for PrettyTree
|
||||||
|
where
|
||||||
|
T: Into<Self>,
|
||||||
|
{
|
||||||
|
type Output = Self;
|
||||||
|
fn add(self, rhs: T) -> Self::Output {
|
||||||
|
self.append(rhs.into())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<T> AddAssign<T> for PrettyTree
|
||||||
|
where
|
||||||
|
T: Into<Self>,
|
||||||
|
{
|
||||||
|
fn add_assign(&mut self, rhs: T) {
|
||||||
|
*self = self.clone().append(rhs.into());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<T> From<Option<T>> for PrettyTree
|
||||||
|
where
|
||||||
|
Self: From<T>,
|
||||||
|
{
|
||||||
|
fn from(x: Option<T>) -> Self {
|
||||||
|
match x {
|
||||||
|
Some(x) => x.into(),
|
||||||
|
None => Self::Nil,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl From<()> for PrettyTree {
|
||||||
|
fn from(_: ()) -> Self {
|
||||||
|
Self::Nil
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl From<&'static str> for PrettyTree {
|
||||||
|
fn from(s: &'static str) -> Self {
|
||||||
|
Self::StaticText(s)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl From<String> for PrettyTree {
|
||||||
|
fn from(s: String) -> Self {
|
||||||
|
Self::Text(Rc::from(s))
|
||||||
|
}
|
||||||
|
}
|
||||||
449
pretty-print/projects/pretty-print/src/tree/mod.rs
Normal file
449
pretty-print/projects/pretty-print/src/tree/mod.rs
Normal file
|
|
@ -0,0 +1,449 @@
|
||||||
|
use crate::{helpers::PrettySequence, render, FmtWrite, PrettyBuilder, RenderAnnotated};
|
||||||
|
use alloc::{borrow::Cow, rc::Rc, string::String};
|
||||||
|
use color_ansi::AnsiStyle;
|
||||||
|
use core::{
|
||||||
|
fmt::{Debug, Formatter},
|
||||||
|
ops::{Add, AddAssign},
|
||||||
|
};
|
||||||
|
use std::io::Write;
|
||||||
|
use unicode_segmentation::UnicodeSegmentation;
|
||||||
|
|
||||||
|
mod display;
|
||||||
|
mod into;
|
||||||
|
|
||||||
|
/// The concrete document type. This type is not meant to be used directly. Instead use the static
|
||||||
|
/// functions on `Doc` or the methods on an `DocAllocator`.
|
||||||
|
///
|
||||||
|
/// The `T` parameter is used to abstract over pointers to `Doc`. See `RefDoc` and `BoxDoc` for how
|
||||||
|
/// it is used
|
||||||
|
pub enum PrettyTree {
|
||||||
|
/// Nothing to show
|
||||||
|
Nil,
|
||||||
|
/// A hard line break
|
||||||
|
Hardline,
|
||||||
|
/// A dynamic text document, all newlines are hard line breaks
|
||||||
|
Text(Rc<str>),
|
||||||
|
/// A static text document, all newlines are hard line breaks
|
||||||
|
StaticText(&'static str),
|
||||||
|
/// A document with ansi styles
|
||||||
|
Annotated {
|
||||||
|
/// The style to use for the text
|
||||||
|
style: Rc<AnsiStyle>,
|
||||||
|
/// The text to display
|
||||||
|
body: Rc<Self>,
|
||||||
|
},
|
||||||
|
/// Concatenates two documents
|
||||||
|
Append {
|
||||||
|
/// The first document
|
||||||
|
lhs: Rc<Self>,
|
||||||
|
/// The second document
|
||||||
|
rhs: Rc<Self>,
|
||||||
|
},
|
||||||
|
/// Concatenates two documents with a space in between
|
||||||
|
Group {
|
||||||
|
/// The first document
|
||||||
|
items: Rc<Self>,
|
||||||
|
},
|
||||||
|
/// Concatenates two documents with a line in between
|
||||||
|
MaybeInline {
|
||||||
|
/// The first document
|
||||||
|
block: Rc<Self>,
|
||||||
|
/// The second document
|
||||||
|
inline: Rc<Self>,
|
||||||
|
},
|
||||||
|
/// Concatenates two documents with a line in between
|
||||||
|
Nest {
|
||||||
|
/// The first document
|
||||||
|
space: isize,
|
||||||
|
/// The second document
|
||||||
|
doc: Rc<Self>,
|
||||||
|
},
|
||||||
|
/// Stores the length of a string document that is not just ascii
|
||||||
|
RenderLength {
|
||||||
|
/// The length of the string
|
||||||
|
length: usize,
|
||||||
|
/// The document
|
||||||
|
body: Rc<Self>,
|
||||||
|
},
|
||||||
|
/// Concatenates two documents with a line in between
|
||||||
|
Union {
|
||||||
|
/// The first document
|
||||||
|
lhs: Rc<Self>,
|
||||||
|
/// The second document
|
||||||
|
rhs: Rc<Self>,
|
||||||
|
},
|
||||||
|
/// Concatenates two documents with a line in between
|
||||||
|
Column {
|
||||||
|
/// The first document
|
||||||
|
invoke: Rc<dyn Fn(usize) -> Self>,
|
||||||
|
},
|
||||||
|
/// Concatenates two documents with a line in between
|
||||||
|
Nesting {
|
||||||
|
/// The first document
|
||||||
|
invoke: Rc<dyn Fn(usize) -> Self>,
|
||||||
|
},
|
||||||
|
/// Concatenates two documents with a line in between
|
||||||
|
Fail,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[allow(non_upper_case_globals)]
|
||||||
|
impl PrettyTree {
|
||||||
|
/// A hard line break
|
||||||
|
pub const Space: Self = PrettyTree::StaticText(" ");
|
||||||
|
/// A line acts like a `\n` but behaves like `space` if it is grouped on a single line.
|
||||||
|
#[inline]
|
||||||
|
pub fn line_or_space() -> Self {
|
||||||
|
Self::Hardline.flat_alt(Self::Space).into()
|
||||||
|
}
|
||||||
|
/// A line acts like `\n` but behaves like `nil` if it is grouped on a single line.
|
||||||
|
#[inline]
|
||||||
|
pub fn line_or_comma() -> Self {
|
||||||
|
Self::Hardline.flat_alt(PrettyTree::StaticText(", ")).into()
|
||||||
|
}
|
||||||
|
/// Acts like `line` but behaves like `nil` if grouped on a single line
|
||||||
|
#[inline]
|
||||||
|
pub fn line_or_nil() -> Self {
|
||||||
|
Self::Hardline.flat_alt(Self::Nil).into()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl PrettyTree {
|
||||||
|
/// The given text, which must not contain line breaks.
|
||||||
|
#[inline]
|
||||||
|
pub fn text<U: Into<Cow<'static, str>>>(data: U) -> Self {
|
||||||
|
match data.into() {
|
||||||
|
Cow::Borrowed(s) => PrettyTree::StaticText(s),
|
||||||
|
Cow::Owned(s) => PrettyTree::Text(Rc::from(s)),
|
||||||
|
}
|
||||||
|
.with_utf8_len()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl PrettyTree {
|
||||||
|
/// Writes a rendered document to a `std::io::Write` object.
|
||||||
|
#[inline]
|
||||||
|
#[cfg(feature = "std")]
|
||||||
|
pub fn render<W>(&self, width: usize, out: &mut W) -> std::io::Result<()>
|
||||||
|
where
|
||||||
|
W: ?Sized + std::io::Write,
|
||||||
|
{
|
||||||
|
self.render_raw(width, &mut crate::IoWrite::new(out))
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Writes a rendered document to a `std::fmt::Write` object.
|
||||||
|
#[inline]
|
||||||
|
pub fn render_fmt<W>(&self, width: usize, out: &mut W) -> core::fmt::Result
|
||||||
|
where
|
||||||
|
W: ?Sized + core::fmt::Write,
|
||||||
|
{
|
||||||
|
self.render_raw(width, &mut FmtWrite::new(out))
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Writes a rendered document to a `RenderAnnotated<A>` object.
|
||||||
|
#[inline]
|
||||||
|
pub fn render_raw<W>(&self, width: usize, out: &mut W) -> Result<(), W::Error>
|
||||||
|
where
|
||||||
|
W: RenderAnnotated,
|
||||||
|
W: ?Sized,
|
||||||
|
{
|
||||||
|
render::best(Rc::new(self.clone()), width, out)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl PrettyTree {
|
||||||
|
/// The given text, which must not contain line breaks.
|
||||||
|
#[inline]
|
||||||
|
#[cfg(feature = "std")]
|
||||||
|
pub fn render_colored<W: Write>(&self, width: usize, out: W) -> std::io::Result<()> {
|
||||||
|
render::best(Rc::new(self.clone()), width, &mut crate::TerminalWriter::new(out))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl PrettyBuilder for PrettyTree {
|
||||||
|
/// Acts as `self` when laid out on multiple lines and acts as `that` when laid out on a single line.
|
||||||
|
///
|
||||||
|
/// ```
|
||||||
|
/// use pretty::{Arena, DocAllocator};
|
||||||
|
///
|
||||||
|
/// let arena = Arena::<()>::new();
|
||||||
|
/// let body = arena.line().append("x");
|
||||||
|
/// let doc = arena
|
||||||
|
/// .text("let")
|
||||||
|
/// .append(arena.line())
|
||||||
|
/// .append("x")
|
||||||
|
/// .group()
|
||||||
|
/// .append(body.clone().flat_alt(arena.line().append("in").append(body)))
|
||||||
|
/// .group();
|
||||||
|
///
|
||||||
|
/// assert_eq!(doc.1.pretty(100).to_string(), "let x in x");
|
||||||
|
/// assert_eq!(doc.1.pretty(8).to_string(), "let x\nx");
|
||||||
|
/// ```
|
||||||
|
#[inline]
|
||||||
|
fn flat_alt<E>(self, flat: E) -> Self
|
||||||
|
where
|
||||||
|
E: Into<PrettyTree>,
|
||||||
|
{
|
||||||
|
Self::MaybeInline { block: Rc::new(self), inline: Rc::new(flat.into()) }
|
||||||
|
}
|
||||||
|
/// Indents `self` by `adjust` spaces from the current cursor position
|
||||||
|
///
|
||||||
|
/// NOTE: The doc pointer type, `D` may need to be cloned. Consider using cheaply cloneable ptr
|
||||||
|
/// like `RefDoc` or `RcDoc`
|
||||||
|
///
|
||||||
|
/// ```rust
|
||||||
|
/// use pretty::DocAllocator;
|
||||||
|
///
|
||||||
|
/// let arena = pretty::Arena::<()>::new();
|
||||||
|
/// let doc = arena
|
||||||
|
/// .text("prefix")
|
||||||
|
/// .append(arena.text(" "))
|
||||||
|
/// .append(arena.reflow("The indent function indents these words!").indent(4));
|
||||||
|
/// assert_eq!(
|
||||||
|
/// doc.1.pretty(24).to_string(),
|
||||||
|
/// "
|
||||||
|
/// prefix The indent
|
||||||
|
/// function
|
||||||
|
/// indents these
|
||||||
|
/// words!"
|
||||||
|
/// .trim_start(),
|
||||||
|
/// );
|
||||||
|
/// ```
|
||||||
|
#[inline]
|
||||||
|
fn indent(self, adjust: usize) -> Self {
|
||||||
|
let spaces = {
|
||||||
|
use crate::render::SPACES;
|
||||||
|
let mut doc = PrettyTree::Nil;
|
||||||
|
let mut remaining = adjust;
|
||||||
|
while remaining != 0 {
|
||||||
|
let i = SPACES.len().min(remaining);
|
||||||
|
remaining -= i;
|
||||||
|
doc = doc.append(PrettyTree::text(&SPACES[..i]))
|
||||||
|
}
|
||||||
|
doc
|
||||||
|
};
|
||||||
|
spaces.append(self).hang(adjust.try_into().unwrap())
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Increase the indentation level of this document.
|
||||||
|
#[inline]
|
||||||
|
fn nest(self, offset: isize) -> Self {
|
||||||
|
if let Self::Nil = self {
|
||||||
|
return self;
|
||||||
|
}
|
||||||
|
if offset == 0 {
|
||||||
|
return self;
|
||||||
|
}
|
||||||
|
Self::Nest { space: offset, doc: Rc::new(self) }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl PrettyTree {
|
||||||
|
fn with_utf8_len(self) -> Self {
|
||||||
|
let s = match &self {
|
||||||
|
Self::Text(s) => s.as_ref(),
|
||||||
|
Self::StaticText(s) => s,
|
||||||
|
// Doc::SmallText(s) => s,
|
||||||
|
_ => return self,
|
||||||
|
};
|
||||||
|
if s.is_ascii() {
|
||||||
|
self
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
let grapheme_len = s.graphemes(true).count();
|
||||||
|
Self::RenderLength { length: grapheme_len, body: Rc::new(self) }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Append the given document after this document.
|
||||||
|
#[inline]
|
||||||
|
pub fn append<E>(self, follow: E) -> Self
|
||||||
|
where
|
||||||
|
E: Into<PrettyTree>,
|
||||||
|
{
|
||||||
|
let rhs = follow.into();
|
||||||
|
match (&self, &rhs) {
|
||||||
|
(Self::Nil, _) => rhs,
|
||||||
|
(_, Self::Nil) => self,
|
||||||
|
_ => Self::Append { lhs: Rc::new(self), rhs: Rc::new(rhs) },
|
||||||
|
}
|
||||||
|
}
|
||||||
|
/// Allocate a document that intersperses the given separator `S` between the given documents
|
||||||
|
/// `[A, B, C, ..., Z]`, yielding `[A, S, B, S, C, S, ..., S, Z]`.
|
||||||
|
///
|
||||||
|
/// Compare [the `intersperse` method from the `itertools` crate](https://docs.rs/itertools/0.5.9/itertools/trait.Itertools.html#method.intersperse).
|
||||||
|
///
|
||||||
|
/// NOTE: The separator type, `S` may need to be cloned. Consider using cheaply cloneable ptr
|
||||||
|
/// like `RefDoc` or `RcDoc`
|
||||||
|
#[inline]
|
||||||
|
pub fn join<I, T1, T2>(terms: I, joint: T2) -> PrettyTree
|
||||||
|
where
|
||||||
|
I: IntoIterator<Item = T1>,
|
||||||
|
T1: Into<PrettyTree>,
|
||||||
|
T2: Into<PrettyTree>,
|
||||||
|
{
|
||||||
|
let joint = joint.into();
|
||||||
|
let mut iter = terms.into_iter().map(|s| s.into());
|
||||||
|
let mut terms = PrettySequence::new(0);
|
||||||
|
terms += iter.next().unwrap_or(PrettyTree::Nil);
|
||||||
|
for term in iter {
|
||||||
|
terms += joint.clone();
|
||||||
|
terms += term;
|
||||||
|
}
|
||||||
|
terms.into()
|
||||||
|
}
|
||||||
|
/// Allocate a document that intersperses the given separator `S` between the given documents
|
||||||
|
pub fn concat<I>(docs: I) -> Self
|
||||||
|
where
|
||||||
|
I: IntoIterator,
|
||||||
|
I::Item: Into<PrettyTree>,
|
||||||
|
{
|
||||||
|
let mut head = Self::Nil;
|
||||||
|
for item in docs.into_iter() {
|
||||||
|
head += item.into();
|
||||||
|
}
|
||||||
|
head
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Mark this document as a group.
|
||||||
|
///
|
||||||
|
/// Groups are layed out on a single line if possible. Within a group, all basic documents with
|
||||||
|
/// several possible layouts are assigned the same layout, that is, they are all layed out
|
||||||
|
/// horizontally and combined into a one single line, or they are each layed out on their own
|
||||||
|
/// line.
|
||||||
|
#[inline]
|
||||||
|
pub fn group(self) -> Self {
|
||||||
|
match self {
|
||||||
|
Self::Group { .. } | Self::Text(_) | Self::StaticText(_) | Self::Nil => self,
|
||||||
|
_ => Self::Group { items: Rc::new(self) },
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Mark this document as a comment.
|
||||||
|
#[inline]
|
||||||
|
pub fn annotate(self, style: Rc<AnsiStyle>) -> Self {
|
||||||
|
Self::Annotated { style: style, body: Rc::new(self) }
|
||||||
|
}
|
||||||
|
/// Mark this document as a hard line break.
|
||||||
|
#[inline]
|
||||||
|
pub fn union<E>(self, other: E) -> Self
|
||||||
|
where
|
||||||
|
E: Into<PrettyTree>,
|
||||||
|
{
|
||||||
|
Self::Union { lhs: Rc::new(self), rhs: Rc::new(other.into()) }
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Lays out `self` so with the nesting level set to the current column
|
||||||
|
///
|
||||||
|
/// NOTE: The doc pointer type, `D` may need to be cloned. Consider using cheaply cloneable ptr
|
||||||
|
/// like `RefDoc` or `RcDoc`
|
||||||
|
///
|
||||||
|
/// ```rust
|
||||||
|
/// use pretty::{docs, DocAllocator};
|
||||||
|
///
|
||||||
|
/// let arena = &pretty::Arena::<()>::new();
|
||||||
|
/// let doc = docs![
|
||||||
|
/// arena,
|
||||||
|
/// "lorem",
|
||||||
|
/// " ",
|
||||||
|
/// arena.intersperse(["ipsum", "dolor"].iter().cloned(), arena.line_()).align(),
|
||||||
|
/// arena.hardline(),
|
||||||
|
/// "next",
|
||||||
|
/// ];
|
||||||
|
/// assert_eq!(doc.1.pretty(80).to_string(), "lorem ipsum\n dolor\nnext");
|
||||||
|
/// ```
|
||||||
|
#[inline]
|
||||||
|
pub fn align(self) -> Self {
|
||||||
|
Self::Column {
|
||||||
|
invoke: Rc::new(move |col| {
|
||||||
|
let self_ = self.clone();
|
||||||
|
Self::Nesting { invoke: Rc::new(move |nest| self_.clone().nest(col as isize - nest as isize)) }
|
||||||
|
}),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Lays out `self` with a nesting level set to the current level plus `adjust`.
|
||||||
|
///
|
||||||
|
/// NOTE: The doc pointer type, `D` may need to be cloned. Consider using cheaply cloneable ptr
|
||||||
|
/// like `RefDoc` or `RcDoc`
|
||||||
|
///
|
||||||
|
/// ```rust
|
||||||
|
/// use pretty::DocAllocator;
|
||||||
|
///
|
||||||
|
/// let arena = pretty::Arena::<()>::new();
|
||||||
|
/// let doc = arena
|
||||||
|
/// .text("prefix")
|
||||||
|
/// .append(arena.text(" "))
|
||||||
|
/// .append(arena.reflow("Indenting these words with nest").hang(4));
|
||||||
|
/// assert_eq!(
|
||||||
|
/// doc.1.pretty(24).to_string(),
|
||||||
|
/// "prefix Indenting these\n words with\n nest",
|
||||||
|
/// );
|
||||||
|
/// ```
|
||||||
|
#[inline]
|
||||||
|
pub fn hang(self, adjust: isize) -> Self {
|
||||||
|
self.nest(adjust).align()
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Lays out `self` and provides the column width of it available to `f`
|
||||||
|
///
|
||||||
|
/// NOTE: The doc pointer type, `D` may need to be cloned. Consider using cheaply cloneable ptr
|
||||||
|
/// like `RefDoc` or `RcDoc`
|
||||||
|
///
|
||||||
|
/// ```rust
|
||||||
|
/// use pretty::DocAllocator;
|
||||||
|
///
|
||||||
|
/// let arena = pretty::Arena::<()>::new();
|
||||||
|
/// let doc = arena
|
||||||
|
/// .text("prefix ")
|
||||||
|
/// .append(arena.column(|l| arena.text("| <- column ").append(arena.as_string(l)).into_doc()));
|
||||||
|
/// assert_eq!(doc.1.pretty(80).to_string(), "prefix | <- column 7");
|
||||||
|
/// ```
|
||||||
|
#[inline]
|
||||||
|
pub fn width<F>(self, f: F) -> Self
|
||||||
|
where
|
||||||
|
F: Fn(isize) -> Self + Clone + 'static,
|
||||||
|
{
|
||||||
|
Self::Column {
|
||||||
|
invoke: Rc::new(move |start| {
|
||||||
|
let f = f.clone();
|
||||||
|
self.clone() + Self::Column { invoke: Rc::new(move |end| f(end as isize - start as isize)) }
|
||||||
|
}),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Puts `self` between `before` and `after`
|
||||||
|
#[inline]
|
||||||
|
pub fn enclose<E, F>(self, before: E, after: F) -> Self
|
||||||
|
where
|
||||||
|
E: Into<Self>,
|
||||||
|
F: Into<Self>,
|
||||||
|
{
|
||||||
|
before.into().append(self).append(after.into())
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Puts `self` between `before` and `after` if `cond` is true
|
||||||
|
pub fn single_quotes(self) -> Self {
|
||||||
|
self.enclose("'", "'")
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Puts `self` between `before` and `after` if `cond` is true
|
||||||
|
pub fn double_quotes(self) -> Self {
|
||||||
|
self.enclose("\"", "\"")
|
||||||
|
}
|
||||||
|
/// Puts `self` between `before` and `after` if `cond` is true
|
||||||
|
pub fn parens(self) -> Self {
|
||||||
|
self.enclose("(", ")")
|
||||||
|
}
|
||||||
|
/// Puts `self` between `before` and `after` if `cond` is true
|
||||||
|
pub fn angles(self) -> Self {
|
||||||
|
self.enclose("<", ">")
|
||||||
|
}
|
||||||
|
/// Puts `self` between `before` and `after` if `cond` is true
|
||||||
|
pub fn braces(self) -> Self {
|
||||||
|
self.enclose("{", "}")
|
||||||
|
}
|
||||||
|
/// Puts `self` between `before` and `after` if `cond` is true
|
||||||
|
pub fn brackets(self) -> Self {
|
||||||
|
self.enclose("[", "]")
|
||||||
|
}
|
||||||
|
}
|
||||||
6
pretty-print/projects/pretty-print/tests/main.rs
Normal file
6
pretty-print/projects/pretty-print/tests/main.rs
Normal file
|
|
@ -0,0 +1,6 @@
|
||||||
|
#[test]
|
||||||
|
fn ready() {
|
||||||
|
println!("it works!")
|
||||||
|
}
|
||||||
|
|
||||||
|
mod readme;
|
||||||
5
pretty-print/projects/pretty-print/tests/readme.md
Normal file
5
pretty-print/projects/pretty-print/tests/readme.md
Normal file
|
|
@ -0,0 +1,5 @@
|
||||||
|
## Tests
|
||||||
|
|
||||||
|
```bash
|
||||||
|
wee test
|
||||||
|
```
|
||||||
38
pretty-print/projects/pretty-print/tests/readme.rs
Normal file
38
pretty-print/projects/pretty-print/tests/readme.rs
Normal file
|
|
@ -0,0 +1,38 @@
|
||||||
|
use pretty_print::*;
|
||||||
|
use SExp::*;
|
||||||
|
enum SExp {
|
||||||
|
Atom(u32),
|
||||||
|
List(Vec<SExp>),
|
||||||
|
}
|
||||||
|
|
||||||
|
impl SExp {
|
||||||
|
/// Return a pretty printed format of self.
|
||||||
|
pub fn to_doc(&self) -> PrettyTree {
|
||||||
|
match self {
|
||||||
|
Atom(x) => PrettyTree::text(x.to_string()),
|
||||||
|
List(xs) => PrettyTree::text("(")
|
||||||
|
.append(PrettyTree::join(xs.into_iter().map(|x| x.to_doc()), PrettyTree::line_or_space()).nest(1).group())
|
||||||
|
.append(PrettyTree::text(")")),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
/// Return a pretty printed format of self.
|
||||||
|
pub fn to_pretty(&self, width: usize) -> String {
|
||||||
|
let mut w = Vec::new();
|
||||||
|
self.to_doc().render(width, &mut w).unwrap();
|
||||||
|
String::from_utf8(w).unwrap()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn main() {
|
||||||
|
let atom = Atom(5);
|
||||||
|
assert_eq!("5", atom.to_pretty(10));
|
||||||
|
let list = List(vec![Atom(1), Atom(2), Atom(3)]);
|
||||||
|
assert_eq!("(1 2 3)", list.to_pretty(10));
|
||||||
|
assert_eq!(
|
||||||
|
"\
|
||||||
|
(1
|
||||||
|
2
|
||||||
|
3)",
|
||||||
|
list.to_pretty(5)
|
||||||
|
);
|
||||||
|
}
|
||||||
29
pretty-print/projects/pretty-test/Cargo.toml
Normal file
29
pretty-print/projects/pretty-test/Cargo.toml
Normal file
|
|
@ -0,0 +1,29 @@
|
||||||
|
[package]
|
||||||
|
name = "pretty-test"
|
||||||
|
publish = false
|
||||||
|
version = "0.0.0"
|
||||||
|
authors = ["Aster <192607617@qq.com>"]
|
||||||
|
description = "..."
|
||||||
|
repository = "https://github.com/oovm/sub_projects"
|
||||||
|
documentation = "https://docs.rs/sub_projects"
|
||||||
|
readme = "Readme.md"
|
||||||
|
license = "MPL-2.0"
|
||||||
|
edition = "2021"
|
||||||
|
exclude = ["package.json", "tests/**"]
|
||||||
|
|
||||||
|
[dependencies]
|
||||||
|
pretty = "0.12.1"
|
||||||
|
|
||||||
|
[dependencies.pretty-print]
|
||||||
|
version = "*"
|
||||||
|
default-features = false
|
||||||
|
features = ["std"]
|
||||||
|
path = "../pretty-print"
|
||||||
|
|
||||||
|
[dev-dependencies]
|
||||||
|
|
||||||
|
[features]
|
||||||
|
default = []
|
||||||
|
|
||||||
|
[package.metadata.docs.rs]
|
||||||
|
all-features = true
|
||||||
6
pretty-print/projects/pretty-test/package.json
Normal file
6
pretty-print/projects/pretty-test/package.json
Normal file
|
|
@ -0,0 +1,6 @@
|
||||||
|
{
|
||||||
|
"private": true,
|
||||||
|
"scripts": {
|
||||||
|
"p": "cargo publish --allow-dirty"
|
||||||
|
}
|
||||||
|
}
|
||||||
2
pretty-print/projects/pretty-test/readme.md
Normal file
2
pretty-print/projects/pretty-test/readme.md
Normal file
|
|
@ -0,0 +1,2 @@
|
||||||
|
Title
|
||||||
|
=====
|
||||||
1
pretty-print/projects/pretty-test/src/errors.rs
Normal file
1
pretty-print/projects/pretty-test/src/errors.rs
Normal file
|
|
@ -0,0 +1 @@
|
||||||
|
|
||||||
5
pretty-print/projects/pretty-test/src/lib.rs
Normal file
5
pretty-print/projects/pretty-test/src/lib.rs
Normal file
|
|
@ -0,0 +1,5 @@
|
||||||
|
#![deny(missing_debug_implementations, missing_copy_implementations)]
|
||||||
|
#![warn(missing_docs, rustdoc::missing_crate_level_docs)]
|
||||||
|
#![doc = include_str!("../readme.md")]
|
||||||
|
#![doc(html_logo_url = "https://raw.githubusercontent.com/oovm/shape-rs/dev/projects/images/Trapezohedron.svg")]
|
||||||
|
#![doc(html_favicon_url = "https://raw.githubusercontent.com/oovm/shape-rs/dev/projects/images/Trapezohedron.svg")]
|
||||||
46
pretty-print/projects/pretty-test/tests/main.rs
Normal file
46
pretty-print/projects/pretty-test/tests/main.rs
Normal file
|
|
@ -0,0 +1,46 @@
|
||||||
|
#![allow(dead_code, unused)]
|
||||||
|
use pretty_print::{AnsiColor, AnsiStyle, PrettyBuilder, PrettyTree};
|
||||||
|
use std::{io::stdout, rc::Rc};
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn ready() {
|
||||||
|
println!("it works!")
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn box1() {
|
||||||
|
let doc = PrettyTree::text("hello")
|
||||||
|
.annotate(Rc::new(AnsiStyle::new(AnsiColor::Red)))
|
||||||
|
.append(PrettyTree::Hardline)
|
||||||
|
.append("the")
|
||||||
|
.append("∷")
|
||||||
|
.append("world");
|
||||||
|
let mut buffer = vec![];
|
||||||
|
doc.render_colored(10, &mut buffer).unwrap();
|
||||||
|
|
||||||
|
println!("{}", String::from_utf8_lossy(&buffer));
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn box2() {
|
||||||
|
let doc = PrettyTree::join(vec!["hello", " ", " ", "world"], "∷").group();
|
||||||
|
let mut buffer = vec![];
|
||||||
|
doc.render_colored(10, &mut buffer).unwrap();
|
||||||
|
|
||||||
|
println!("{}", String::from_utf8_lossy(&buffer));
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn box_doc_inference() {
|
||||||
|
let doc = PrettyTree::text("test").append(PrettyTree::line_or_space()).append(PrettyTree::text("test")).group();
|
||||||
|
|
||||||
|
println!("{}", doc.pretty(10));
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn newline_in_text() {
|
||||||
|
let doc = PrettyTree::text("test")
|
||||||
|
.append(PrettyTree::line_or_space().append(PrettyTree::text("\"test\n test\"")).nest(4))
|
||||||
|
.group();
|
||||||
|
println!("{}", doc.pretty(10));
|
||||||
|
}
|
||||||
5
pretty-print/projects/pretty-test/tests/readme.md
Normal file
5
pretty-print/projects/pretty-test/tests/readme.md
Normal file
|
|
@ -0,0 +1,5 @@
|
||||||
|
## Tests
|
||||||
|
|
||||||
|
```bash
|
||||||
|
wee test
|
||||||
|
```
|
||||||
9
pretty-print/rustfmt.toml
Normal file
9
pretty-print/rustfmt.toml
Normal file
|
|
@ -0,0 +1,9 @@
|
||||||
|
version = "Two"
|
||||||
|
edition = "2018"
|
||||||
|
max_width = 128
|
||||||
|
|
||||||
|
imports_granularity = "Crate"
|
||||||
|
use_small_heuristics = "Max"
|
||||||
|
control_brace_style = "ClosingNextLine"
|
||||||
|
normalize_comments = true
|
||||||
|
format_code_in_doc_comments = true
|
||||||
Loading…
Add table
Add a link
Reference in a new issue