Exclude the last blank line when counting lines

This commit is contained in:
1024jp 2024-04-06 16:34:33 +09:00
parent 51eb35a523
commit cc321f4f0a
3 changed files with 38 additions and 25 deletions

View File

@ -5,6 +5,7 @@
### Improvements
- Change the line count behavior to ignore the new line character at the end of the last line.
- Change the filter field for the outline inspector to apply the selection of the filter history immediately.
- Remove wrapped line marks in the line number view.

View File

@ -39,7 +39,7 @@ extension StringProtocol {
}
/// The number of lines in the whole string including the last blank line.
/// The number of lines in the whole string excluding the last blank line.
var numberOfLines: Int {
self.numberOfLines()
@ -54,15 +54,17 @@ extension StringProtocol {
guard !self.isEmpty, index > self.startIndex else { return 1 }
return self.numberOfLines(in: self.startIndex..<index)
return self.numberOfLines(in: self.startIndex..<index, includesLastBreak: true)
}
/// Counts the number of lines in the given range including the last blank line.
/// Counts the number of lines in the given range.
///
/// - Parameter range: The character range to count lines, or when `nil`, the entire range.
/// - Parameters:
/// - range: The character range to count lines, or when `nil`, the entire range.
/// - includesLastBreak: The flag to count the new line character at the end.
/// - Returns: The number of lines.
func numberOfLines(in range: Range<String.Index>? = nil) -> Int {
func numberOfLines(in range: Range<String.Index>? = nil, includesLastBreak: Bool = false) -> Int {
let range = range ?? self.startIndex..<self.endIndex
@ -73,7 +75,7 @@ extension StringProtocol {
count += 1
}
if self[range].last?.isNewline == true {
if includesLastBreak, self[range].last?.isNewline == true {
count += 1
}
@ -81,11 +83,13 @@ extension StringProtocol {
}
/// Counts the number of lines in the given ranges including the last blank line.
/// Counts the number of lines in the given ranges.
///
/// - Parameter ranges: The character ranges to count lines.
/// - Parameters:
/// - ranges: The character ranges to count lines.
/// - includesLastBreak: The flag to count the new line character at the end.
/// - Returns: The number of lines.
func numberOfLines(in ranges: [Range<String.Index>]) -> Int {
func numberOfLines(in ranges: [Range<String.Index>], includesLastBreak: Bool = false) -> Int {
assert(!ranges.isEmpty)
@ -93,7 +97,7 @@ extension StringProtocol {
// use simple count for efficiency
if ranges.count == 1 {
return self.numberOfLines(in: ranges[0])
return self.numberOfLines(in: ranges[0], includesLastBreak: includesLastBreak)
}
// evaluate line ranges to avoid double-count lines holding multiple ranges
@ -104,7 +108,7 @@ extension StringProtocol {
lineRanges.append(substringRange)
}
if self[range].last?.isNewline == true {
if includesLastBreak, self[range].last?.isNewline == true {
lineRanges.append(self.lineRange(at: range.upperBound))
}
}
@ -137,15 +141,17 @@ extension String {
guard !self.isEmpty, location > 0 else { return 1 }
return self.numberOfLines(in: NSRange(location: 0, length: location))
return self.numberOfLines(in: NSRange(location: 0, length: location), includesLastBreak: true)
}
/// Counts the number of lines in the given range including the last blank line.
/// Counts the number of lines in the given range.
///
/// - Parameter range: The character range to count lines, or when `nil`, the entire range.
/// - Parameters:
/// - ranges: The character range to count lines, or when `nil`, the entire range.
/// - includesLastBreak: The flag to count the new line character at the end.
/// - Returns: The number of lines.
func numberOfLines(in range: NSRange? = nil) -> Int {
func numberOfLines(in range: NSRange? = nil, includesLastBreak: Bool = false) -> Int {
let range = range ?? self.nsRange
@ -156,7 +162,7 @@ extension String {
count += 1
}
if (self as NSString).character(at: range.upperBound - 1).isNewline == true {
if includesLastBreak, (self as NSString).character(at: range.upperBound - 1).isNewline == true {
count += 1
}

View File

@ -9,7 +9,7 @@
//
// ---------------------------------------------------------------------------
//
// © 2015-2023 1024jp
// © 2015-2024 1024jp
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
@ -107,18 +107,24 @@ final class StringExtensionsTests: XCTestCase {
XCTAssertEqual("".numberOfLines, 0)
XCTAssertEqual("a".numberOfLines, 1)
XCTAssertEqual("\n".numberOfLines, 2)
XCTAssertEqual("\n\n".numberOfLines, 3)
XCTAssertEqual("\n".numberOfLines, 1)
XCTAssertEqual("\n\n".numberOfLines, 2)
XCTAssertEqual("\u{feff}".numberOfLines, 1)
XCTAssertEqual("ab\r\ncd".numberOfLines, 2)
let testString = "a\nb c\n\n"
XCTAssertEqual(testString.numberOfLines, 4)
XCTAssertEqual(testString.numberOfLines, 3)
XCTAssertEqual(testString.numberOfLines(in: NSRange(0..<0)), 0) // ""
XCTAssertEqual(testString.numberOfLines(in: NSRange(0..<1)), 1) // "a"
XCTAssertEqual(testString.numberOfLines(in: NSRange(0..<2)), 2) // "a\n"
XCTAssertEqual(testString.numberOfLines(in: NSRange(0..<6)), 3) // "a\nb c\n"
XCTAssertEqual(testString.numberOfLines(in: NSRange(0..<7)), 4) // "a\nb c\n\n"
XCTAssertEqual(testString.numberOfLines(in: NSRange(0..<2)), 1) // "a\n"
XCTAssertEqual(testString.numberOfLines(in: NSRange(0..<6)), 2) // "a\nb c\n"
XCTAssertEqual(testString.numberOfLines(in: NSRange(0..<7)), 3) // "a\nb c\n\n"
XCTAssertEqual(testString.numberOfLines(in: NSRange(0..<0), includesLastBreak: true), 0) // ""
XCTAssertEqual(testString.numberOfLines(in: NSRange(0..<1), includesLastBreak: true), 1) // "a"
XCTAssertEqual(testString.numberOfLines(in: NSRange(0..<2), includesLastBreak: true), 2) // "a\n"
XCTAssertEqual(testString.numberOfLines(in: NSRange(0..<6), includesLastBreak: true), 3) // "a\nb c\n"
XCTAssertEqual(testString.numberOfLines(in: NSRange(0..<7), includesLastBreak: true), 4) // "a\nb c\n\n"
XCTAssertEqual(testString.lineNumber(at: 0), 1)
XCTAssertEqual(testString.lineNumber(at: 1), 1)
@ -146,11 +152,11 @@ final class StringExtensionsTests: XCTestCase {
let bomString = "\u{FEFF}\nb"
let range = bomString.startIndex..<bomString.index(bomString.startIndex, offsetBy: 2)
XCTAssertEqual(bomString.numberOfLines(in: [range, range]), 2) // "\u{FEFF}\nb"
XCTAssertEqual(bomString.numberOfLines(in: [range, range]), 1) // "\u{FEFF}\n"
}
func testColumnCOunt() {
func testColumnCount() {
let string = "aaa \r\n🐱 "