44e4061c |
(defpackage :fwoar.lisp-sandbox.ot-edit
(:use :cl )
(:export
#:edit
#:insert
#:editable-string
#:apply-edits))
(in-package :fwoar.lisp-sandbox.ot-edit)
(defgeneric transform-for-op (op))
(defgeneric edit (base operation))
(defgeneric apply-edits (base operations))
|
76b82aba |
(fw.lu:defclass+ editable-string ()
((%string :initarg :string :accessor string-to-edit)
(%transform :accessor transform :initform 'identity)))
|
44e4061c |
|
4898e7f0 |
(defclass op ()
((%epoch :reader epoch :initform (let ((c (load-time-value (vector 0)))) (incf (elt c 0))))))
|
44e4061c |
|
76b82aba |
(defmethod apply-edits ((base string) (operations sequence))
(string-to-edit
(reduce 'edit
(sort operations '< :key 'epoch)
:initial-value (editable-string base))))
|
cfdc28be |
(fw.lu:defclass+ insert (op)
|
44e4061c |
((%point :initarg :point :accessor point)
(%value :initarg :value :reader value)))
|
cfdc28be |
(defmethod print-object ((o insert) s)
(print-unreadable-object (o s :type t :identity t)
(format s "~d/~d" (point o) (epoch o))))
|
44e4061c |
(defmethod transform-for-op ((op insert))
(let ((point (point op))
(insert-length (length (value op))))
(lambda (new-point)
(if (< new-point point)
new-point
(+ new-point insert-length)))))
(defun do-insert (base new point)
(let ((begin (subseq base 0 point))
(end (subseq base point)))
(concatenate 'string begin new end)))
(defmethod edit ((base editable-string) (operation insert))
|
f1131399 |
(let* ((current-transform (transform base))
(op-point (point operation))
(string-to-edit (string-to-edit base))
(transformed-op (insert (funcall current-transform op-point)
(value operation))))
(setf (string-to-edit base) (do-insert string-to-edit (value transformed-op) (point transformed-op))
(transform base) (alexandria:compose (transform-for-op transformed-op) current-transform))
|
44e4061c |
base))
|
d71eb841 |
(fw.lu:defclass+ replace-char (op)
((%point :initarg :point :accessor point)
(%value :initarg :value :reader value)))
(defmethod print-object ((o replace-char) s)
(print-unreadable-object (o s :type t :identity t)
(format s "~d/~d" (point o) (epoch o))))
(defmethod transform-for-op ((op replace-char))
(let ((point (point op))
(insert-length (length (value op))))
(lambda (new-point)
(if (<= new-point point)
new-point
(+ new-point -1 insert-length)))))
|
44e4061c |
(defun do-replace-char (base new point)
(let ((begin (subseq base 0 point))
(end (subseq base (1+ point))))
(concatenate 'string begin new end)))
(defmethod edit ((base editable-string) (operation replace-char))
|
f1131399 |
(let* ((current-transform (transform base))
(op-point (point operation))
(string-to-edit (string-to-edit base))
(transformed-op (replace-char (funcall current-transform op-point)
(value operation))))
(setf (string-to-edit base) (do-replace-char string-to-edit (value transformed-op) (point transformed-op))
(transform base) (alexandria:compose (transform-for-op transformed-op) current-transform))
|
44e4061c |
base))
(defun sample ()
|
b7f9ee2a |
(assert (equal (apply-edits "ac" (list (insert 0 "z")
(insert 1 "b")
(insert 2 "d")))
|
f1131399 |
"zabcd"))
(assert
(let ((ops (list (insert 1 "fff")
(insert 1 "ggg")
(insert 1 "hhh"))))
(list (equal (apply-edits #1="abcd" ops)
(apply-edits #1# (reverse ops)))
(equal (apply-edits #1# (reverse ops))
(apply-edits #1# (serapeum:reshuffle ops)))
(equal (apply-edits #1# (reverse ops))
"afffggghhhbcd"))))
(let ((ops (lambda ()
(list (insert 2 "fff")
(insert 1 "bbb")
(insert 1 "ddd")))))
(:printv (funcall ops))
(assert (equal (:printv (apply-edits "ac" (:printv (serapeum:reshuffle (funcall ops)))))
(:printv (apply-edits "ac" (:printv (serapeum:reshuffle (funcall ops)))))))
(assert (equal (:printv (apply-edits "ace" (serapeum:reshuffle (funcall ops))))
(:printv (apply-edits "ace" (serapeum:reshuffle (funcall ops))))))))
|