Login
7 branches 0 tags
Ben (X13/Arch) Improved image support afd65be 2 years ago 1111 Commits
nujel / stdlib_modules / app / termed / buffer.nuj
(defclass TextBuffer
  "A buffer for text editors"
  :export

  (defn new (self filename)
        (def lines [(buffer/allocate 16)])
        { :lines lines
          :lines-clean []
          :filename filename
          :changed #f
          :pos-x 0
          :pos-y 0
          :scroll-x 0
          :scroll-y 0
          :prototype* self })

  (defn get-line (self line-y)
        (def lines (ref self :lines))
        (if (or (< line-y 0)
                (>= line-y (:length lines)))
            #nil
            (ref lines line-y)))

  (defn line-length (self pos-y)
        (def line (:get-line self pos-y))
        (when-not line (return 0))
        (dotimes (i (:length line) (:length line))
                 (when (zero? (ref line i))
                   (return i))))

  (defn backward-delete-line (self)
        (def lines (ref self :lines))
        (def ll (- (:length lines) 1))
        (def pos-y (ref self :pos-y))
        (when (zero? pos-y) (return))
        (while (< pos-y ll)
          (set! lines pos-y (inc pos-y))
          (inc! pos-y))
        (:length! lines (max 0 ll))
        (set! self :pos-y (- (ref self :pos-y) 1))
        (:line-clean! self (ref self :pos-y) #f)
        (set! self :pos-x (inc (:line-length self (ref self :pos-y)))))

  (defn backward-char (self)
        (def pos-x (ref self :pos-x))
        (when (< pos-x 1)
          (def pos-y (ref self :pos-y))
          (when (< pos-y 1)
            (return))
          (set! self :pos-y (dec pos-y))
          (:line-clean! self (ref self :pos-y) #f)
          (set! self :pos-x (:line-length self (dec pos-y)))
          (return))
        (set! self :pos-x (dec pos-x)))

  (defn previous-line (self)
        (def pos-x (ref self :pos-x))
        (def pos-y (ref self :pos-y))
        (when (< pos-y 1)
          (return))
        (set! self :pos-y (dec pos-y))
        (:line-clean! self (ref self :pos-y) #f)
        (set! self :pos-x (min pos-x (:line-length self (dec pos-y)))))

  (defn next-line (self)
        (def pos-x (ref self :pos-x))
        (def pos-y (ref self :pos-y))
        (def lines (ref self :lines))
        (def ll (- (:length lines) 1))
        (when (>= pos-y ll)
          (return))
        (set! self :pos-y (inc pos-y))
        (:line-clean! self (ref self :pos-y) #f)
        (set! self :pos-x (min pos-x (:line-length self (inc pos-y)))))

  (defn forward-char (self)
        (def pos-x (ref self :pos-x))
        (def pos-y (ref self :pos-y))
        (def ll (:line-length self pos-y))
        (when (>= pos-x ll)
          (when (>= (inc pos-y) (:length (ref self :lines)))
            (return))
          (set! self :pos-x 0)
          (set! self :pos-y (inc pos-y))
          (:line-clean! self (ref self :pos-y) #f)
          (return))
        (set! self :pos-x (inc pos-x)))

  (defn delete-forward-char (self)
        (def line (:get-line self (ref self :pos-y)))
        (when-not line (return))
        (def pos-x (ref self :pos-x))
        (def bl (- (:length line) 2))
        (while (<= pos-x bl)
          (set! line pos-x (ref line (inc pos-x)))
          (inc! pos-x))
        (set! line (- (:length line) 1) 0))

  (defn backward-delete-char (self)
        (def line (:get-line self (ref self :pos-y)))
        (when-not line (return))
        (def pos-x (ref self :pos-x))
        (when (< pos-x 1)
          (return (:backward-delete-line self)))
        (def bl (- (:length line) 1))
        (while (<= pos-x bl)
          (set! line (- pos-x 1) (ref line pos-x))
          (inc! pos-x))
        (set! line bl 0)
        (set! self :pos-x (- (ref self :pos-x) 1)))

  (defn insert-char (self c)
        :export
        (def line (:get-line self (ref self :pos-y)))
        (when-not line
                                        ; ToDo: add line?
                  (return))
        (def pos-x (ref self :pos-x))
        (def i (- (:length line) 1))
        (when-not (zero? (ref line i))
                  (:length! line (+ (:length line) 16))
                  (def i (- (:length line) 1)))
        (while (> i pos-x)
          (set! line i (ref line (dec i)))
          (dec! i))
        (set! line pos-x c)
        (set! self :pos-x (inc pos-x)))

  (defn insert-newline (self)
        :export
        (def lines (ref self :lines))
        (def lines-clean (ref self :lines-clean))
        (:length! lines (inc (:length lines)))
        (:length! lines-clean (:length lines))
        (set! lines (- (:length lines) 1) (buffer/allocate 16))
        (set! self :pos-x 0)
        (set! self :pos-y (inc (ref self :pos-y)))
        (:line-clean! self (ref self :pos-y) #f))

  (defn get-buf-name (self buf)
        (def filename (ref self :filename))
        (def saved (if (ref self :changed) "*" ""))
        (fmt "{filename}{saved}"))

  (defn line-clean? (self line)
        (def lines-clean (ref self :lines-clean))
        (when (>= line (:length lines-clean)) (return #f))
        (or (ref lines-clean line) #f))

  (defn line-clean! (self line clean)
        (def lines-clean (ref self :lines-clean))
        (when (>= line (:length lines-clean))
          (:length! lines-clean (inc line)))
        (set! lines-clean line clean)))