Skip to content

Commit 4e022ca

Browse files
committed
Support finding attributes in styled text and conversion to and from HTML in library (lispkit styled-text).
1 parent 43080b9 commit 4e022ca

File tree

1 file changed

+99
-17
lines changed

1 file changed

+99
-17
lines changed

Sources/LispKit/Primitives/StyledTextLibrary.swift

+99-17
Original file line numberDiff line numberDiff line change
@@ -197,6 +197,7 @@ public final class StyledTextLibrary: NativeLibrary {
197197
self.define(Procedure("load-styled-text", loadStyledText))
198198
self.define(Procedure("copy-styled-text", copyStyledText))
199199
self.define(Procedure("save-styled-text", saveStyledText))
200+
self.define(Procedure("html->styled-text", htmlToStyledText))
200201
self.define(Procedure("bytevector->styled-text", bytevectorToStyledText))
201202
self.define(Procedure("styled-text=?", styledTextEquals))
202203
self.define(Procedure("styled-text-string", styledTextString))
@@ -208,6 +209,9 @@ public final class StyledTextLibrary: NativeLibrary {
208209
self.define(Procedure("styled-text-remove!", styledTextRemove))
209210
self.define(Procedure("styled-text-attribute", styledTextAttribute))
210211
self.define(Procedure("styled-text-attributes", styledTextAttributes))
212+
self.define(Procedure("styled-text-first-attribute", styledTextFirstAttribute))
213+
self.define(Procedure("styled-text-first-attributes", styledTextFirstAttributes))
214+
self.define(Procedure("styled-text->html", styledTextToHtml))
211215
self.define(Procedure("styled-text->bytevector", styledTextToBytevector))
212216

213217
// Text styles
@@ -584,6 +588,16 @@ public final class StyledTextLibrary: NativeLibrary {
584588
return .void
585589
}
586590

591+
private func htmlToStyledText(html: Expr) throws -> Expr {
592+
let data = Data(try html.asString().utf8)
593+
let str = try NSMutableAttributedString(
594+
data: data,
595+
options: [.documentType: NSAttributedString.DocumentType.html,
596+
.characterEncoding: String.Encoding.utf8.rawValue],
597+
documentAttributes: nil)
598+
return .object(StyledText(str))
599+
}
600+
587601
private func bytevectorToStyledText(expr: Expr, format: Expr, args: Arguments) throws -> Expr {
588602
let subvec = try BytevectorLibrary.subVector("bytevector->styled-text", expr, args)
589603
if let docType = self.documentType(from: try format.asSymbol()) {
@@ -731,40 +745,67 @@ public final class StyledTextLibrary: NativeLibrary {
731745
return .void
732746
}
733747

734-
private func styledTextAttribute(text: Expr, index: Expr, sym: Expr, args: Arguments) throws -> Expr {
735-
guard let (start, end) = args.optional(.false, .false) else {
736-
throw RuntimeError.argumentCount(of: "styled-text-attribute", min: 3, max: 5,
737-
args: .pair(text, .pair(index, .pair(sym, .makeList(args)))))
738-
}
748+
private func styledTextAttribute(text: Expr,
749+
sym: Expr,
750+
index: Expr,
751+
start: Expr?,
752+
end: Expr?) throws -> Expr {
739753
let str = try self.styledText(from: text).value
740-
let idx = try index.asInt(below: str.length + 1)
741754
guard let key = self.attributeKey(from: try sym.asSymbol()) else {
742755
throw RuntimeError.eval(.unknownTextStyleAttribute, sym)
743756
}
744-
let s = start.isTrue ? try start.asInt(below: idx + 1) : 0
745-
let e = end.isTrue ? try end.asInt(above: s, below: str.length + 1) : str.length
757+
let idx = try index.asInt(below: str.length + 1)
758+
let s = start != nil && start!.isTrue ? try start!.asInt(below: idx + 1) : 0
759+
let e = end != nil && end!.isTrue ? try end!.asInt(above: s, below: str.length + 1) : str.length
746760
var range = NSRange(location: 0, length: 0)
747761
if let value = str.attribute(key,
748762
at: idx,
749763
longestEffectiveRange: &range,
750764
in: NSRange(location: s, length: e - s)),
751765
let res = try self.textStyleValue(key: key, value: value) {
752766
return .values(.pair(res, .pair(.pair(.fixnum(Int64(range.location)),
753-
.fixnum(Int64(range.location + range.length))), .null)))
754-
} else {
755-
return .false
767+
.fixnum(Int64(range.location + range.length))),
768+
.null)))
756769
}
770+
return .values(.pair(.false, .pair(.false, .null)))
757771
}
758772

759-
private func styledTextAttributes(text: Expr, index: Expr, args: Arguments) throws -> Expr {
760-
guard let (start, end) = args.optional(.false, .false) else {
761-
throw RuntimeError.argumentCount(of: "styled-text-attributes", min: 2, max: 4,
762-
args: .pair(text, .pair(index, .makeList(args))))
773+
private func styledTextFirstAttribute(text: Expr,
774+
sym: Expr,
775+
start: Expr?,
776+
end: Expr?) throws -> Expr {
777+
let str = try self.styledText(from: text).value
778+
guard let key = self.attributeKey(from: try sym.asSymbol()) else {
779+
throw RuntimeError.eval(.unknownTextStyleAttribute, sym)
763780
}
781+
let s = start != nil && start!.isTrue ? try start!.asInt(below: str.length + 1) : 0
782+
let e = end != nil && end!.isTrue ? try end!.asInt(above: s, below: str.length + 1) : str.length
783+
var res = Expr.undef
784+
str.enumerateAttribute(key,
785+
in: NSRange(location: s, length: e - s),
786+
options: [],
787+
using: { value, range, stop in
788+
do {
789+
if let value = value,
790+
let val = try self.textStyleValue(key: key, value: value) {
791+
res = .values(.pair(val, .pair(.pair(.fixnum(Int64(range.location)),
792+
.fixnum(Int64(range.location + range.length))),
793+
.null)))
794+
stop.pointee = false
795+
}
796+
} catch {}
797+
})
798+
return res.isUndef ? .values(.pair(.false, .pair(.false, .null))) : res
799+
}
800+
801+
private func styledTextAttributes(text: Expr,
802+
index: Expr,
803+
start: Expr?,
804+
end: Expr?) throws -> Expr {
764805
let str = try self.styledText(from: text).value
765806
let idx = try index.asInt(below: str.length + 1)
766-
let s = start.isTrue ? try start.asInt(below: idx + 1) : 0
767-
let e = end.isTrue ? try end.asInt(above: s, below: str.length + 1) : str.length
807+
let s = start != nil && start!.isTrue ? try start!.asInt(below: idx + 1) : 0
808+
let e = end != nil && end!.isTrue ? try end!.asInt(above: s, below: str.length + 1) : str.length
768809
var range = NSRange(location: 0, length: 0)
769810
let res = TextStyle()
770811
res.attributes = str.attributes(at: idx,
@@ -776,6 +817,47 @@ public final class StyledTextLibrary: NativeLibrary {
776817
.null)))
777818
}
778819

820+
private func styledTextFirstAttributes(text: Expr, start: Expr?, end: Expr?) throws -> Expr {
821+
let str = try self.styledText(from: text).value
822+
let s = start != nil && start!.isTrue ? try start!.asInt(below: str.length + 1) : 0
823+
let e = end != nil && end!.isTrue ? try end!.asInt(above: s, below: str.length + 1) : str.length
824+
var res = Expr.undef
825+
str.enumerateAttributes(in: NSRange(location: s, length: e - s),
826+
options: [],
827+
using: { attribs, range, stop in
828+
if !attribs.isEmpty {
829+
let tstyle = TextStyle()
830+
tstyle.attributes = attribs
831+
res = .values(.pair(.object(tstyle),
832+
.pair(.pair(.fixnum(Int64(range.location)),
833+
.fixnum(Int64(range.location + range.length))),
834+
.null)))
835+
stop.pointee = true
836+
}
837+
})
838+
guard res.isUndef else {
839+
return res
840+
}
841+
return .values(.pair(.false, .pair(.false, .null)))
842+
}
843+
844+
private func styledTextToHtml(text: Expr,
845+
start: Expr?,
846+
end: Expr?) throws -> Expr {
847+
let str = try self.styledText(from: text).value
848+
let e = (end?.isTrue ?? false) ? try end!.asInt(below: str.length + 1) : str.length
849+
let s = (start?.isTrue ?? false) ? try start!.asInt(below: e + 1) : 0
850+
let data = try str.data(
851+
from: NSRange(location: s, length: e - s),
852+
documentAttributes:[.documentType: NSAttributedString.DocumentType.html,
853+
.characterEncoding: String.Encoding.utf8.rawValue])
854+
if let res = String(data: data, encoding: String.Encoding.utf8) {
855+
return .makeString(res)
856+
} else {
857+
return .false
858+
}
859+
}
860+
779861
private func styledTextToBytevector(text: Expr,
780862
format: Expr,
781863
start: Expr?,

0 commit comments

Comments
 (0)