Property definitions

core $ Buffer :: defaultinit
# A mutable sequence of characters.
abstract class Buffer
	super Text

	# Returns an arbitrary subclass of `Buffer` with default parameters
	new is abstract

	# Returns an instance of a subclass of `Buffer` with `i` base capacity
	new with_cap(i: Int) is abstract

	# Returns an instance of a subclass of `Buffer` with `t` as content
	new from_text(t: Text) do
		var ret = new Buffer.with_cap(t.byte_length)
		ret.append t
		return ret
	end

	redef type SELFTYPE: Buffer is fixed

	# Copy-On-Write flag
	#
	# If the `Buffer` was to_s'd, the next in-place altering
	# operation will cause the current `Buffer` to be re-allocated.
	#
	# The flag will then be set at `false`.
	protected var written = false

	# Modifies the char contained at pos `index`
	fun []=(index: Int, item: Char) is abstract

	redef fun to_buffer do return clone

	# ~~~
	# var b = new Buffer
	# b.append("Buffer!")
	# var c = b.clone
	# assert b == c
	# ~~~
	redef fun clone do
		var cln = new Buffer.with_cap(byte_length)
		cln.append self
		return cln
	end

	# Adds a char `c` at the end of self
	fun add(c: Char) is abstract

	# Clears the buffer
	#
	# ~~~
	# var b = new Buffer
	# b.append "hello"
	# assert not b.is_empty
	# b.clear
	# assert b.is_empty
	# ~~~
	fun clear is abstract

	# Enlarges the subsequent array containing the chars of self
	fun enlarge(cap: Int) is abstract

	# Adds the content of text `s` at the end of self
	#
	# ~~~
	# var b = new Buffer
	# b.append "hello"
	# b.append "world"
	# assert b == "helloworld"
	# ~~~
	fun append(s: Text) is abstract

	# `self` is appended in such a way that `self` is repeated `r` times
	#
	# ~~~
	# var b = new Buffer
	# b.append "hello"
	# b.times 3
	# assert b == "hellohellohello"
	# ~~~
	fun times(r: Int) is abstract

	# Reverses itself in-place
	#
	# ~~~
	# var b = new Buffer
	# b.append("hello")
	# b.reverse
	# assert b == "olleh"
	# ~~~
	fun reverse is abstract

	# Changes each lower-case char in `self` by its upper-case variant
	#
	# ~~~
	# var b = new Buffer
	# b.append("Hello World!")
	# b.upper
	# assert b == "HELLO WORLD!"
	# ~~~
	fun upper is abstract

	# Changes each upper-case char in `self` by its lower-case variant
	#
	# ~~~
	# var b = new Buffer
	# b.append("Hello World!")
	# b.lower
	# assert b == "hello world!"
	# ~~~
	fun lower is abstract

	# Capitalizes each word in `self`
	#
	# Letters that follow a letter are lowercased
	# Letters that follow a non-letter are upcased.
	#
	# If `keep_upper = true`, uppercase letters are not lowercased.
	#
	# When `src` is specified, this method reads from `src` instead of `self`
	# but it still writes the result to the beginning of `self`.
	# This requires `self` to have the capacity to receive all of the
	# capitalized content of `src`.
	#
	# SEE: `Char::is_letter` for the definition of a letter.
	#
	# ~~~
	# var b = new FlatBuffer.from("jAVAsCriPt")
	# b.capitalize
	# assert b == "Javascript"
	# b = new FlatBuffer.from("i am root")
	# b.capitalize
	# assert b == "I Am Root"
	# b = new FlatBuffer.from("ab_c -ab0c ab\nc")
	# b.capitalize
	# assert b == "Ab_C -Ab0C Ab\nC"
	#
	# b = new FlatBuffer.from("12345")
	# b.capitalize(src="foo")
	# assert b == "Foo45"
	#
	# b = new FlatBuffer.from("preserve my ACRONYMS")
	# b.capitalize(keep_upper=true)
	# assert b == "Preserve My ACRONYMS"
	# ~~~
	fun capitalize(keep_upper: nullable Bool, src: nullable Text) do
		src = src or else self
		var length = src.length
		if length == 0 then return
		keep_upper = keep_upper or else false

		var c = src[0].to_upper
		self[0] = c
		var prev = c
		for i in [1 .. length[ do
			prev = c
			c = src[i]
			if prev.is_letter then
				if keep_upper then
					self[i] = c
				else
					self[i] = c.to_lower
				end
			else
				self[i] = c.to_upper
			end
		end
	end

	# In Buffers, the internal sequence of character is mutable
	# Thus, `chars` can be used to modify the buffer.
	redef fun chars: Sequence[Char] is abstract

	# 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
end
lib/core/text/abstract_text.nit:1518,1--1866,3