import Foundation /// How wide each character of a line is as Emacs displays Org text: bracket links show only /// their description (or path), and with the options, emphasis markers are hidden and entities /// and sub/superscripts show as their characters. Emacs's `current-column` counts only what is /// visible, so tag alignment, filling and table widths all depend on this. struct VisibleLine { /// Display width of the character starting at each UTF-16 offset; -1 for a tab. private var widths: [Int] /// Whether a character starts at each offset (false for the second half of a pair). private var starts: [Bool] private var hidden: [Bool] /// The line at `range` (UTF-16) inside `context`, which holds the lines around it so that /// links spanning lines are seen whole. init(_ context: String, line range: Range, options: EditingOptions, settings: OrgSettings = .default) { let whole = VisibleLine(context, options: options, settings: settings) widths = Array(whole.widths[range]) starts = Array(whole.starts[range]) hidden = Array(whole.hidden[range]) } init(_ line: String, options: EditingOptions, settings: OrgSettings = .default) { let count = line.utf16.count widths = [Int](repeating: 0, count: count) starts = [Bool](repeating: false, count: count) hidden = [Bool](repeating: false, count: count) var index = 0 for character in line { widths[index] = character == "\t" ? -1 : displayWidth(of: character) starts[index] = true index += String(character).utf16.count } let needsLinks = line.contains("[[") let needsEmphasis = options.hideEmphasisMarkers && line.contains(where: { "*/_+=~".contains($0) }) if needsLinks || needsEmphasis { let tree = OrgParser.parse(line + "\n", defaults: settings) for node in tree.root.descendants() { switch node.kind { case .link where node.text.hasPrefix("[["): if let description = node.firstChild(.linkDescription) { hide(node.range.lowerBound..) { for i in range where i >= 0 && i < hidden.count { hidden[i] = true } } private func advance(_ column: Int, _ i: Int) -> Int { widths[i] < 0 ? (column / 8 + 1) * 8 : column + widths[i] } /// Display column at UTF-16 `offset`. func column(at offset: Int) -> Int { var column = 0 for i in 0..