             (let [[_ part1 right-rope position1] (rope-split-at root start)
                   length (- end start)
                   rest (- (count part1) position1)]
-              (cond (= length (+ rest (count right-rope))) (conc (core/subs part1 position1) right-rope)
+              (cond (= length (+ rest (count right-rope))) (conc (subs part1 position1) right-rope)
                     (> length rest)
                     (let [[middle-rope part2 _ position2] (rope-split-at right-rope (- length rest))]
-                      (conc (core/subs part1 position1) (conjoin middle-rope (core/subs part2 0 position2))))
-                    :default (core/subs part1 position1 (+ position1 length))))))))
+                      (conc (subs part1 position1) (conjoin middle-rope (subs part2 0 position2))))
+                    :default (subs part1 position1 (+ position1 length)))))))
+  ([root start-line start-column end-line end-column]
+     {:pre [(>= (- end-line start-line) 0)]}
+     (if (and (= start-line end-line)
+              (= start-column end-column))
+       ""
+       (report root (translate root start-line start-column) (translate root end-line end-column)))))
+
+(defn translate
+  ([root row column]
+     {:pre [(>= row 0) (>= column 0)]}
+     (if (> row (-> root measure :lines))
+       (throw (IndexOutOfBoundsException. (str "Line index out of range: " row)))
+       (let [[left-rope left-lines line right-lines right-rope] (rope-split-at-line root row),
+             offset (+ (count left-rope) (count (apply concat left-lines)) (count left-lines) column),
+             delta (- column (count line))]
+         (if (and (pos? delta) (empty? right-lines)
+                  (not (and (>= (count right-rope) delta)
+                            (zero? (first (translate right-rope delta))))))
+           (throw (IndexOutOfBoundsException. (str "String index out of range: " column)))
+           offset))))
+  ([root index]
+     {:pre [(>= index 0)]}
+     (let [[left-rope leaf _ rest] (rope-split-at root index),
+           lines (->> (subs leaf 0 rest) split-lines),
+           rope-weight (-> left-rope measure :lines),
+           weight (dec (count lines))]
+       (if (zero? weight)
+         (let [[_ _ line _ right-rope] (rope-split-at-line left-rope rope-weight)]
+           [rope-weight, (+ (count line) (count right-rope) (-> lines first count))])
+         [(+ rope-weight weight), (-> lines last count)]))))
 
 (defn delete
   "Delete the characters at interval [start..end] in the Rope"
-  [root start end]
-  {:pre [(>= (- end start) 0)]}
-  (cond
-   (= start end) root
-   (= end (count root))
-   (let [[left-rope part _ position] (rope-split-at root start)]
-     (conjoin left-rope (core/subs part 0 position)))
-   :default
-   (let [[left-rope part1 _ position1] (rope-split-at root start)
-         [_ part2 right-rope position2] (rope-split-at root end)]
-     (conc (conjoin left-rope (core/subs part1 0 position1) (core/subs part2 position2))
-           right-rope))))
+  ([root start end]
+     {:pre [(>= (- end start) 0)]}
+     (cond
+      (= start end) root
+      (= end (count root))
+      (let [[left-rope part _ position] (rope-split-at root start)]
+        (conjoin left-rope (subs part 0 position)))
+      :default
+      (let [[left-rope part1 _ position1] (rope-split-at root start)
+            [_ part2 right-rope position2] (rope-split-at root end)]
+        (conc (conjoin left-rope (subs part1 0 position1) (subs part2 position2))
+              right-rope))))
+  ([root start-line start-column end-line end-column]
+     {:pre [(>= (- end-line start-line) 0)]}
+     (if (and (= start-line end-line)
+              (= start-column end-column))
+       root
+       (delete root (translate root start-line start-column) (translate root end-line end-column)))))
+
+(defn delete-line
+  "Delete the line at index in the Rope"
+  [root index]
+  (delete root index 0 (inc index) 0))
 
 (deftype Rope [#^CharSequence left #^CharSequence right #^CharSequenceMeasurement weight #^int level]
   Measurable

  CharSequence
  (charAt [this index] (nth this index))
  (length [this] (count this))
  (subSequence [this start end] (subs this start end))
  (toString [this] (rope->string this)))  

(defmethod print-method Rope [rope writer]
  (print-simple (format "#<Rope %s>" (->> (measure rope) vals (join "/"))) writer))

; Must be defined here, else the dispatch on type/class will fail
(defmethod concat [Rope String] [coll string]
  (let [right-child (right coll)]
    (if (and (= String (type right-child))
             (<= (+ (count right-child) (count string)) *leaf-cutoff-length*))
      (rope (left coll) (str right-child string))
      (concat coll (rope string)))))

     (conjoin root (reduce conc x xs))))

(defn- rope-split-at
  "Split Rope at index

The additional remaining string index will be the fourth vector element"
  [root index]
  (if (= (count root) index)
    [root "" (rope) 0]
    (let [[left-rope string right-rope _] (rope-split root (fn [weight _] (>= index (:length weight))))
          position (- index (count left-rope))]
      [left-rope string right-rope position])))

(defn- split-lines
  [s]
  (seq (.split #"\n" s -1)))

(defn- rope-split-at-line
  "Split Rope at line

The resulting vector consists of the left rope, lines left of the target line,
then the same for everything right of the line."
  [root index]
  (let [[left-rope string right-rope _] (rope-split root (fn [weight _] (> index (:lines weight)))),
        position (- index (:lines (measure left-rope))),
        [left-lines [line & right-lines]] (split-at position (split-lines string))]
    [left-rope left-lines line right-lines right-rope]))

(defn insert
  "Insert string in Rope at index or line/column"
  ([root index string]
     (if (= index (count root))
       (conjoin root string)
       (let [[left-rope target right-rope position] (rope-split-at root index)]
         (conc (conjoin left-rope (subs target 0 position) string (subs target position))
               right-rope))))
  ([root line column string]
     (let [[left-rope left-lines line right-lines right-rope] (rope-split-at-line root line)]
       (conc (conjoin left-rope
                      (apply str (interpose \n left-lines))
                      "\n" (subs line 0 column) string (subs line column)
                      (apply str (interpose \n right-lines)))
             right-rope))))

(defn report
  "Report in O(log n) time for Ropes, the equivalent of string subs"
  ([root start]
     (if (= start (count root))
       "" ; For consistency with "normal" subs
       (let [[_ string right-rope position] (rope-split-at root start)]
         (conc (subs string position) right-rope))))
  ([root start end]
     {:pre [(>= (- end start) 0)]}
     (let [length (count root)]
