mirror of
https://github.com/rust-lang/rust-analyzer.git
synced 2025-12-27 16:07:46 +00:00
Subtree text-size
This commit is contained in:
commit
6f83ceeda6
54
lib/text-size/.github/workflows/ci.yaml
vendored
Normal file
54
lib/text-size/.github/workflows/ci.yaml
vendored
Normal file
@ -0,0 +1,54 @@
|
||||
name: CI
|
||||
on:
|
||||
pull_request:
|
||||
push:
|
||||
branches:
|
||||
- master
|
||||
- staging
|
||||
- trying
|
||||
|
||||
env:
|
||||
RUSTFLAGS: -D warnings
|
||||
RUSTUP_MAX_RETRIES: 10
|
||||
CARGO_NET_RETRY: 10
|
||||
|
||||
jobs:
|
||||
rust:
|
||||
name: Rust
|
||||
runs-on: ${{ matrix.os }}
|
||||
|
||||
strategy:
|
||||
fail-fast: false
|
||||
matrix:
|
||||
os: [ubuntu-latest, windows-latest, macos-latest]
|
||||
|
||||
steps:
|
||||
- name: Checkout repository
|
||||
uses: actions/checkout@v2
|
||||
|
||||
- name: Install Rust toolchain
|
||||
uses: actions-rs/toolchain@v1
|
||||
with:
|
||||
toolchain: stable
|
||||
profile: minimal
|
||||
|
||||
- name: Test
|
||||
run: cargo test --all-features
|
||||
|
||||
rustdoc:
|
||||
name: Docs
|
||||
runs-on: ubuntu-latest
|
||||
|
||||
steps:
|
||||
- name: Checkout repository
|
||||
uses: actions/checkout@v2
|
||||
|
||||
- name: Install Rust toolchain
|
||||
uses: actions-rs/toolchain@v1
|
||||
with:
|
||||
toolchain: nightly
|
||||
profile: minimal
|
||||
override: true
|
||||
|
||||
- name: Rustdoc
|
||||
run: cargo rustdoc --all-features -- -D warnings
|
||||
3
lib/text-size/.gitignore
vendored
Normal file
3
lib/text-size/.gitignore
vendored
Normal file
@ -0,0 +1,3 @@
|
||||
/target
|
||||
**/*.rs.bk
|
||||
Cargo.lock
|
||||
26
lib/text-size/CHANGELOG.md
Normal file
26
lib/text-size/CHANGELOG.md
Normal file
@ -0,0 +1,26 @@
|
||||
# Changelog
|
||||
|
||||
## 1.1.0
|
||||
|
||||
* add `TextRange::ordering` method
|
||||
|
||||
## 1.0.0 :tada:
|
||||
|
||||
* the carate is renamed to `text-size` from `text_unit`
|
||||
|
||||
Transition table:
|
||||
- `TextUnit::of_char(c)` ⟹ `TextSize::of(c)`
|
||||
- `TextUnit::of_str(s)` ⟹ `TextSize::of(s)`
|
||||
- `TextUnit::from_usize(size)` ⟹ `TextSize::try_from(size).unwrap_or_else(|| panic!(_))`
|
||||
- `unit.to_usize()` ⟹ `usize::from(size)`
|
||||
- `TextRange::from_to(from, to)` ⟹ `TextRange::new(from, to)`
|
||||
- `TextRange::offset_len(offset, size)` ⟹ `TextRange::from_len(offset, size)`
|
||||
- `range.start()` ⟹ `range.start()`
|
||||
- `range.end()` ⟹ `range.end()`
|
||||
- `range.len()` ⟹ `range.len()`
|
||||
- `range.is_empty()` ⟹ `range.is_empty()`
|
||||
- `a.is_subrange(b)` ⟹ `b.contains_range(a)`
|
||||
- `a.intersection(b)` ⟹ `a.intersect(b)`
|
||||
- `a.extend_to(b)` ⟹ `a.cover(b)`
|
||||
- `range.contains(offset)` ⟹ `range.contains(point)`
|
||||
- `range.contains_inclusive(offset)` ⟹ `range.contains_inclusive(point)`
|
||||
25
lib/text-size/Cargo.toml
Normal file
25
lib/text-size/Cargo.toml
Normal file
@ -0,0 +1,25 @@
|
||||
[package]
|
||||
name = "text-size"
|
||||
version = "1.1.1"
|
||||
edition = "2018"
|
||||
|
||||
authors = [
|
||||
"Aleksey Kladov <aleksey.kladov@gmail.com>",
|
||||
"Christopher Durham (CAD97) <cad97@cad97.com>"
|
||||
]
|
||||
description = "Newtypes for text offsets"
|
||||
license = "MIT OR Apache-2.0"
|
||||
repository = "https://github.com/rust-analyzer/text-size"
|
||||
documentation = "https://docs.rs/text-size"
|
||||
|
||||
[dependencies]
|
||||
serde = { version = "1.0", optional = true, default_features = false }
|
||||
|
||||
[dev-dependencies]
|
||||
serde_test = "1.0"
|
||||
static_assertions = "1.1"
|
||||
|
||||
[[test]]
|
||||
name = "serde"
|
||||
path = "tests/serde.rs"
|
||||
required-features = ["serde"]
|
||||
201
lib/text-size/LICENSE-APACHE
Normal file
201
lib/text-size/LICENSE-APACHE
Normal file
@ -0,0 +1,201 @@
|
||||
Apache License
|
||||
Version 2.0, January 2004
|
||||
http://www.apache.org/licenses/
|
||||
|
||||
TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
|
||||
|
||||
1. Definitions.
|
||||
|
||||
"License" shall mean the terms and conditions for use, reproduction,
|
||||
and distribution as defined by Sections 1 through 9 of this document.
|
||||
|
||||
"Licensor" shall mean the copyright owner or entity authorized by
|
||||
the copyright owner that is granting the License.
|
||||
|
||||
"Legal Entity" shall mean the union of the acting entity and all
|
||||
other entities that control, are controlled by, or are under common
|
||||
control with that entity. For the purposes of this definition,
|
||||
"control" means (i) the power, direct or indirect, to cause the
|
||||
direction or management of such entity, whether by contract or
|
||||
otherwise, or (ii) ownership of fifty percent (50%) or more of the
|
||||
outstanding shares, or (iii) beneficial ownership of such entity.
|
||||
|
||||
"You" (or "Your") shall mean an individual or Legal Entity
|
||||
exercising permissions granted by this License.
|
||||
|
||||
"Source" form shall mean the preferred form for making modifications,
|
||||
including but not limited to software source code, documentation
|
||||
source, and configuration files.
|
||||
|
||||
"Object" form shall mean any form resulting from mechanical
|
||||
transformation or translation of a Source form, including but
|
||||
not limited to compiled object code, generated documentation,
|
||||
and conversions to other media types.
|
||||
|
||||
"Work" shall mean the work of authorship, whether in Source or
|
||||
Object form, made available under the License, as indicated by a
|
||||
copyright notice that is included in or attached to the work
|
||||
(an example is provided in the Appendix below).
|
||||
|
||||
"Derivative Works" shall mean any work, whether in Source or Object
|
||||
form, that is based on (or derived from) the Work and for which the
|
||||
editorial revisions, annotations, elaborations, or other modifications
|
||||
represent, as a whole, an original work of authorship. For the purposes
|
||||
of this License, Derivative Works shall not include works that remain
|
||||
separable from, or merely link (or bind by name) to the interfaces of,
|
||||
the Work and Derivative Works thereof.
|
||||
|
||||
"Contribution" shall mean any work of authorship, including
|
||||
the original version of the Work and any modifications or additions
|
||||
to that Work or Derivative Works thereof, that is intentionally
|
||||
submitted to Licensor for inclusion in the Work by the copyright owner
|
||||
or by an individual or Legal Entity authorized to submit on behalf of
|
||||
the copyright owner. For the purposes of this definition, "submitted"
|
||||
means any form of electronic, verbal, or written communication sent
|
||||
to the Licensor or its representatives, including but not limited to
|
||||
communication on electronic mailing lists, source code control systems,
|
||||
and issue tracking systems that are managed by, or on behalf of, the
|
||||
Licensor for the purpose of discussing and improving the Work, but
|
||||
excluding communication that is conspicuously marked or otherwise
|
||||
designated in writing by the copyright owner as "Not a Contribution."
|
||||
|
||||
"Contributor" shall mean Licensor and any individual or Legal Entity
|
||||
on behalf of whom a Contribution has been received by Licensor and
|
||||
subsequently incorporated within the Work.
|
||||
|
||||
2. Grant of Copyright License. Subject to the terms and conditions of
|
||||
this License, each Contributor hereby grants to You a perpetual,
|
||||
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
|
||||
copyright license to reproduce, prepare Derivative Works of,
|
||||
publicly display, publicly perform, sublicense, and distribute the
|
||||
Work and such Derivative Works in Source or Object form.
|
||||
|
||||
3. Grant of Patent License. Subject to the terms and conditions of
|
||||
this License, each Contributor hereby grants to You a perpetual,
|
||||
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
|
||||
(except as stated in this section) patent license to make, have made,
|
||||
use, offer to sell, sell, import, and otherwise transfer the Work,
|
||||
where such license applies only to those patent claims licensable
|
||||
by such Contributor that are necessarily infringed by their
|
||||
Contribution(s) alone or by combination of their Contribution(s)
|
||||
with the Work to which such Contribution(s) was submitted. If You
|
||||
institute patent litigation against any entity (including a
|
||||
cross-claim or counterclaim in a lawsuit) alleging that the Work
|
||||
or a Contribution incorporated within the Work constitutes direct
|
||||
or contributory patent infringement, then any patent licenses
|
||||
granted to You under this License for that Work shall terminate
|
||||
as of the date such litigation is filed.
|
||||
|
||||
4. Redistribution. You may reproduce and distribute copies of the
|
||||
Work or Derivative Works thereof in any medium, with or without
|
||||
modifications, and in Source or Object form, provided that You
|
||||
meet the following conditions:
|
||||
|
||||
(a) You must give any other recipients of the Work or
|
||||
Derivative Works a copy of this License; and
|
||||
|
||||
(b) You must cause any modified files to carry prominent notices
|
||||
stating that You changed the files; and
|
||||
|
||||
(c) You must retain, in the Source form of any Derivative Works
|
||||
that You distribute, all copyright, patent, trademark, and
|
||||
attribution notices from the Source form of the Work,
|
||||
excluding those notices that do not pertain to any part of
|
||||
the Derivative Works; and
|
||||
|
||||
(d) If the Work includes a "NOTICE" text file as part of its
|
||||
distribution, then any Derivative Works that You distribute must
|
||||
include a readable copy of the attribution notices contained
|
||||
within such NOTICE file, excluding those notices that do not
|
||||
pertain to any part of the Derivative Works, in at least one
|
||||
of the following places: within a NOTICE text file distributed
|
||||
as part of the Derivative Works; within the Source form or
|
||||
documentation, if provided along with the Derivative Works; or,
|
||||
within a display generated by the Derivative Works, if and
|
||||
wherever such third-party notices normally appear. The contents
|
||||
of the NOTICE file are for informational purposes only and
|
||||
do not modify the License. You may add Your own attribution
|
||||
notices within Derivative Works that You distribute, alongside
|
||||
or as an addendum to the NOTICE text from the Work, provided
|
||||
that such additional attribution notices cannot be construed
|
||||
as modifying the License.
|
||||
|
||||
You may add Your own copyright statement to Your modifications and
|
||||
may provide additional or different license terms and conditions
|
||||
for use, reproduction, or distribution of Your modifications, or
|
||||
for any such Derivative Works as a whole, provided Your use,
|
||||
reproduction, and distribution of the Work otherwise complies with
|
||||
the conditions stated in this License.
|
||||
|
||||
5. Submission of Contributions. Unless You explicitly state otherwise,
|
||||
any Contribution intentionally submitted for inclusion in the Work
|
||||
by You to the Licensor shall be under the terms and conditions of
|
||||
this License, without any additional terms or conditions.
|
||||
Notwithstanding the above, nothing herein shall supersede or modify
|
||||
the terms of any separate license agreement you may have executed
|
||||
with Licensor regarding such Contributions.
|
||||
|
||||
6. Trademarks. This License does not grant permission to use the trade
|
||||
names, trademarks, service marks, or product names of the Licensor,
|
||||
except as required for reasonable and customary use in describing the
|
||||
origin of the Work and reproducing the content of the NOTICE file.
|
||||
|
||||
7. Disclaimer of Warranty. Unless required by applicable law or
|
||||
agreed to in writing, Licensor provides the Work (and each
|
||||
Contributor provides its Contributions) on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
|
||||
implied, including, without limitation, any warranties or conditions
|
||||
of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
|
||||
PARTICULAR PURPOSE. You are solely responsible for determining the
|
||||
appropriateness of using or redistributing the Work and assume any
|
||||
risks associated with Your exercise of permissions under this License.
|
||||
|
||||
8. Limitation of Liability. In no event and under no legal theory,
|
||||
whether in tort (including negligence), contract, or otherwise,
|
||||
unless required by applicable law (such as deliberate and grossly
|
||||
negligent acts) or agreed to in writing, shall any Contributor be
|
||||
liable to You for damages, including any direct, indirect, special,
|
||||
incidental, or consequential damages of any character arising as a
|
||||
result of this License or out of the use or inability to use the
|
||||
Work (including but not limited to damages for loss of goodwill,
|
||||
work stoppage, computer failure or malfunction, or any and all
|
||||
other commercial damages or losses), even if such Contributor
|
||||
has been advised of the possibility of such damages.
|
||||
|
||||
9. Accepting Warranty or Additional Liability. While redistributing
|
||||
the Work or Derivative Works thereof, You may choose to offer,
|
||||
and charge a fee for, acceptance of support, warranty, indemnity,
|
||||
or other liability obligations and/or rights consistent with this
|
||||
License. However, in accepting such obligations, You may act only
|
||||
on Your own behalf and on Your sole responsibility, not on behalf
|
||||
of any other Contributor, and only if You agree to indemnify,
|
||||
defend, and hold each Contributor harmless for any liability
|
||||
incurred by, or claims asserted against, such Contributor by reason
|
||||
of your accepting any such warranty or additional liability.
|
||||
|
||||
END OF TERMS AND CONDITIONS
|
||||
|
||||
APPENDIX: How to apply the Apache License to your work.
|
||||
|
||||
To apply the Apache License to your work, attach the following
|
||||
boilerplate notice, with the fields enclosed by brackets "[]"
|
||||
replaced with your own identifying information. (Don't include
|
||||
the brackets!) The text should be enclosed in the appropriate
|
||||
comment syntax for the file format. We also recommend that a
|
||||
file or class name and description of purpose be included on the
|
||||
same "printed page" as the copyright notice for easier
|
||||
identification within third-party archives.
|
||||
|
||||
Copyright [yyyy] [name of copyright owner]
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
23
lib/text-size/LICENSE-MIT
Normal file
23
lib/text-size/LICENSE-MIT
Normal file
@ -0,0 +1,23 @@
|
||||
Permission is hereby granted, free of charge, to any
|
||||
person obtaining a copy of this software and associated
|
||||
documentation files (the "Software"), to deal in the
|
||||
Software without restriction, including without
|
||||
limitation the rights to use, copy, modify, merge,
|
||||
publish, distribute, sublicense, and/or sell copies of
|
||||
the Software, and to permit persons to whom the Software
|
||||
is furnished to do so, subject to the following
|
||||
conditions:
|
||||
|
||||
The above copyright notice and this permission notice
|
||||
shall be included in all copies or substantial portions
|
||||
of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF
|
||||
ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED
|
||||
TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A
|
||||
PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT
|
||||
SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
|
||||
CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
||||
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR
|
||||
IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
|
||||
DEALINGS IN THE SOFTWARE.
|
||||
27
lib/text-size/README.md
Normal file
27
lib/text-size/README.md
Normal file
@ -0,0 +1,27 @@
|
||||
# text-size
|
||||
|
||||
[](https://travis-ci.org/matklad/text-size)
|
||||
[](https://crates.io/crates/text-size)
|
||||
[](https://docs.rs/text-size/)
|
||||
|
||||
|
||||
A library that provides newtype wrappers for `u32` and `(u32, u32)` for use as text offsets.
|
||||
|
||||
See the [docs](https://docs.rs/text-size/) for more.
|
||||
|
||||
## License
|
||||
|
||||
Licensed under either of
|
||||
|
||||
* Apache License, Version 2.0
|
||||
([LICENSE-APACHE](LICENSE-APACHE) or http://www.apache.org/licenses/LICENSE-2.0)
|
||||
* MIT license
|
||||
([LICENSE-MIT](LICENSE-MIT) or http://opensource.org/licenses/MIT)
|
||||
|
||||
at your option.
|
||||
|
||||
## Contribution
|
||||
|
||||
Unless you explicitly state otherwise, any contribution intentionally submitted
|
||||
for inclusion in the work by you, as defined in the Apache-2.0 license, shall be
|
||||
dual licensed as above, without any additional terms or conditions.
|
||||
6
lib/text-size/bors.toml
Normal file
6
lib/text-size/bors.toml
Normal file
@ -0,0 +1,6 @@
|
||||
status = [
|
||||
"Rust (ubuntu-latest)",
|
||||
"Rust (windows-latest)",
|
||||
"Rust (macos-latest)",
|
||||
]
|
||||
delete_merged_branches = true
|
||||
32
lib/text-size/src/lib.rs
Normal file
32
lib/text-size/src/lib.rs
Normal file
@ -0,0 +1,32 @@
|
||||
//! Newtypes for working with text sizes/ranges in a more type-safe manner.
|
||||
//!
|
||||
//! This library can help with two things:
|
||||
//! * Reducing storage requirements for offsets and ranges, under the
|
||||
//! assumption that 32 bits is enough.
|
||||
//! * Providing standard vocabulary types for applications where text ranges
|
||||
//! are pervasive.
|
||||
//!
|
||||
//! However, you should not use this library simply because you work with
|
||||
//! strings. In the overwhelming majority of cases, using `usize` and
|
||||
//! `std::ops::Range<usize>` is better. In particular, if you are publishing a
|
||||
//! library, using only std types in the interface would make it more
|
||||
//! interoperable. Similarly, if you are writing something like a lexer, which
|
||||
//! produces, but does not *store* text ranges, then sticking to `usize` would
|
||||
//! be better.
|
||||
//!
|
||||
//! Minimal Supported Rust Version: latest stable.
|
||||
|
||||
#![forbid(unsafe_code)]
|
||||
#![warn(missing_debug_implementations, missing_docs)]
|
||||
|
||||
mod range;
|
||||
mod size;
|
||||
mod traits;
|
||||
|
||||
#[cfg(feature = "serde")]
|
||||
mod serde_impls;
|
||||
|
||||
pub use crate::{range::TextRange, size::TextSize, traits::TextLen};
|
||||
|
||||
#[cfg(target_pointer_width = "16")]
|
||||
compile_error!("text-size assumes usize >= u32 and does not work on 16-bit targets");
|
||||
456
lib/text-size/src/range.rs
Normal file
456
lib/text-size/src/range.rs
Normal file
@ -0,0 +1,456 @@
|
||||
use cmp::Ordering;
|
||||
|
||||
use {
|
||||
crate::TextSize,
|
||||
std::{
|
||||
cmp, fmt,
|
||||
ops::{Add, AddAssign, Bound, Index, IndexMut, Range, RangeBounds, Sub, SubAssign},
|
||||
},
|
||||
};
|
||||
|
||||
/// A range in text, represented as a pair of [`TextSize`][struct@TextSize].
|
||||
///
|
||||
/// It is a logic error for `start` to be greater than `end`.
|
||||
#[derive(Default, Copy, Clone, Eq, PartialEq, Hash)]
|
||||
pub struct TextRange {
|
||||
// Invariant: start <= end
|
||||
start: TextSize,
|
||||
end: TextSize,
|
||||
}
|
||||
|
||||
impl fmt::Debug for TextRange {
|
||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||
write!(f, "{}..{}", self.start().raw, self.end().raw)
|
||||
}
|
||||
}
|
||||
|
||||
impl TextRange {
|
||||
/// Creates a new `TextRange` with the given `start` and `end` (`start..end`).
|
||||
///
|
||||
/// # Panics
|
||||
///
|
||||
/// Panics if `end < start`.
|
||||
///
|
||||
/// # Examples
|
||||
///
|
||||
/// ```rust
|
||||
/// # use text_size::*;
|
||||
/// let start = TextSize::from(5);
|
||||
/// let end = TextSize::from(10);
|
||||
/// let range = TextRange::new(start, end);
|
||||
///
|
||||
/// assert_eq!(range.start(), start);
|
||||
/// assert_eq!(range.end(), end);
|
||||
/// assert_eq!(range.len(), end - start);
|
||||
/// ```
|
||||
#[inline]
|
||||
pub const fn new(start: TextSize, end: TextSize) -> TextRange {
|
||||
assert!(start.raw <= end.raw);
|
||||
TextRange { start, end }
|
||||
}
|
||||
|
||||
/// Create a new `TextRange` with the given `offset` and `len` (`offset..offset + len`).
|
||||
///
|
||||
/// # Examples
|
||||
///
|
||||
/// ```rust
|
||||
/// # use text_size::*;
|
||||
/// let text = "0123456789";
|
||||
///
|
||||
/// let offset = TextSize::from(2);
|
||||
/// let length = TextSize::from(5);
|
||||
/// let range = TextRange::at(offset, length);
|
||||
///
|
||||
/// assert_eq!(range, TextRange::new(offset, offset + length));
|
||||
/// assert_eq!(&text[range], "23456")
|
||||
/// ```
|
||||
#[inline]
|
||||
pub const fn at(offset: TextSize, len: TextSize) -> TextRange {
|
||||
TextRange::new(offset, TextSize::new(offset.raw + len.raw))
|
||||
}
|
||||
|
||||
/// Create a zero-length range at the specified offset (`offset..offset`).
|
||||
///
|
||||
/// # Examples
|
||||
///
|
||||
/// ```rust
|
||||
/// # use text_size::*;
|
||||
/// let point: TextSize;
|
||||
/// # point = TextSize::from(3);
|
||||
/// let range = TextRange::empty(point);
|
||||
/// assert!(range.is_empty());
|
||||
/// assert_eq!(range, TextRange::new(point, point));
|
||||
/// ```
|
||||
#[inline]
|
||||
pub const fn empty(offset: TextSize) -> TextRange {
|
||||
TextRange {
|
||||
start: offset,
|
||||
end: offset,
|
||||
}
|
||||
}
|
||||
|
||||
/// Create a range up to the given end (`..end`).
|
||||
///
|
||||
/// # Examples
|
||||
///
|
||||
/// ```rust
|
||||
/// # use text_size::*;
|
||||
/// let point: TextSize;
|
||||
/// # point = TextSize::from(12);
|
||||
/// let range = TextRange::up_to(point);
|
||||
///
|
||||
/// assert_eq!(range.len(), point);
|
||||
/// assert_eq!(range, TextRange::new(0.into(), point));
|
||||
/// assert_eq!(range, TextRange::at(0.into(), point));
|
||||
/// ```
|
||||
#[inline]
|
||||
pub const fn up_to(end: TextSize) -> TextRange {
|
||||
TextRange {
|
||||
start: TextSize::new(0),
|
||||
end,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Identity methods.
|
||||
impl TextRange {
|
||||
/// The start point of this range.
|
||||
#[inline]
|
||||
pub const fn start(self) -> TextSize {
|
||||
self.start
|
||||
}
|
||||
|
||||
/// The end point of this range.
|
||||
#[inline]
|
||||
pub const fn end(self) -> TextSize {
|
||||
self.end
|
||||
}
|
||||
|
||||
/// The size of this range.
|
||||
#[inline]
|
||||
pub const fn len(self) -> TextSize {
|
||||
// HACK for const fn: math on primitives only
|
||||
TextSize {
|
||||
raw: self.end().raw - self.start().raw,
|
||||
}
|
||||
}
|
||||
|
||||
/// Check if this range is empty.
|
||||
#[inline]
|
||||
pub const fn is_empty(self) -> bool {
|
||||
// HACK for const fn: math on primitives only
|
||||
self.start().raw == self.end().raw
|
||||
}
|
||||
}
|
||||
|
||||
/// Manipulation methods.
|
||||
impl TextRange {
|
||||
/// Check if this range contains an offset.
|
||||
///
|
||||
/// The end index is considered excluded.
|
||||
///
|
||||
/// # Examples
|
||||
///
|
||||
/// ```rust
|
||||
/// # use text_size::*;
|
||||
/// let (start, end): (TextSize, TextSize);
|
||||
/// # start = 10.into(); end = 20.into();
|
||||
/// let range = TextRange::new(start, end);
|
||||
/// assert!(range.contains(start));
|
||||
/// assert!(!range.contains(end));
|
||||
/// ```
|
||||
#[inline]
|
||||
pub fn contains(self, offset: TextSize) -> bool {
|
||||
self.start() <= offset && offset < self.end()
|
||||
}
|
||||
|
||||
/// Check if this range contains an offset.
|
||||
///
|
||||
/// The end index is considered included.
|
||||
///
|
||||
/// # Examples
|
||||
///
|
||||
/// ```rust
|
||||
/// # use text_size::*;
|
||||
/// let (start, end): (TextSize, TextSize);
|
||||
/// # start = 10.into(); end = 20.into();
|
||||
/// let range = TextRange::new(start, end);
|
||||
/// assert!(range.contains_inclusive(start));
|
||||
/// assert!(range.contains_inclusive(end));
|
||||
/// ```
|
||||
#[inline]
|
||||
pub fn contains_inclusive(self, offset: TextSize) -> bool {
|
||||
self.start() <= offset && offset <= self.end()
|
||||
}
|
||||
|
||||
/// Check if this range completely contains another range.
|
||||
///
|
||||
/// # Examples
|
||||
///
|
||||
/// ```rust
|
||||
/// # use text_size::*;
|
||||
/// let larger = TextRange::new(0.into(), 20.into());
|
||||
/// let smaller = TextRange::new(5.into(), 15.into());
|
||||
/// assert!(larger.contains_range(smaller));
|
||||
/// assert!(!smaller.contains_range(larger));
|
||||
///
|
||||
/// // a range always contains itself
|
||||
/// assert!(larger.contains_range(larger));
|
||||
/// assert!(smaller.contains_range(smaller));
|
||||
/// ```
|
||||
#[inline]
|
||||
pub fn contains_range(self, other: TextRange) -> bool {
|
||||
self.start() <= other.start() && other.end() <= self.end()
|
||||
}
|
||||
|
||||
/// The range covered by both ranges, if it exists.
|
||||
/// If the ranges touch but do not overlap, the output range is empty.
|
||||
///
|
||||
/// # Examples
|
||||
///
|
||||
/// ```rust
|
||||
/// # use text_size::*;
|
||||
/// assert_eq!(
|
||||
/// TextRange::intersect(
|
||||
/// TextRange::new(0.into(), 10.into()),
|
||||
/// TextRange::new(5.into(), 15.into()),
|
||||
/// ),
|
||||
/// Some(TextRange::new(5.into(), 10.into())),
|
||||
/// );
|
||||
/// ```
|
||||
#[inline]
|
||||
pub fn intersect(self, other: TextRange) -> Option<TextRange> {
|
||||
let start = cmp::max(self.start(), other.start());
|
||||
let end = cmp::min(self.end(), other.end());
|
||||
if end < start {
|
||||
return None;
|
||||
}
|
||||
Some(TextRange::new(start, end))
|
||||
}
|
||||
|
||||
/// Extends the range to cover `other` as well.
|
||||
///
|
||||
/// # Examples
|
||||
///
|
||||
/// ```rust
|
||||
/// # use text_size::*;
|
||||
/// assert_eq!(
|
||||
/// TextRange::cover(
|
||||
/// TextRange::new(0.into(), 5.into()),
|
||||
/// TextRange::new(15.into(), 20.into()),
|
||||
/// ),
|
||||
/// TextRange::new(0.into(), 20.into()),
|
||||
/// );
|
||||
/// ```
|
||||
#[inline]
|
||||
pub fn cover(self, other: TextRange) -> TextRange {
|
||||
let start = cmp::min(self.start(), other.start());
|
||||
let end = cmp::max(self.end(), other.end());
|
||||
TextRange::new(start, end)
|
||||
}
|
||||
|
||||
/// Extends the range to cover `other` offsets as well.
|
||||
///
|
||||
/// # Examples
|
||||
///
|
||||
/// ```rust
|
||||
/// # use text_size::*;
|
||||
/// assert_eq!(
|
||||
/// TextRange::empty(0.into()).cover_offset(20.into()),
|
||||
/// TextRange::new(0.into(), 20.into()),
|
||||
/// )
|
||||
/// ```
|
||||
#[inline]
|
||||
pub fn cover_offset(self, offset: TextSize) -> TextRange {
|
||||
self.cover(TextRange::empty(offset))
|
||||
}
|
||||
|
||||
/// Add an offset to this range.
|
||||
///
|
||||
/// Note that this is not appropriate for changing where a `TextRange` is
|
||||
/// within some string; rather, it is for changing the reference anchor
|
||||
/// that the `TextRange` is measured against.
|
||||
///
|
||||
/// The unchecked version (`Add::add`) will _always_ panic on overflow,
|
||||
/// in contrast to primitive integers, which check in debug mode only.
|
||||
#[inline]
|
||||
pub fn checked_add(self, offset: TextSize) -> Option<TextRange> {
|
||||
Some(TextRange {
|
||||
start: self.start.checked_add(offset)?,
|
||||
end: self.end.checked_add(offset)?,
|
||||
})
|
||||
}
|
||||
|
||||
/// Subtract an offset from this range.
|
||||
///
|
||||
/// Note that this is not appropriate for changing where a `TextRange` is
|
||||
/// within some string; rather, it is for changing the reference anchor
|
||||
/// that the `TextRange` is measured against.
|
||||
///
|
||||
/// The unchecked version (`Sub::sub`) will _always_ panic on overflow,
|
||||
/// in contrast to primitive integers, which check in debug mode only.
|
||||
#[inline]
|
||||
pub fn checked_sub(self, offset: TextSize) -> Option<TextRange> {
|
||||
Some(TextRange {
|
||||
start: self.start.checked_sub(offset)?,
|
||||
end: self.end.checked_sub(offset)?,
|
||||
})
|
||||
}
|
||||
|
||||
/// Relative order of the two ranges (overlapping ranges are considered
|
||||
/// equal).
|
||||
///
|
||||
///
|
||||
/// This is useful when, for example, binary searching an array of disjoint
|
||||
/// ranges.
|
||||
///
|
||||
/// # Examples
|
||||
///
|
||||
/// ```
|
||||
/// # use text_size::*;
|
||||
/// # use std::cmp::Ordering;
|
||||
///
|
||||
/// let a = TextRange::new(0.into(), 3.into());
|
||||
/// let b = TextRange::new(4.into(), 5.into());
|
||||
/// assert_eq!(a.ordering(b), Ordering::Less);
|
||||
///
|
||||
/// let a = TextRange::new(0.into(), 3.into());
|
||||
/// let b = TextRange::new(3.into(), 5.into());
|
||||
/// assert_eq!(a.ordering(b), Ordering::Less);
|
||||
///
|
||||
/// let a = TextRange::new(0.into(), 3.into());
|
||||
/// let b = TextRange::new(2.into(), 5.into());
|
||||
/// assert_eq!(a.ordering(b), Ordering::Equal);
|
||||
///
|
||||
/// let a = TextRange::new(0.into(), 3.into());
|
||||
/// let b = TextRange::new(2.into(), 2.into());
|
||||
/// assert_eq!(a.ordering(b), Ordering::Equal);
|
||||
///
|
||||
/// let a = TextRange::new(2.into(), 3.into());
|
||||
/// let b = TextRange::new(2.into(), 2.into());
|
||||
/// assert_eq!(a.ordering(b), Ordering::Greater);
|
||||
/// ```
|
||||
#[inline]
|
||||
pub fn ordering(self, other: TextRange) -> Ordering {
|
||||
if self.end() <= other.start() {
|
||||
Ordering::Less
|
||||
} else if other.end() <= self.start() {
|
||||
Ordering::Greater
|
||||
} else {
|
||||
Ordering::Equal
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Index<TextRange> for str {
|
||||
type Output = str;
|
||||
#[inline]
|
||||
fn index(&self, index: TextRange) -> &str {
|
||||
&self[Range::<usize>::from(index)]
|
||||
}
|
||||
}
|
||||
|
||||
impl Index<TextRange> for String {
|
||||
type Output = str;
|
||||
#[inline]
|
||||
fn index(&self, index: TextRange) -> &str {
|
||||
&self[Range::<usize>::from(index)]
|
||||
}
|
||||
}
|
||||
|
||||
impl IndexMut<TextRange> for str {
|
||||
#[inline]
|
||||
fn index_mut(&mut self, index: TextRange) -> &mut str {
|
||||
&mut self[Range::<usize>::from(index)]
|
||||
}
|
||||
}
|
||||
|
||||
impl IndexMut<TextRange> for String {
|
||||
#[inline]
|
||||
fn index_mut(&mut self, index: TextRange) -> &mut str {
|
||||
&mut self[Range::<usize>::from(index)]
|
||||
}
|
||||
}
|
||||
|
||||
impl RangeBounds<TextSize> for TextRange {
|
||||
fn start_bound(&self) -> Bound<&TextSize> {
|
||||
Bound::Included(&self.start)
|
||||
}
|
||||
|
||||
fn end_bound(&self) -> Bound<&TextSize> {
|
||||
Bound::Excluded(&self.end)
|
||||
}
|
||||
}
|
||||
|
||||
impl<T> From<TextRange> for Range<T>
|
||||
where
|
||||
T: From<TextSize>,
|
||||
{
|
||||
#[inline]
|
||||
fn from(r: TextRange) -> Self {
|
||||
r.start().into()..r.end().into()
|
||||
}
|
||||
}
|
||||
|
||||
macro_rules! ops {
|
||||
(impl $Op:ident for TextRange by fn $f:ident = $op:tt) => {
|
||||
impl $Op<&TextSize> for TextRange {
|
||||
type Output = TextRange;
|
||||
#[inline]
|
||||
fn $f(self, other: &TextSize) -> TextRange {
|
||||
self $op *other
|
||||
}
|
||||
}
|
||||
impl<T> $Op<T> for &TextRange
|
||||
where
|
||||
TextRange: $Op<T, Output=TextRange>,
|
||||
{
|
||||
type Output = TextRange;
|
||||
#[inline]
|
||||
fn $f(self, other: T) -> TextRange {
|
||||
*self $op other
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
impl Add<TextSize> for TextRange {
|
||||
type Output = TextRange;
|
||||
#[inline]
|
||||
fn add(self, offset: TextSize) -> TextRange {
|
||||
self.checked_add(offset)
|
||||
.expect("TextRange +offset overflowed")
|
||||
}
|
||||
}
|
||||
|
||||
impl Sub<TextSize> for TextRange {
|
||||
type Output = TextRange;
|
||||
#[inline]
|
||||
fn sub(self, offset: TextSize) -> TextRange {
|
||||
self.checked_sub(offset)
|
||||
.expect("TextRange -offset overflowed")
|
||||
}
|
||||
}
|
||||
|
||||
ops!(impl Add for TextRange by fn add = +);
|
||||
ops!(impl Sub for TextRange by fn sub = -);
|
||||
|
||||
impl<A> AddAssign<A> for TextRange
|
||||
where
|
||||
TextRange: Add<A, Output = TextRange>,
|
||||
{
|
||||
#[inline]
|
||||
fn add_assign(&mut self, rhs: A) {
|
||||
*self = *self + rhs
|
||||
}
|
||||
}
|
||||
|
||||
impl<S> SubAssign<S> for TextRange
|
||||
where
|
||||
TextRange: Sub<S, Output = TextRange>,
|
||||
{
|
||||
#[inline]
|
||||
fn sub_assign(&mut self, rhs: S) {
|
||||
*self = *self - rhs
|
||||
}
|
||||
}
|
||||
48
lib/text-size/src/serde_impls.rs
Normal file
48
lib/text-size/src/serde_impls.rs
Normal file
@ -0,0 +1,48 @@
|
||||
use {
|
||||
crate::{TextRange, TextSize},
|
||||
serde::{de, Deserialize, Deserializer, Serialize, Serializer},
|
||||
};
|
||||
|
||||
impl Serialize for TextSize {
|
||||
fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
|
||||
where
|
||||
S: Serializer,
|
||||
{
|
||||
self.raw.serialize(serializer)
|
||||
}
|
||||
}
|
||||
|
||||
impl<'de> Deserialize<'de> for TextSize {
|
||||
fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
|
||||
where
|
||||
D: Deserializer<'de>,
|
||||
{
|
||||
u32::deserialize(deserializer).map(TextSize::from)
|
||||
}
|
||||
}
|
||||
|
||||
impl Serialize for TextRange {
|
||||
fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
|
||||
where
|
||||
S: Serializer,
|
||||
{
|
||||
(self.start(), self.end()).serialize(serializer)
|
||||
}
|
||||
}
|
||||
|
||||
impl<'de> Deserialize<'de> for TextRange {
|
||||
#[allow(clippy::nonminimal_bool)]
|
||||
fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
|
||||
where
|
||||
D: Deserializer<'de>,
|
||||
{
|
||||
let (start, end) = Deserialize::deserialize(deserializer)?;
|
||||
if !(start <= end) {
|
||||
return Err(de::Error::custom(format!(
|
||||
"invalid range: {:?}..{:?}",
|
||||
start, end
|
||||
)));
|
||||
}
|
||||
Ok(TextRange::new(start, end))
|
||||
}
|
||||
}
|
||||
173
lib/text-size/src/size.rs
Normal file
173
lib/text-size/src/size.rs
Normal file
@ -0,0 +1,173 @@
|
||||
use {
|
||||
crate::TextLen,
|
||||
std::{
|
||||
convert::TryFrom,
|
||||
fmt, iter,
|
||||
num::TryFromIntError,
|
||||
ops::{Add, AddAssign, Sub, SubAssign},
|
||||
u32,
|
||||
},
|
||||
};
|
||||
|
||||
/// A measure of text length. Also, equivalently, an index into text.
|
||||
///
|
||||
/// This is a UTF-8 bytes offset stored as `u32`, but
|
||||
/// most clients should treat it as an opaque measure.
|
||||
///
|
||||
/// For cases that need to escape `TextSize` and return to working directly
|
||||
/// with primitive integers, `TextSize` can be converted losslessly to/from
|
||||
/// `u32` via [`From`] conversions as well as losslessly be converted [`Into`]
|
||||
/// `usize`. The `usize -> TextSize` direction can be done via [`TryFrom`].
|
||||
///
|
||||
/// These escape hatches are primarily required for unit testing and when
|
||||
/// converting from UTF-8 size to another coordinate space, such as UTF-16.
|
||||
#[derive(Clone, Copy, Default, PartialEq, Eq, PartialOrd, Ord, Hash)]
|
||||
pub struct TextSize {
|
||||
pub(crate) raw: u32,
|
||||
}
|
||||
|
||||
impl fmt::Debug for TextSize {
|
||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||
write!(f, "{}", self.raw)
|
||||
}
|
||||
}
|
||||
|
||||
impl TextSize {
|
||||
/// Creates a new instance of `TextSize` from a raw `u32`.
|
||||
#[inline]
|
||||
pub const fn new(raw: u32) -> TextSize {
|
||||
TextSize { raw }
|
||||
}
|
||||
|
||||
/// The text size of some primitive text-like object.
|
||||
///
|
||||
/// Accepts `char`, `&str`, and `&String`.
|
||||
///
|
||||
/// # Examples
|
||||
///
|
||||
/// ```rust
|
||||
/// # use text_size::*;
|
||||
/// let char_size = TextSize::of('🦀');
|
||||
/// assert_eq!(char_size, TextSize::from(4));
|
||||
///
|
||||
/// let str_size = TextSize::of("rust-analyzer");
|
||||
/// assert_eq!(str_size, TextSize::from(13));
|
||||
/// ```
|
||||
#[inline]
|
||||
pub fn of<T: TextLen>(text: T) -> TextSize {
|
||||
text.text_len()
|
||||
}
|
||||
}
|
||||
|
||||
/// Methods to act like a primitive integer type, where reasonably applicable.
|
||||
// Last updated for parity with Rust 1.42.0.
|
||||
impl TextSize {
|
||||
/// Checked addition. Returns `None` if overflow occurred.
|
||||
#[inline]
|
||||
pub const fn checked_add(self, rhs: TextSize) -> Option<TextSize> {
|
||||
match self.raw.checked_add(rhs.raw) {
|
||||
Some(raw) => Some(TextSize { raw }),
|
||||
None => None,
|
||||
}
|
||||
}
|
||||
|
||||
/// Checked subtraction. Returns `None` if overflow occurred.
|
||||
#[inline]
|
||||
pub const fn checked_sub(self, rhs: TextSize) -> Option<TextSize> {
|
||||
match self.raw.checked_sub(rhs.raw) {
|
||||
Some(raw) => Some(TextSize { raw }),
|
||||
None => None,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl From<u32> for TextSize {
|
||||
#[inline]
|
||||
fn from(raw: u32) -> Self {
|
||||
TextSize { raw }
|
||||
}
|
||||
}
|
||||
|
||||
impl From<TextSize> for u32 {
|
||||
#[inline]
|
||||
fn from(value: TextSize) -> Self {
|
||||
value.raw
|
||||
}
|
||||
}
|
||||
|
||||
impl TryFrom<usize> for TextSize {
|
||||
type Error = TryFromIntError;
|
||||
#[inline]
|
||||
fn try_from(value: usize) -> Result<Self, TryFromIntError> {
|
||||
Ok(u32::try_from(value)?.into())
|
||||
}
|
||||
}
|
||||
|
||||
impl From<TextSize> for usize {
|
||||
#[inline]
|
||||
fn from(value: TextSize) -> Self {
|
||||
value.raw as usize
|
||||
}
|
||||
}
|
||||
|
||||
macro_rules! ops {
|
||||
(impl $Op:ident for TextSize by fn $f:ident = $op:tt) => {
|
||||
impl $Op<TextSize> for TextSize {
|
||||
type Output = TextSize;
|
||||
#[inline]
|
||||
fn $f(self, other: TextSize) -> TextSize {
|
||||
TextSize { raw: self.raw $op other.raw }
|
||||
}
|
||||
}
|
||||
impl $Op<&TextSize> for TextSize {
|
||||
type Output = TextSize;
|
||||
#[inline]
|
||||
fn $f(self, other: &TextSize) -> TextSize {
|
||||
self $op *other
|
||||
}
|
||||
}
|
||||
impl<T> $Op<T> for &TextSize
|
||||
where
|
||||
TextSize: $Op<T, Output=TextSize>,
|
||||
{
|
||||
type Output = TextSize;
|
||||
#[inline]
|
||||
fn $f(self, other: T) -> TextSize {
|
||||
*self $op other
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
ops!(impl Add for TextSize by fn add = +);
|
||||
ops!(impl Sub for TextSize by fn sub = -);
|
||||
|
||||
impl<A> AddAssign<A> for TextSize
|
||||
where
|
||||
TextSize: Add<A, Output = TextSize>,
|
||||
{
|
||||
#[inline]
|
||||
fn add_assign(&mut self, rhs: A) {
|
||||
*self = *self + rhs
|
||||
}
|
||||
}
|
||||
|
||||
impl<S> SubAssign<S> for TextSize
|
||||
where
|
||||
TextSize: Sub<S, Output = TextSize>,
|
||||
{
|
||||
#[inline]
|
||||
fn sub_assign(&mut self, rhs: S) {
|
||||
*self = *self - rhs
|
||||
}
|
||||
}
|
||||
|
||||
impl<A> iter::Sum<A> for TextSize
|
||||
where
|
||||
TextSize: Add<A, Output = TextSize>,
|
||||
{
|
||||
#[inline]
|
||||
fn sum<I: Iterator<Item = A>>(iter: I) -> TextSize {
|
||||
iter.fold(0.into(), Add::add)
|
||||
}
|
||||
}
|
||||
36
lib/text-size/src/traits.rs
Normal file
36
lib/text-size/src/traits.rs
Normal file
@ -0,0 +1,36 @@
|
||||
use {crate::TextSize, std::convert::TryInto};
|
||||
|
||||
use priv_in_pub::Sealed;
|
||||
mod priv_in_pub {
|
||||
pub trait Sealed {}
|
||||
}
|
||||
|
||||
/// Primitives with a textual length that can be passed to [`TextSize::of`].
|
||||
pub trait TextLen: Copy + Sealed {
|
||||
/// The textual length of this primitive.
|
||||
fn text_len(self) -> TextSize;
|
||||
}
|
||||
|
||||
impl Sealed for &'_ str {}
|
||||
impl TextLen for &'_ str {
|
||||
#[inline]
|
||||
fn text_len(self) -> TextSize {
|
||||
self.len().try_into().unwrap()
|
||||
}
|
||||
}
|
||||
|
||||
impl Sealed for &'_ String {}
|
||||
impl TextLen for &'_ String {
|
||||
#[inline]
|
||||
fn text_len(self) -> TextSize {
|
||||
self.as_str().text_len()
|
||||
}
|
||||
}
|
||||
|
||||
impl Sealed for char {}
|
||||
impl TextLen for char {
|
||||
#[inline]
|
||||
fn text_len(self) -> TextSize {
|
||||
(self.len_utf8() as u32).into()
|
||||
}
|
||||
}
|
||||
18
lib/text-size/tests/auto_traits.rs
Normal file
18
lib/text-size/tests/auto_traits.rs
Normal file
@ -0,0 +1,18 @@
|
||||
use {
|
||||
static_assertions::*,
|
||||
std::{
|
||||
fmt::Debug,
|
||||
hash::Hash,
|
||||
marker::{Send, Sync},
|
||||
panic::{RefUnwindSafe, UnwindSafe},
|
||||
},
|
||||
text_size::*,
|
||||
};
|
||||
|
||||
// auto traits
|
||||
assert_impl_all!(TextSize: Send, Sync, Unpin, UnwindSafe, RefUnwindSafe);
|
||||
assert_impl_all!(TextRange: Send, Sync, Unpin, UnwindSafe, RefUnwindSafe);
|
||||
|
||||
// common traits
|
||||
assert_impl_all!(TextSize: Copy, Debug, Default, Hash, Ord);
|
||||
assert_impl_all!(TextRange: Copy, Debug, Default, Hash, Eq);
|
||||
24
lib/text-size/tests/constructors.rs
Normal file
24
lib/text-size/tests/constructors.rs
Normal file
@ -0,0 +1,24 @@
|
||||
use text_size::TextSize;
|
||||
|
||||
#[derive(Copy, Clone)]
|
||||
struct BadRope<'a>(&'a [&'a str]);
|
||||
|
||||
impl BadRope<'_> {
|
||||
fn text_len(self) -> TextSize {
|
||||
self.0.iter().copied().map(TextSize::of).sum()
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn main() {
|
||||
let x: char = 'c';
|
||||
let _ = TextSize::of(x);
|
||||
|
||||
let x: &str = "hello";
|
||||
let _ = TextSize::of(x);
|
||||
|
||||
let x: &String = &"hello".into();
|
||||
let _ = TextSize::of(x);
|
||||
|
||||
let _ = BadRope(&[""]).text_len();
|
||||
}
|
||||
8
lib/text-size/tests/indexing.rs
Normal file
8
lib/text-size/tests/indexing.rs
Normal file
@ -0,0 +1,8 @@
|
||||
use text_size::*;
|
||||
|
||||
#[test]
|
||||
fn main() {
|
||||
let range = TextRange::default();
|
||||
_ = &""[range];
|
||||
_ = &String::new()[range];
|
||||
}
|
||||
76
lib/text-size/tests/main.rs
Normal file
76
lib/text-size/tests/main.rs
Normal file
@ -0,0 +1,76 @@
|
||||
use {std::ops, text_size::*};
|
||||
|
||||
fn size(x: u32) -> TextSize {
|
||||
TextSize::from(x)
|
||||
}
|
||||
|
||||
fn range(x: ops::Range<u32>) -> TextRange {
|
||||
TextRange::new(x.start.into(), x.end.into())
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn sum() {
|
||||
let xs: Vec<TextSize> = vec![size(0), size(1), size(2)];
|
||||
assert_eq!(xs.iter().sum::<TextSize>(), size(3));
|
||||
assert_eq!(xs.into_iter().sum::<TextSize>(), size(3));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn math() {
|
||||
assert_eq!(size(10) + size(5), size(15));
|
||||
assert_eq!(size(10) - size(5), size(5));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn checked_math() {
|
||||
assert_eq!(size(1).checked_add(size(1)), Some(size(2)));
|
||||
assert_eq!(size(1).checked_sub(size(1)), Some(size(0)));
|
||||
assert_eq!(size(1).checked_sub(size(2)), None);
|
||||
assert_eq!(size(!0).checked_add(size(1)), None);
|
||||
}
|
||||
|
||||
#[test]
|
||||
#[rustfmt::skip]
|
||||
fn contains() {
|
||||
assert!( range(2..4).contains_range(range(2..3)));
|
||||
assert!( ! range(2..4).contains_range(range(1..3)));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn intersect() {
|
||||
assert_eq!(range(1..2).intersect(range(2..3)), Some(range(2..2)));
|
||||
assert_eq!(range(1..5).intersect(range(2..3)), Some(range(2..3)));
|
||||
assert_eq!(range(1..2).intersect(range(3..4)), None);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn cover() {
|
||||
assert_eq!(range(1..2).cover(range(2..3)), range(1..3));
|
||||
assert_eq!(range(1..5).cover(range(2..3)), range(1..5));
|
||||
assert_eq!(range(1..2).cover(range(4..5)), range(1..5));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn cover_offset() {
|
||||
assert_eq!(range(1..3).cover_offset(size(0)), range(0..3));
|
||||
assert_eq!(range(1..3).cover_offset(size(1)), range(1..3));
|
||||
assert_eq!(range(1..3).cover_offset(size(2)), range(1..3));
|
||||
assert_eq!(range(1..3).cover_offset(size(3)), range(1..3));
|
||||
assert_eq!(range(1..3).cover_offset(size(4)), range(1..4));
|
||||
}
|
||||
|
||||
#[test]
|
||||
#[rustfmt::skip]
|
||||
fn contains_point() {
|
||||
assert!( ! range(1..3).contains(size(0)));
|
||||
assert!( range(1..3).contains(size(1)));
|
||||
assert!( range(1..3).contains(size(2)));
|
||||
assert!( ! range(1..3).contains(size(3)));
|
||||
assert!( ! range(1..3).contains(size(4)));
|
||||
|
||||
assert!( ! range(1..3).contains_inclusive(size(0)));
|
||||
assert!( range(1..3).contains_inclusive(size(1)));
|
||||
assert!( range(1..3).contains_inclusive(size(2)));
|
||||
assert!( range(1..3).contains_inclusive(size(3)));
|
||||
assert!( ! range(1..3).contains_inclusive(size(4)));
|
||||
}
|
||||
79
lib/text-size/tests/serde.rs
Normal file
79
lib/text-size/tests/serde.rs
Normal file
@ -0,0 +1,79 @@
|
||||
use {serde_test::*, std::ops, text_size::*};
|
||||
|
||||
fn size(x: u32) -> TextSize {
|
||||
TextSize::from(x)
|
||||
}
|
||||
|
||||
fn range(x: ops::Range<u32>) -> TextRange {
|
||||
TextRange::new(x.start.into(), x.end.into())
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn size_serialization() {
|
||||
assert_tokens(&size(00), &[Token::U32(00)]);
|
||||
assert_tokens(&size(10), &[Token::U32(10)]);
|
||||
assert_tokens(&size(20), &[Token::U32(20)]);
|
||||
assert_tokens(&size(30), &[Token::U32(30)]);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn range_serialization() {
|
||||
assert_tokens(
|
||||
&range(00..10),
|
||||
&[
|
||||
Token::Tuple { len: 2 },
|
||||
Token::U32(00),
|
||||
Token::U32(10),
|
||||
Token::TupleEnd,
|
||||
],
|
||||
);
|
||||
assert_tokens(
|
||||
&range(10..20),
|
||||
&[
|
||||
Token::Tuple { len: 2 },
|
||||
Token::U32(10),
|
||||
Token::U32(20),
|
||||
Token::TupleEnd,
|
||||
],
|
||||
);
|
||||
assert_tokens(
|
||||
&range(20..30),
|
||||
&[
|
||||
Token::Tuple { len: 2 },
|
||||
Token::U32(20),
|
||||
Token::U32(30),
|
||||
Token::TupleEnd,
|
||||
],
|
||||
);
|
||||
assert_tokens(
|
||||
&range(30..40),
|
||||
&[
|
||||
Token::Tuple { len: 2 },
|
||||
Token::U32(30),
|
||||
Token::U32(40),
|
||||
Token::TupleEnd,
|
||||
],
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn invalid_range_deserialization() {
|
||||
assert_tokens::<TextRange>(
|
||||
&range(62..92),
|
||||
&[
|
||||
Token::Tuple { len: 2 },
|
||||
Token::U32(62),
|
||||
Token::U32(92),
|
||||
Token::TupleEnd,
|
||||
],
|
||||
);
|
||||
assert_de_tokens_error::<TextRange>(
|
||||
&[
|
||||
Token::Tuple { len: 2 },
|
||||
Token::U32(92),
|
||||
Token::U32(62),
|
||||
Token::TupleEnd,
|
||||
],
|
||||
"invalid range: 92..62",
|
||||
);
|
||||
}
|
||||
Loading…
x
Reference in New Issue
Block a user