Skip to content

Instantly share code, notes, and snippets.

@aphyr
Created August 26, 2015 00:42
Show Gist options
  • Select an option

  • Save aphyr/1390b4ee07ef8846e7f0 to your computer and use it in GitHub Desktop.

Select an option

Save aphyr/1390b4ee07ef8846e7f0 to your computer and use it in GitHub Desktop.
(ns scratch.core
(:require [clojure.string :as str])
(:import [java.util.stream IntStream]
[java.lang CharSequence
StringBuilder]))
(defn ^IntStream str->codepoints
"Given a string, yields a sequence of integer codepoints."
[^String string]
(-> string .codePoints .iterator iterator-seq))
(defn codepoints->str
"Converts a sequence (or any reducible) of integer codepoints to a string."
[codepoints]
(str (reduce #(.appendCodePoint ^StringBuilder %1 %2)
; If we can cheaply take a guess at how many codepoints there
; are, we can preallocate roughly that big a stringbuilder.
(if (counted? codepoints)
(StringBuilder. (count codepoints))
(StringBuilder.))
codepoints)))
(defn splice
"Given a sequence of codepoints and a collection of entities, splices entity
text codepoints into given codepoints, yielding a lazy sequence."
([entities codepoints]
(splice 0 entities codepoints))
([i entities codepoints]
(if-let [{:keys [start end text] :as entity} (first entities)]
(lazy-seq
(concat (take (- start i) codepoints)
(str->codepoints text)
(splice end (next entities) (drop (- end i) codepoints))))
codepoints)))
(defn render
"Takes a tweet as text and a sequence of entities, represented as maps like
{:text a replacement string
:start index (in codepoints) of the beginning of the replacement
:end index (in codepoints) of the end of the replacement}
returns the tweet string with indexes (inclusive to exclusive) replaced by
their replacement strings."
[text entities]
(->> text
str->codepoints
(splice 0 (sort-by :start entities))
codepoints->str))
(ns scratch.core-test
(:require [clojure.test :refer :all]
[scratch.core :refer :all]))
(deftest a-test
(testing "empty"
(is (= "" (render "" []))))
(testing "no entities"
(is (= "hi" (render "hi" []))))
(testing "one entity"
(is (= "hello world!"
(render "hello garbage!" [{:start 6 :end 13 :text "world"}]))))
(testing "outside BMP"
(is (= "omg ๐Ÿ˜‚ cats"
(render "omg ๐Ÿ˜ผ๐Ÿ˜ผ๐Ÿ˜ผ cats" [{:start 4 :end 7 :text "๐Ÿ˜‚"}])))))
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment