chore: reduce usage of String.length (#13681)

This PR reduces the usage of `String.length` in our codebase.

This is just the first step of many towards eliminating `String.length`.
This commit is contained in:
Julia Markus Himmel 2026-05-07 16:27:06 +02:00 committed by GitHub
parent 422920f643
commit 79659457fb
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
36 changed files with 180 additions and 134 deletions

View file

@ -10,6 +10,7 @@ public import Init.Data.Repr
public import Init.Data.Char.Basic
public import Init.Data.ToString.Basic
public import Init.Data.String.Basic
public import Init.Data.String.Length
import Init.NotationExtra
import all Init.Data.Repr
import Init.Omega

View file

@ -31,3 +31,4 @@ public import Init.Data.String.Subslice
public import Init.Data.String.Iter
public import Init.Data.String.Iterate
public import Init.Data.String.Hashable
public import Init.Data.String.Length

View file

@ -264,28 +264,6 @@ theorem String.toList_empty : "".toList = [] := by
theorem String.data_empty : "".toList = [] :=
toList_empty
/--
Returns the length of a string in Unicode code points.
Examples:
* `"".length = 0`
* `"abc".length = 3`
* `"L∃∀N".length = 4`
-/
@[extern "lean_string_length", expose, tagged_return]
def String.length (b : @& String) : Nat :=
b.toList.length
@[simp]
theorem String.Internal.size_toArray {b : String} : (String.Internal.toArray b).size = b.length :=
(rfl)
@[simp]
theorem String.length_toList {s : String} : s.toList.length = s.length := (rfl)
@[deprecated String.length_toList (since := "2025-10-30")]
theorem String.length_data {b : String} : b.toList.length = b.length := (rfl)
private theorem ByteArray.utf8Decode?go_eq_utf8Decode?go_extract {b : ByteArray} {hi : i ≤ b.size} {acc : Array Char} :
utf8Decode?.go b i acc hi = (utf8Decode?.go (b.extract i b.size) 0 #[] (by simp)).map (acc ++ ·) := by
fun_cases utf8Decode?.go b i acc hi with
@ -436,14 +414,6 @@ theorem String.ofList_eq_empty_iff {l : List Char} : String.ofList l = "" ↔ l
theorem List.asString_eq_empty_iff {l : List Char} : String.ofList l = "" ↔ l = [] :=
String.ofList_eq_empty_iff
@[simp]
theorem String.length_ofList {l : List Char} : (String.ofList l).length = l.length := by
rw [← String.length_toList, String.toList_ofList]
@[deprecated String.length_ofList (since := "2025-10-30")]
theorem List.length_asString {l : List Char} : (String.ofList l).length = l.length :=
String.length_ofList
end
namespace String
@ -531,7 +501,7 @@ theorem Pos.Raw.isValid_ofList {l : List Char} {p : Pos.Raw} :
rw [isValid_iff_exists_append]
refine ⟨?_, ?_⟩
· rintro ⟨t₁, t₂, ht, rfl⟩
refine ⟨t₁.length, ?_⟩
refine ⟨t₁.toList.length, ?_⟩
have := congrArg String.toList ht
simp only [String.toList_ofList, String.toList_append] at this
simp [this]
@ -3127,8 +3097,6 @@ namespace String
theorem ext {s₁ s₂ : String} (h : s₁.toList = s₂.toList) : s₁ = s₂ :=
toList_injective h
@[simp] theorem length_empty : "".length = 0 := by simp [← length_toList, toList_empty]
@[deprecated singleton_eq_ofList (since := "2025-10-30")]
theorem singleton_eq {c : Char} : String.singleton c = ofList [c] :=
singleton_eq_ofList
@ -3140,10 +3108,6 @@ theorem singleton_eq {c : Char} : String.singleton c = ofList [c] :=
theorem data_singleton (c : Char) : (String.singleton c).toList = [c] :=
toList_singleton c
@[simp]
theorem length_singleton {c : Char} : (String.singleton c).length = 1 := by
simp [← length_toList]
@[simp]
theorem toList_push (c : Char) : (String.push s c).toList = s.toList ++ [c] := by
simp [← append_singleton]
@ -3152,15 +3116,6 @@ theorem toList_push (c : Char) : (String.push s c).toList = s.toList ++ [c] := b
theorem data_push (c : Char) : (String.push s c).toList = s.toList ++ [c] :=
toList_push c
@[simp] theorem length_push (c : Char) : (String.push s c).length = s.length + 1 := by
simp [← length_toList]
@[simp] theorem length_pushn (c : Char) (n : Nat) : (pushn s c n).length = s.length + n := by
rw [pushn_eq_repeat_push]; induction n <;> simp [Nat.repeat, Nat.add_assoc, *]
@[simp] theorem length_append (s t : String) : (s ++ t).length = s.length + t.length := by
simp [← length_toList]
theorem lt_iff {s t : String} : s < t ↔ s.toList < t.toList := .rfl
namespace Pos.Raw
@ -3211,11 +3166,3 @@ theorem bytes_inj {s t : String} : s.toByteArray = t.toByteArray ↔ s = t :=
toByteArray_inj
end String
namespace Char
@[deprecated String.length_singleton (since := "2026-02-12")]
theorem length_toString (c : Char) : c.toString.length = 1 := by
simp
end Char

View file

@ -11,6 +11,7 @@ public import Init.Data.String.Basic
import all Init.Data.String.Basic
import Init.Data.String.Search
import Init.Data.String.Termination
import Init.Data.String.Length
public section

View file

@ -21,3 +21,4 @@ public import Init.Data.String.Lemmas.Iter
public import Init.Data.String.Lemmas.Hashable
public import Init.Data.String.Lemmas.TakeDrop
public import Init.Data.String.Lemmas.StringOrder
public import Init.Data.String.Lemmas.Length

View file

@ -81,11 +81,6 @@ theorem toList_join {l : List String} : (String.join l).toList = l.flatMap Strin
theorem join_append {l m : List String} : String.join (l ++ m) = String.join l ++ String.join m := by
simp [← toList_inj]
@[simp]
theorem length_join {l : List String} : (String.join l).length = (l.map String.length).sum := by
simp only [← length_toList, toList_join, List.length_flatMap]
simp
namespace Slice
@[simp]

View file

@ -162,10 +162,6 @@ theorem Slice.isEmpty_slice_eq_false_iff {s : Slice} {p₁ p₂ h} :
(s.slice p₁ p₂ h).isEmpty = false ↔ p₁ ≠ p₂ := by
rw [ne_eq, ← isEmpty_slice (h := h), Bool.not_eq_true]
@[simp]
theorem length_eq_zero_iff {s : String} : s.length = 0 ↔ s = "" := by
simp [← length_toList]
@[simp]
theorem toByteArray_eq_empty_iff {s : String} :
s.toByteArray = ByteArray.empty ↔ s = "" := by

View file

@ -0,0 +1,31 @@
/-
Copyright (c) 2026 Lean FRO, LLC. All rights reserved.
Released under Apache 2.0 license as described in the file LICENSE.
Authors: Markus Himmel
-/
module
prelude
public import Init.Data.String.Length
public import Init.Data.String.Modify
import Init.Data.String.Lemmas.Modify
import Init.Data.String.Lemmas.Intercalate
public section
namespace String
@[simp]
theorem length_eq_zero_iff {s : String} : s.length = 0 ↔ s = "" := by
simp [← length_toList]
@[simp]
theorem length_map {f : Char → Char} {s : String} : (s.map f).length = s.length := by
simp [← length_toList]
@[simp]
theorem length_join {l : List String} : (String.join l).length = (l.map String.length).sum := by
simp only [← length_toList, toList_join, List.length_flatMap]
simp [length_toList]
end String

View file

@ -57,10 +57,6 @@ theorem map_eq_internal {f : Char → Char} {s : String} : s.map f = .ofList (s.
apply String.toList_injective
simp only [toList_map, toList_ofList]
@[simp]
theorem length_map {f : Char → Char} {s : String} : (s.map f).length = s.length := by
simp [← length_toList]
@[simp]
theorem map_eq_empty {f : Char → Char} {s : String} : s.map f = "" ↔ s = "" := by
simp [← toList_eq_nil_iff]

View file

@ -301,8 +301,8 @@ This implies that the notion of match and longest match coincide.
class NoPrefixPatternModel {ρ : Type} (pat : ρ) [PatternModel pat] : Prop where
eq_empty (s t) : PatternModel.Matches pat s → PatternModel.Matches pat (s ++ t) → t = ""
theorem NoPrefixPatternModel.of_length_eq {ρ : Type} {pat : ρ} [PatternModel pat]
(h : ∀ s t, PatternModel.Matches pat s → PatternModel.Matches pat t → s.length = t.length) :
theorem NoPrefixPatternModel.of_length_toList_eq {ρ : Type} {pat : ρ} [PatternModel pat]
(h : ∀ s t, PatternModel.Matches pat s → PatternModel.Matches pat t → s.toList.length = t.toList.length) :
NoPrefixPatternModel pat where
eq_empty s t hs ht := by simpa using h s _ hs ht
@ -322,8 +322,8 @@ This implies that the notion of reverse match and longest reverse match coincide
class NoSuffixPatternModel {ρ : Type} (pat : ρ) [PatternModel pat] : Prop where
eq_empty (s t) : PatternModel.Matches pat t → PatternModel.Matches pat (s ++ t) → s = ""
theorem NoSuffixPatternModel.of_length_eq {ρ : Type} {pat : ρ} [PatternModel pat]
(h : ∀ s t, PatternModel.Matches pat s → PatternModel.Matches pat t → s.length = t.length) :
theorem NoSuffixPatternModel.of_length_toList_eq {ρ : Type} {pat : ρ} [PatternModel pat]
(h : ∀ s t, PatternModel.Matches pat s → PatternModel.Matches pat t → s.toList.length = t.toList.length) :
NoSuffixPatternModel pat where
eq_empty s t hs ht := by simpa using h t _ hs ht

View file

@ -33,10 +33,10 @@ instance {c : Char} : StrictPatternModel c where
not_matches_empty := by simp [PatternModel.Matches]
instance {c : Char} : NoPrefixPatternModel c :=
.of_length_eq (by simp +contextual [PatternModel.Matches])
.of_length_toList_eq (by simp +contextual [PatternModel.Matches])
instance {c : Char} : NoSuffixPatternModel c :=
.of_length_eq (by simp +contextual [PatternModel.Matches])
.of_length_toList_eq (by simp +contextual [PatternModel.Matches])
theorem isMatch_iff {c : Char} {s : Slice} {pos : s.Pos} :
IsMatch c pos ↔
@ -186,12 +186,12 @@ theorem isLongestMatchAtChain_iff {c : Char} {s : Slice} {pos pos' : s.Pos} :
theorem isLongestMatchAtChain_iff_toList {c : Char} {s : Slice} {pos pos' : s.Pos} :
IsLongestMatchAtChain c pos pos' ↔
∃ (h : pos ≤ pos'), (s.slice pos pos' h).copy.toList = List.replicate (s.slice pos pos' h).copy.length c := by
∃ (h : pos ≤ pos'), (s.slice pos pos' h).copy.toList = List.replicate (s.slice pos pos' h).copy.toList.length c := by
simp [isLongestMatchAtChain_iff_isLongestMatchAtChain_beq, CharPred.isLongestMatchAtChain_iff_toList,
List.eq_replicate_iff]
theorem isLongestMatchAtChain_startPos_endPos_iff_toList {c : Char} {s : Slice} :
IsLongestMatchAtChain c s.startPos s.endPos ↔ s.copy.toList = List.replicate s.copy.length c := by
IsLongestMatchAtChain c s.startPos s.endPos ↔ s.copy.toList = List.replicate s.copy.toList.length c := by
simp [isLongestMatchAtChain_iff_isLongestMatchAtChain_beq,
CharPred.isLongestMatchAtChain_startPos_endPos_iff_toList, List.eq_replicate_iff]
@ -216,12 +216,12 @@ theorem isLongestRevMatchAtChain_iff {c : Char} {s : Slice} {pos pos' : s.Pos} :
theorem isLongestRevMatchAtChain_iff_toList {c : Char} {s : Slice} {pos pos' : s.Pos} :
IsLongestRevMatchAtChain c pos pos' ↔
∃ (h : pos ≤ pos'), (s.slice pos pos' h).copy.toList = List.replicate (s.slice pos pos' h).copy.length c := by
∃ (h : pos ≤ pos'), (s.slice pos pos' h).copy.toList = List.replicate (s.slice pos pos' h).copy.toList.length c := by
simp [isLongestRevMatchAtChain_iff_isLongestRevMatchAtChain_beq, CharPred.isLongestRevMatchAtChain_iff_toList,
List.eq_replicate_iff]
theorem isLongestRevMatchAtChain_startPos_endPos_iff_toList {c : Char} {s : Slice} :
IsLongestRevMatchAtChain c s.startPos s.endPos ↔ s.copy.toList = List.replicate s.copy.length c := by
IsLongestRevMatchAtChain c s.startPos s.endPos ↔ s.copy.toList = List.replicate s.copy.toList.length c := by
simp [isLongestRevMatchAtChain_iff_isLongestRevMatchAtChain_beq,
CharPred.isLongestRevMatchAtChain_startPos_endPos_iff_toList, List.eq_replicate_iff]

View file

@ -33,10 +33,10 @@ instance {p : Char → Bool} : StrictPatternModel p where
not_matches_empty := by simp [PatternModel.Matches]
instance {p : Char → Bool} : NoPrefixPatternModel p :=
.of_length_eq (by simp +contextual [PatternModel.Matches])
.of_length_toList_eq (by simp +contextual [PatternModel.Matches])
instance {p : Char → Bool} : NoSuffixPatternModel p :=
.of_length_eq (by simp +contextual [PatternModel.Matches])
.of_length_toList_eq (by simp +contextual [PatternModel.Matches])
theorem isMatch_iff {p : Char → Bool} {s : Slice} {pos : s.Pos} :
IsMatch p pos ↔

View file

@ -29,10 +29,10 @@ theorem strictPatternModel {pat : Slice} (hpat : pat.isEmpty = false) : StrictPa
not_matches_empty := by simpa [PatternModel.Matches]
instance {pat : Slice} : NoPrefixPatternModel pat :=
.of_length_eq (by simp +contextual [PatternModel.Matches])
.of_length_toList_eq (by simp +contextual [PatternModel.Matches])
instance {pat : Slice} : NoSuffixPatternModel pat :=
.of_length_eq (by simp +contextual [PatternModel.Matches])
.of_length_toList_eq (by simp +contextual [PatternModel.Matches])
theorem isMatch_iff {pat s : Slice} {pos : s.Pos} :
IsMatch pat pos ↔ (s.sliceTo pos).copy = pat.copy := by
@ -287,10 +287,10 @@ theorem strictPatternModel {pat : String} (h : pat ≠ "") : StrictPatternModel
not_matches_empty := by simpa [PatternModel.Matches]
instance {pat : String} : NoPrefixPatternModel pat :=
.of_length_eq (by simp +contextual [PatternModel.Matches])
.of_length_toList_eq (by simp +contextual [PatternModel.Matches])
instance {pat : String} : NoSuffixPatternModel pat :=
.of_length_eq (by simp +contextual [PatternModel.Matches])
.of_length_toList_eq (by simp +contextual [PatternModel.Matches])
theorem isMatch_iff_slice {pat : String} {s : Slice} {pos : s.Pos} :
IsMatch (ρ := String) pat pos ↔ IsMatch (ρ := Slice) pat.toSlice pos := by

View file

@ -87,7 +87,7 @@ theorem get_eq_of_lt_skipPrefixWhile_char {c : Char} {s : Slice} {pos : s.Pos} (
Pos.get_eq_of_lt_skipWhile_char (Pos.startPos_le _) (by rwa [skipPrefixWhile_eq_skipWhile_startPos] at h)
@[simp]
theorem all_char_iff {c : Char} {s : Slice} : s.all c ↔ s.copy.toList = List.replicate s.copy.length c := by
theorem all_char_iff {c : Char} {s : Slice} : s.all c ↔ s.copy.toList = List.replicate s.copy.toList.length c := by
rw [Bool.eq_iff_iff]
simp [Pattern.Model.all_eq_true_iff, Char.isLongestMatchAtChain_startPos_endPos_iff_toList]
@ -123,7 +123,7 @@ theorem get_eq_of_skipSuffixWhile_le_char {c : Char} {s : Slice} {pos : s.Pos}
Pos.get_eq_of_revSkipWhile_le_char h' (by rwa [skipSuffixWhile_eq_revSkipWhile_endPos] at h)
@[simp]
theorem revAll_char_iff {c : Char} {s : Slice} : s.revAll c ↔ s.copy.toList = List.replicate s.copy.length c := by
theorem revAll_char_iff {c : Char} {s : Slice} : s.revAll c ↔ s.copy.toList = List.replicate s.copy.toList.length c := by
rw [Bool.eq_iff_iff]
simp [Pattern.Model.revAll_eq_true_iff, Char.isLongestRevMatchAtChain_startPos_endPos_iff_toList]

View file

@ -760,9 +760,9 @@ theorem splits_nextn_startPos (s : String) (n : Nat) :
simpa using s.splits_startPos.nextn n
theorem Slice.Pos.Splits.prevn {s : Slice} {t₁ t₂ : String} {p : s.Pos} (h : p.Splits t₁ t₂) (n : Nat) :
(p.prevn n).Splits (String.ofList (t₁.toList.take (t₁.length - n))) (String.ofList (t₁.toList.drop (t₁.length - n)) ++ t₂) := by
(p.prevn n).Splits (String.ofList (t₁.toList.take (t₁.toList.length - n))) (String.ofList (t₁.toList.drop (t₁.toList.length - n)) ++ t₂) := by
induction n generalizing p t₁ t₂ with
| zero => simpa [← String.length_toList]
| zero => simpa
| succ n ih =>
rw [Pos.prevn_add_one]
split
@ -771,16 +771,16 @@ theorem Slice.Pos.Splits.prevn {s : Slice} {t₁ t₂ : String} {p : s.Pos} (h :
simpa [Nat.add_sub_add_right, List.take_append, List.drop_append, ← append_assoc] using ih h.prev
theorem Slice.splits_prevn_endPos (s : Slice) (n : Nat) :
(s.endPos.prevn n).Splits (String.ofList (s.copy.toList.take (s.copy.length - n)))
(String.ofList (s.copy.toList.drop (s.copy.length - n))) := by
(s.endPos.prevn n).Splits (String.ofList (s.copy.toList.take (s.copy.toList.length - n)))
(String.ofList (s.copy.toList.drop (s.copy.toList.length - n))) := by
simpa using s.splits_endPos.prevn n
theorem Pos.Splits.prevn {s t₁ t₂ : String} {p : s.Pos} (h : p.Splits t₁ t₂) (n : Nat) :
(p.prevn n).Splits (String.ofList (t₁.toList.take (t₁.length - n))) (String.ofList (t₁.toList.drop (t₁.length - n)) ++ t₂) := by
(p.prevn n).Splits (String.ofList (t₁.toList.take (t₁.toList.length - n))) (String.ofList (t₁.toList.drop (t₁.toList.length - n)) ++ t₂) := by
simpa [← splits_toSlice_iff, toSlice_prevn] using h.toSlice.prevn n
theorem splits_prevn_endPos (s : String) (n : Nat) :
(s.endPos.prevn n).Splits (String.ofList (s.toList.take (s.length - n))) (String.ofList (s.toList.drop (s.length - n))) := by
(s.endPos.prevn n).Splits (String.ofList (s.toList.take (s.toList.length - n))) (String.ofList (s.toList.drop (s.toList.length - n))) := by
simpa using s.splits_endPos.prevn n
@[simp]

View file

@ -29,7 +29,7 @@ theorem dropEnd_eq_sliceTo {s : Slice} {n : Nat} : s.dropEnd n = s.sliceTo (s.en
@[simp]
theorem toList_copy_dropEnd {s : Slice} {n : Nat} :
(s.dropEnd n).copy.toList = s.copy.toList.take (s.copy.length - n) := by
(s.dropEnd n).copy.toList = s.copy.toList.take (s.copy.toList.length - n) := by
simp [dropEnd_eq_sliceTo, (s.splits_prevn_endPos n).copy_sliceTo_eq]
theorem take_eq_sliceTo {s : Slice} {n : Nat} : s.take n = s.sliceTo (s.startPos.nextn n) :=
@ -44,7 +44,7 @@ theorem takeEnd_eq_sliceFrom {s : Slice} {n : Nat} : s.takeEnd n = s.sliceFrom (
@[simp]
theorem toList_copy_takeEnd {s : Slice} {n : Nat} :
(s.takeEnd n).copy.toList = s.copy.toList.drop (s.copy.length - n) := by
(s.takeEnd n).copy.toList = s.copy.toList.drop (s.copy.toList.length - n) := by
simp [takeEnd_eq_sliceFrom, (s.splits_prevn_endPos n).copy_sliceFrom_eq]
end Slice
@ -63,7 +63,7 @@ theorem dropEnd_toSlice {s : String} {n : Nat} : s.toSlice.dropEnd n = s.dropEnd
@[simp]
theorem toList_copy_dropEnd {s : String} {n : Nat} :
(s.dropEnd n).copy.toList = s.toList.take (s.length - n) := by
(s.dropEnd n).copy.toList = s.toList.take (s.toList.length - n) := by
simp [← dropEnd_toSlice]
@[simp]
@ -80,7 +80,7 @@ theorem takeEnd_toSlice {s : String} {n : Nat} : s.toSlice.takeEnd n = s.takeEnd
@[simp]
theorem toList_copy_takeEnd {s : String} {n : Nat} :
(s.takeEnd n).copy.toList = s.toList.drop (s.length - n) := by
(s.takeEnd n).copy.toList = s.toList.drop (s.toList.length - n) := by
simp [← takeEnd_toSlice]
end String

View file

@ -0,0 +1,68 @@
/-
Copyright (c) 2026 Lean FRO, LLC. All rights reserved.
Released under Apache 2.0 license as described in the file LICENSE.
Authors: Julia Markus Himmel
-/
module
prelude
public import Init.Data.String.Basic
import Init.Data.Char.Lemmas
public section
namespace String
/--
Returns the length of a string in Unicode code points.
Examples:
* `"".length = 0`
* `"abc".length = 3`
* `"L∃∀N".length = 4`
-/
@[extern "lean_string_length", expose, tagged_return]
def length (b : @& String) : Nat :=
b.toList.length
@[simp]
theorem Internal.size_toArray {b : String} : (String.Internal.toArray b).size = b.length :=
(rfl)
theorem length_toList {s : String} : s.toList.length = s.length := (rfl)
@[deprecated String.length_toList (since := "2025-10-30")]
theorem length_data {b : String} : b.toList.length = b.length := (rfl)
@[simp]
theorem length_ofList {l : List Char} : (String.ofList l).length = l.length := by
rw [← String.length_toList, String.toList_ofList]
@[deprecated String.length_ofList (since := "2025-10-30")]
theorem _root_.List.length_asString {l : List Char} : (String.ofList l).length = l.length :=
String.length_ofList
@[simp] theorem length_empty : "".length = 0 := by simp [← length_toList, toList_empty]
@[simp]
theorem length_singleton {c : Char} : (String.singleton c).length = 1 := by
simp [← length_toList]
@[simp] theorem length_push (c : Char) : (String.push s c).length = s.length + 1 := by
simp [← length_toList]
@[simp] theorem length_pushn (c : Char) (n : Nat) : (pushn s c n).length = s.length + n := by
rw [pushn_eq_repeat_push]; induction n <;> simp [Nat.repeat, Nat.add_assoc, *]
@[simp] theorem length_append (s t : String) : (s ++ t).length = s.length + t.length := by
simp [← length_toList]
end String
namespace Char
@[deprecated String.length_singleton (since := "2026-02-12")]
theorem length_toString (c : Char) : c.toString.length = 1 := by
simp
end Char

View file

@ -28,7 +28,7 @@ open Slice.Pattern
Returns a {name}`String.Slice` obtained by removing the specified number of characters (Unicode code
points) from the start of the string.
If {name}`n` is greater than {lean}`s.length`, returns an empty slice.
If {name}`n` is greater than {lean}`s.toList.length`, returns an empty slice.
This is a cheap operation because it does not allocate a new string to hold the result.
To convert the result into a string, use {name}`String.Slice.copy`.
@ -50,7 +50,7 @@ def Internal.dropImpl (s : String) (n : Nat) : String :=
Returns a {name}`String.Slice` obtained by removing the specified number of characters (Unicode code
points) from the end of the string.
If {name}`n` is greater than {lean}`s.length`, returns an empty slice.
If {name}`n` is greater than {lean}`s.toList.length`, returns an empty slice.
This is a cheap operation because it does not allocate a new string to hold the result.
To convert the result into a string, use {name}`String.Slice.copy`.
@ -80,7 +80,7 @@ def Internal.dropRightImpl (s : String) (n : Nat) : String :=
Returns a {name}`String.Slice` that contains the first {name}`n` characters (Unicode code points) of
{name}`s`.
If {name}`n` is greater than {lean}`s.length`, returns {lean}`s.toSlice`.
If {name}`n` is greater than {lean}`s.toList.length`, returns {lean}`s.toSlice`.
This is a cheap operation because it does not allocate a new string to hold the result.
To convert the result into a string, use {name}`String.Slice.copy`.
@ -99,7 +99,7 @@ Examples:
Returns a {name}`String.Slice` that contains the last {name}`n` characters (Unicode code points) of
{name}`s`.
If {name}`n` is greater than {lean}`s.length`, returns {lean}`s.toSlice`.
If {name}`n` is greater than {lean}`s.toList.length`, returns {lean}`s.toSlice`.
This is a cheap operation because it does not allocate a new string to hold the result.
To convert the result into a string, use {name}`String.Slice.copy`.

View file

@ -11,6 +11,7 @@ import Init.Data.String.Search
public import Init.Data.ToString.Basic
import Init.Data.Iterators.Consumers.Collect
import Init.System.Platform
import Init.Data.String.Length
public section

View file

@ -1026,7 +1026,7 @@ and/or return data.
partial def Handle.lines (h : Handle) : IO (Array String) := do
let rec read (lines : Array String) := do
let line ← h.getLine
if line.length == 0 then
if line.isEmpty then
pure lines
else if line.back == '\n' then
let line := line.dropEnd 1 |>.copy
@ -1780,7 +1780,7 @@ and/or return data.
partial def lines (s : Stream) : IO (Array String) := do
let rec read (lines : Array String) := do
let line ← s.getLine
if line.length == 0 then
if line.isEmpty then
pure lines
else if line.back == '\n' then
let line := line.dropEnd 1 |>.copy

View file

@ -13,6 +13,7 @@ import Init.Data.String.Search
import Init.Omega
import Init.System.Platform
import Init.While
import Init.Data.String.Length
public section

View file

@ -12,6 +12,7 @@ import Init.Data.Nat.Order
import Init.Data.Order.Lemmas
import Init.Data.Range
import Init.While
import Init.Data.String.Length
set_option linter.missingDocs true

View file

@ -11,6 +11,7 @@ import Init.Data.String.TakeDrop
import Init.Data.Ord.String
import Init.Data.Ord.UInt
import Init.Data.String.Search
import Init.Data.String.Length
public section
namespace Lean

View file

@ -12,6 +12,7 @@ import Init.Data.String.TakeDrop
import Init.Data.String.Search
import Init.Data.ToString.Macro
import Init.While
import Init.Data.String.Length
public section
@ -114,28 +115,28 @@ def rewriteManualLinksCore (s : String) : Id (Array (Lean.Syntax.Range × String
let pre := iter
iter := iter.next h
if !lookingAt scheme pre then
match pre.skip? scheme with
| none =>
out := out.push c
continue
let start := pre.nextn scheme.length
let mut iter' := start
while h' : ¬iter'.IsAtEnd do
let c' := iter'.get h'
let pre' := iter'
iter' := iter'.next h'
if urlChar c' && ¬iter'.IsAtEnd then
continue
match rw (s.extract start pre') with
| .error err =>
errors := errors.push (⟨pre.offset, pre'.offset⟩, err)
out := out.push c
break
| .ok path =>
out := out ++ manualRoot ++ path
out := out.push c'
iter := iter'
break
| some start =>
let mut iter' := start
while h' : ¬iter'.IsAtEnd do
let c' := iter'.get h'
let pre' := iter'
iter' := iter'.next h'
if urlChar c' && ¬iter'.IsAtEnd then
continue
match rw (s.extract start pre') with
| .error err =>
errors := errors.push (⟨pre.offset, pre'.offset⟩, err)
out := out.push c
break
| .ok path =>
out := out ++ manualRoot ++ path
out := out.push c'
iter := iter'
break
pure (errors, out)
@ -154,12 +155,6 @@ where
c == '!' || c == '$' || c == '&' || c == '\'' || /- c == '(' || c == ')' || -/ c == '*' ||
c == '+' || c == ',' || c == ';' || c == '='
/--
Returns `true` if `goal` is a prefix of the string at the position pointed to by `iter`.
-/
lookingAt (goal : String) {s : String} (iter : s.Pos) : Bool :=
String.Pos.Raw.substrEq s iter.offset goal 0 goal.rawEndPos.byteIdx
/--
Rewrites Lean reference manual links in `docstring` to point at the reference manual.

View file

@ -11,6 +11,7 @@ prelude
public import Lean.DocString.Types
public import Init.Data.String.TakeDrop
public import Init.Data.String.Search
public import Init.Data.String.Length
import Init.Data.ToString.Macro
import Init.While

View file

@ -14,6 +14,7 @@ public import Lean.Util.Sorry
import Init.Data.String.Search
import Init.Data.Format.Macro
import Init.Data.Iterators.Consumers.Collect
import Init.Data.String.Length
public section
@ -688,7 +689,7 @@ def inlineExpr (e : Expr) (maxInlineLength := 30) : MessageData :=
(fun ctx => do
let msg := MessageData.ofExpr e
let render ← msg.formatExpensively ctx
if render.length > maxInlineLength || render.any (· == '\n') then
if render.positions.length > maxInlineLength || render.any (· == '\n') then
return indentD msg ++ "\n"
else
return " `" ++ msg ++ "` ")
@ -704,7 +705,7 @@ def inlineExprTrailing (e : Expr) (maxInlineLength := 30) : MessageData :=
(fun ctx => do
let msg := MessageData.ofExpr e
let render ← msg.formatExpensively ctx
if render.length > maxInlineLength || render.any (· == '\n') then
if render.positions.length > maxInlineLength || render.any (· == '\n') then
return indentD msg
else
return " `" ++ msg ++ "`")

View file

@ -127,7 +127,7 @@ def mkSpecTheoremName (env : Environment) (instName : Name) (privateSpecs : Bool
if privateSpecs then mkPrivateName env thmName else thmName
def startsWithFollowedByNumber (s p : String) : Bool :=
s.startsWith p && (s.drop p.length).isNat
(s.dropPrefix? p).any (·.isNat)
def isSpecThmLikeSuffix (fieldName : Name) (s : String) : Bool :=
s == s!"{fieldName}_spec" || startsWithFollowedByNumber s s!"{fieldName}_spec_"

View file

@ -18,6 +18,7 @@ import Init.Data.String.TakeDrop
import Init.Data.List.Monadic
import Init.Data.Option.BasicAux
import Init.Data.ToString.Macro
import Init.Data.String.Length
public section

View file

@ -9,6 +9,7 @@ module
prelude
public import Lean.Server.Rpc.Basic
import Init.Data.Array.GetLit
import Init.Data.String.Length
public section

View file

@ -11,6 +11,7 @@ public import Std.Net
public import Std.Http.Internal
public import Std.Http.Data.URI.Encoding
public import Init.Data.String.Search
public import Init.Data.String.Length
public section

View file

@ -9,6 +9,7 @@ prelude
public import Std.Internal.Parsec.Basic
public import Init.Data.String.Slice
public import Init.Data.String.Termination
import Init.Data.String.Length
public section

View file

@ -8,6 +8,7 @@ module
prelude
public import Std.Time.Date
public import Init.Data.String.Basic
public import Init.Data.String.Length
public section

View file

@ -442,7 +442,7 @@ inductive Modifier
It takes a constructor function to build the `Modifier` and a classify function that maps the pattern length to a specific type.
-/
private def parseMod (constructor : α → Modifier) (classify : Nat → Option α) (p : String) : Parser Modifier :=
let len := p.length
let len := p.positions.length
match classify len with
| some res => pure (constructor res)
| none => fail s!"invalid quantity of characters for '{p.front}'"
@ -617,16 +617,16 @@ private def specParse (s : String) : Except String FormatString :=
-- Pretty printer
private def leftPad (n : Nat) (a : Char) (s : String) : String :=
"".pushn a (n - s.length) ++ s
"".pushn a (n - s.positions.length) ++ s
private def rightPad (n : Nat) (a : Char) (s : String) : String :=
s ++ "".pushn a (n - s.length)
s ++ "".pushn a (n - s.positions.length)
private def pad (size : Nat) (n : Int) (cut : Bool := false) : String :=
let (sign, n) := if n < 0 then ("-", -n) else ("", n)
let numStr := toString n
if numStr.length > size then
if numStr.positions.length > size then
sign ++ if cut then numStr.drop (numStr.length - size) |>.copy else numStr
else
sign ++ leftPad size '0' numStr

View file

@ -8,6 +8,7 @@ module
prelude
public import Init.Data.String.TakeDrop
public import Init.Data.String.Search
public import Init.Data.String.Length
namespace Lake

View file

@ -10,6 +10,7 @@ public import Init.Data.ToString.Basic
import Init.Data.UInt.Lemmas
import Init.Data.String.Basic
import Init.Data.Nat.Fold
import Init.Data.String.Length
namespace Lake

View file

@ -13,6 +13,7 @@ import Init.Data.String.TakeDrop
import Lean.Data.Trie
import Init.Data.String.Search
import Init.Omega
import Init.Data.String.Length
/-! # Version