mirror of
https://github.com/apple/sourcekit-lsp.git
synced 2026-03-02 18:23:24 +01:00
Add `.swift-format` to the repo and format the repo with `swift-format`. This commit does not add any automation to enforce formatting of sourcekit-lsp in CI. The goal of this commit is to get the majority of source changes out of the way so that the diff of actually enforcing formatting will have fewer changes or conflicts.
216 lines
6.6 KiB
Swift
216 lines
6.6 KiB
Swift
//===----------------------------------------------------------------------===//
|
|
//
|
|
// This source file is part of the Swift.org open source project
|
|
//
|
|
// Copyright (c) 2014 - 2018 Apple Inc. and the Swift project authors
|
|
// Licensed under Apache License v2.0 with Runtime Library Exception
|
|
//
|
|
// See https://swift.org/LICENSE.txt for license information
|
|
// See https://swift.org/CONTRIBUTORS.txt for the list of Swift project authors
|
|
//
|
|
//===----------------------------------------------------------------------===//
|
|
|
|
public struct LineTable: Hashable {
|
|
@usableFromInline
|
|
var impl: [String.Index]
|
|
|
|
public var content: String
|
|
|
|
public init(_ string: String) {
|
|
content = string
|
|
|
|
var i = string.startIndex
|
|
impl = [i]
|
|
while i != string.endIndex {
|
|
let c = string[i]
|
|
string.formIndex(after: &i)
|
|
if c == "\n" || c == "\r\n" || c == "\r" {
|
|
impl.append(i)
|
|
}
|
|
}
|
|
}
|
|
|
|
/// The number of lines.
|
|
@inlinable
|
|
public var count: Int { return impl.count }
|
|
|
|
/// Returns the given (zero-based) line as a Substring, including the newline.
|
|
///
|
|
/// - parameter line: Line number (zero-based).
|
|
@inlinable
|
|
public subscript(line: Int) -> Substring {
|
|
return content[impl[line]..<(line == count - 1 ? content.endIndex : impl[line + 1])]
|
|
}
|
|
|
|
/// Translate String.Index to logical line/utf16 pair.
|
|
@usableFromInline
|
|
func lineAndUTF16ColumnOf(_ index: String.Index, fromLine: Int = 0) -> (line: Int, utf16Column: Int) {
|
|
precondition(0 <= fromLine && fromLine < count)
|
|
|
|
// Binary search.
|
|
var lower = fromLine
|
|
var upper = count
|
|
while true {
|
|
let mid = lower + (upper - lower) / 2
|
|
let lineStartIndex = impl[mid]
|
|
if mid == lower || lineStartIndex == index {
|
|
return (
|
|
line: mid,
|
|
utf16Column: content.utf16.distance(from: lineStartIndex, to: index)
|
|
)
|
|
} else if lineStartIndex < index {
|
|
lower = mid
|
|
} else {
|
|
upper = mid
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
extension LineTable: RandomAccessCollection {
|
|
public var startIndex: Int {
|
|
return impl.startIndex
|
|
}
|
|
|
|
public var endIndex: Int {
|
|
return impl.endIndex
|
|
}
|
|
}
|
|
|
|
extension LineTable {
|
|
|
|
// MARK: - Editing
|
|
|
|
/// Replace the line table's `content` in the given range and update the line data.
|
|
///
|
|
/// - parameter fromLine: Starting line number (zero-based).
|
|
/// - parameter fromOff: Starting UTF-16 column offset (zero-based).
|
|
/// - parameter toLine: Ending line number (zero-based).
|
|
/// - parameter toOff: Ending UTF-16 column offset (zero-based).
|
|
/// - parameter replacement: The new text for the given range.
|
|
@inlinable
|
|
mutating public func replace(
|
|
fromLine: Int,
|
|
utf16Offset fromOff: Int,
|
|
toLine: Int,
|
|
utf16Offset toOff: Int,
|
|
with replacement: String
|
|
) {
|
|
let start = content.utf16.index(impl[fromLine], offsetBy: fromOff)
|
|
let end = content.utf16.index(impl[toLine], offsetBy: toOff)
|
|
|
|
var newText = self.content
|
|
newText.replaceSubrange(start..<end, with: replacement)
|
|
|
|
self = LineTable(newText)
|
|
}
|
|
|
|
/// Edit the line table's `content` and update the line data.
|
|
///
|
|
/// - parameter fromLine: Starting line number (zero-based).
|
|
/// - parameter fromOff: Starting UTF-16 column offset (zero-based).
|
|
/// - parameter utf16Length: The number of UTF-16 code units to replace.
|
|
/// - parameter replacement: The new text for the given range.
|
|
mutating public func replace(
|
|
fromLine: Int,
|
|
utf16Offset fromOff: Int,
|
|
utf16Length: Int,
|
|
with replacement: String
|
|
) {
|
|
let start = content.utf16.index(impl[fromLine], offsetBy: fromOff)
|
|
let end = content.utf16.index(start, offsetBy: utf16Length)
|
|
let (toLine, toOff) = lineAndUTF16ColumnOf(end, fromLine: fromLine)
|
|
self.replace(fromLine: fromLine, utf16Offset: fromOff, toLine: toLine, utf16Offset: toOff, with: replacement)
|
|
}
|
|
}
|
|
|
|
extension LineTable {
|
|
|
|
// MARK: - Position translation
|
|
|
|
/// Returns `String.Index` of given logical position.
|
|
///
|
|
/// - parameter line: Line number (zero-based).
|
|
/// - parameter utf16Column: UTF-16 column offset (zero-based).
|
|
@inlinable
|
|
public func stringIndexOf(line: Int, utf16Column: Int) -> String.Index? {
|
|
guard line < count else {
|
|
// Line out of range.
|
|
return nil
|
|
}
|
|
let lineSlice = self[line]
|
|
return content.utf16.index(lineSlice.startIndex, offsetBy: utf16Column, limitedBy: lineSlice.endIndex)
|
|
}
|
|
|
|
/// Returns UTF8 buffer offset of given logical position.
|
|
///
|
|
/// - parameter line: Line number (zero-based).
|
|
/// - parameter utf16Column: UTF-16 column offset (zero-based).
|
|
@inlinable
|
|
public func utf8OffsetOf(line: Int, utf16Column: Int) -> Int? {
|
|
guard let stringIndex = stringIndexOf(line: line, utf16Column: utf16Column) else {
|
|
return nil
|
|
}
|
|
return content.utf8.distance(from: content.startIndex, to: stringIndex)
|
|
}
|
|
|
|
/// Returns logical position of given source offset.
|
|
///
|
|
/// - parameter utf8Offset: UTF-8 buffer offset (zero-based).
|
|
@inlinable
|
|
public func lineAndUTF16ColumnOf(utf8Offset: Int) -> (line: Int, utf16Column: Int)? {
|
|
guard utf8Offset <= content.utf8.count else {
|
|
// Offset ouf of range.
|
|
return nil
|
|
}
|
|
return lineAndUTF16ColumnOf(content.utf8.index(content.startIndex, offsetBy: utf8Offset))
|
|
}
|
|
|
|
/// Returns UTF16 column offset at UTF8 version of logical position.
|
|
///
|
|
/// - parameter line: Line number (zero-based).
|
|
/// - parameter utf8Column: UTF-8 column offset (zero-based).
|
|
@inlinable
|
|
public func utf16ColumnAt(line: Int, utf8Column: Int) -> Int? {
|
|
return convertColumn(
|
|
line: line,
|
|
column: utf8Column,
|
|
indexFunction: content.utf8.index(_:offsetBy:limitedBy:),
|
|
distanceFunction: content.utf16.distance(from:to:)
|
|
)
|
|
}
|
|
|
|
/// Returns UTF8 column offset at UTF16 version of logical position.
|
|
///
|
|
/// - parameter line: Line number (zero-based).
|
|
/// - parameter utf16Column: UTF-16 column offset (zero-based).
|
|
@inlinable
|
|
public func utf8ColumnAt(line: Int, utf16Column: Int) -> Int? {
|
|
return convertColumn(
|
|
line: line,
|
|
column: utf16Column,
|
|
indexFunction: content.utf16.index(_:offsetBy:limitedBy:),
|
|
distanceFunction: content.utf8.distance(from:to:)
|
|
)
|
|
}
|
|
|
|
@inlinable
|
|
func convertColumn(
|
|
line: Int,
|
|
column: Int,
|
|
indexFunction: (Substring.Index, Int, Substring.Index) -> Substring.Index?,
|
|
distanceFunction: (Substring.Index, Substring.Index) -> Int
|
|
) -> Int? {
|
|
guard line < count else {
|
|
// Line out of range.
|
|
return nil
|
|
}
|
|
let lineSlice = self[line]
|
|
guard let targetIndex = indexFunction(lineSlice.startIndex, column, lineSlice.endIndex) else {
|
|
// Column out of range
|
|
return nil
|
|
}
|
|
return distanceFunction(lineSlice.startIndex, targetIndex)
|
|
}
|
|
}
|