krz/orgstar

A native macOS editor for org-mode files. editor org-mode swift

Sources/OrgCore/Commands/InlineImages.swift

43 lines · 2222 bytes

4 symbols in this file
 1import Foundation
 2
 3/// Image links shown as images (`org-display-inline-images`, Org 9.8.7): a line that is only a
 4/// link to an image file, without a description, and the width `#+ATTR_ORG: :width` asks for.
 5public enum InlineImages {
 6    public struct Image: Sendable, Equatable {
 7        public let path: String
 8        public let width: Int?
 9    }
10
11    static let extensions = "png|jpe?g|gif|svg|webp|tiff?|bmp|heic|avif"
12    static let pattern = try! NSRegularExpression(pattern: "^[ \\t]*\\[\\[(?:file:)?([^\\]\\n]+\\.(?:\(extensions)))\\]\\][ \\t]*$", options: .caseInsensitive)
13
14    /// The image on the line at `lineStart`, if the line is an image link.
15    public static func image(atLine lineStart: Int, in text: NSString) -> Image? {
16        let line = text.lineRange(for: NSRange(location: lineStart, length: 0))
17        let content = text.substring(with: line).trimmingCharacters(in: .newlines)
18        guard let m = pattern.firstMatch(in: content, range: NSRange(location: 0, length: (content as NSString).length)) else { return nil }
19        let path = (content as NSString).substring(with: m.range(at: 1))
20        guard !path.contains("://"), !path.hasPrefix("attachment:") else { return nil }
21        // `#+ATTR_ORG: :width N` in the affiliated keywords above.
22        var width: Int?
23        var at = line.location
24        while at > 0 {
25            let previous = text.lineRange(for: NSRange(location: at - 1, length: 0))
26            let keyword = text.substring(with: previous)
27            guard keyword.range(of: "^[ \\t]*#\\+[A-Za-z_]+:", options: .regularExpression) != nil else { break }
28            if width == nil, let w = keyword.firstMatch(of: /(?i)^[ \t]*#\+ATTR_ORG:.*:width[ \t]+([0-9]+)/) { width = Int(w.1) }
29            at = previous.location
30        }
31        return Image(path: path, width: width)
32    }
33
34    /// `#+STARTUP: inlineimages` or `noinlineimages`, if the file says.
35    public static func startup(_ tree: OrgTree) -> Bool? {
36        var shown: Bool?
37        for word in tree.settings.startup {
38            if word.lowercased() == "inlineimages" { shown = true }
39            if word.lowercased() == "noinlineimages" { shown = false }
40        }
41        return shown
42    }
43}