+
+ # Appends `length` chars from `s` starting at index `from`
+ #
+ # ~~~
+ # var b = new Buffer
+ # b.append_substring("abcde", 1, 2)
+ # assert b == "bc"
+ # b.append_substring("vwxyz", 2, 3)
+ # assert b == "bcxyz"
+ # b.append_substring("ABCDE", 4, 300)
+ # assert b == "bcxyzE"
+ # b.append_substring("VWXYZ", 400, 1)
+ # assert b == "bcxyzE"
+ # ~~~
+ fun append_substring(s: Text, from, length: Int) do
+ if from < 0 then
+ length += from
+ from = 0
+ end
+ var ln = s.length
+ if (length + from) > ln then length = ln - from
+ if length <= 0 then return
+ append_substring_impl(s, from, length)
+ end
+
+ # Unsafe version of `append_substring` for performance
+ #
+ # NOTE: Use only if sure about `from` and `length`, no checks
+ # or bound recalculation is done
+ fun append_substring_impl(s: Text, from, length: Int) do
+ var max = from + length
+ for i in [from .. max[ do add s[i]
+ end
+
+ redef fun *(i) do
+ var ret = new Buffer.with_cap(byte_length * i)
+ for its in [0 .. i[ do ret.append self
+ return ret
+ end
+
+ redef fun insert_at(s, pos) do
+ var obuf = new Buffer.with_cap(byte_length + s.byte_length)
+ obuf.append_substring(self, 0, pos)
+ obuf.append s
+ obuf.append_substring(self, pos, length - pos)
+ return obuf
+ end
+
+ # Inserts `s` at position `pos`
+ #
+ # ~~~
+ # var b = new Buffer
+ # b.append "美しい世界"
+ # b.insert(" nit ", 3)
+ # assert b == "美しい nit 世界"
+ # ~~~
+ fun insert(s: Text, pos: Int) is abstract
+
+ # Inserts `c` at position `pos`
+ #
+ # ~~~
+ # var b = new Buffer
+ # b.append "美しい世界"
+ # b.insert_char(' ', 3)
+ # assert b == "美しい 世界"
+ # ~~~
+ fun insert_char(c: Char, pos: Int) is abstract
+
+ # Removes a substring from `self` at position `pos`
+ #
+ # NOTE: `length` defaults to 1, expressed in chars
+ #
+ # ~~~
+ # var b = new Buffer
+ # b.append("美しい 世界")
+ # b.remove_at(3)
+ # assert b == "美しい世界"
+ # b.remove_at(1, 2)
+ # assert b == "美世界"
+ # ~~~
+ fun remove_at(pos: Int, length: nullable Int) is abstract
+
+ redef fun reversed do
+ var ret = clone
+ ret.reverse
+ return ret
+ end
+
+ redef fun to_upper do
+ var ret = clone
+ ret.upper
+ return ret
+ end
+
+ redef fun to_lower do
+ var ret = clone
+ ret.lower
+ return ret
+ end
+
+ redef fun to_snake_case do
+ var ret = clone
+ ret.snake_case
+ return ret
+ end
+
+ # Takes a camel case `self` and converts it to snake case
+ #
+ # SEE: `to_snake_case`
+ fun snake_case do
+ if self.is_lower then return
+ var prev_is_lower = false
+ var prev_is_upper = false
+
+ var i = 0
+ while i < length do
+ var char = chars[i]
+ if char.is_lower then
+ prev_is_lower = true
+ prev_is_upper = false
+ else if char.is_upper then
+ if prev_is_lower then
+ insert_char('_', i)
+ i += 1
+ else if prev_is_upper and i + 1 < length and self[i + 1].is_lower then
+ insert_char('_', i)
+ i += 1
+ end
+ self[i] = char.to_lower
+ prev_is_lower = false
+ prev_is_upper = true
+ else
+ prev_is_lower = false
+ prev_is_upper = false
+ end
+ i += 1
+ end
+ end
+
+ redef fun to_camel_case
+ do
+ var new_str = clone
+ new_str.camel_case
+ return new_str
+ end
+
+ # Takes a snake case `self` and converts it to camel case
+ #
+ # SEE: `to_camel_case`
+ fun camel_case do
+ if is_upper then return
+
+ var underscore_count = 0
+
+ var pos = 1
+ while pos < length do
+ var char = self[pos]
+ if char == '_' then
+ underscore_count += 1
+ else if underscore_count > 0 then
+ pos -= underscore_count
+ remove_at(pos, underscore_count)
+ self[pos] = char.to_upper
+ underscore_count = 0
+ end
+ pos += 1
+ end
+ if underscore_count > 0 then remove_at(pos - underscore_count - 1, underscore_count)
+ end
+
+ redef fun capitalized(keep_upper) do
+ if length == 0 then return self
+
+ var buf = new Buffer.with_cap(byte_length)
+ buf.capitalize(keep_upper=keep_upper, src=self)
+ return buf
+ end