godot-xterm/addons/godot_xterm/circular_list.gd

186 lines
4.8 KiB
GDScript3
Raw Normal View History

2020-05-10 12:56:49 +02:00
# Copyright (c) 2016 The xterm.js authors. All rights reserved.
# Ported to GDScript by the GodotXterm authors.
# License MIT
extends Reference
# Represents a circular list; a list with a maximum size that wraps around when push is called,
# overriding values at the start of the list.
signal deleted(index, amount)
2020-05-10 12:56:49 +02:00
signal inserted
signal trimmed
var _array
var _start_index: int
var length: int = 0 setget _set_length,_get_length
var max_length: int setget _set_max_length,_get_max_length
var is_full: bool setget ,_get_is_full
2020-05-10 12:56:49 +02:00
func _set_length(new_length: int):
if new_length > length:
for i in range(length, new_length):
_array[i] = null
length = new_length
func _get_length():
return length
func _set_max_length(new_max_length):
if max_length == new_max_length:
return
# Reconstruct array, starting at index 0.
# Only transfer values from the indexes 0 to length.
var new_array = []
new_array.resize(new_max_length)
for i in range(0, min(new_max_length, length)):
new_array[i] = _array[_get_cyclic_index(i)]
_array = new_array
max_length = new_max_length
_start_index = 0
func _get_max_length():
return max_length
# Ringbuffer is at max length.
func _get_is_full() -> bool:
return length == max_length
func _init(max_length = 0):
2020-05-10 12:56:49 +02:00
self.max_length = max_length
_array = []
_array.resize(max_length)
_start_index = 0
func get_el(index: int):
return _array[_get_cyclic_index(index)]
# Alias for `get_al`.
func get_line(index: int):
return get_el(index)
2020-05-10 12:56:49 +02:00
func set_el(index: int, value) -> void:
_array[_get_cyclic_index(index)] = value
# Alias for `set_el`.
func set_line(index: int, value) -> void:
set_el(index, value)
# Pushes a new value onto the list, wrapping around to the start of the array, overriding index 0
# if the maximum length is reached.
# @param value The value to push onto the list.
2020-05-10 12:56:49 +02:00
func push(value) -> void:
_array[_get_cyclic_index(length)] = value
if length == max_length:
_start_index += 1
_start_index %= max_length
emit_signal("trimmed", 1)
else:
length += 1
# Advance ringbuffer index and return current element for recycling.
# Note: The buffer must be full for this method to work.
# @throws When the buffer is not full.
func recycle():
if length != max_length:
push_error("Can only recycle when the buffer is full")
_start_index = (_start_index + 1) % max_length
emit_signal("trimmed", 1)
return _array[_get_cyclic_index(length - 1)]
# Removes and returns the last value on the list.
# @return The popped value.
func pop():
var last = _array[_get_cyclic_index(length - 1)]
length -= 1
return last
# Deletes and/or inserts items at a particular index (in that order). Unlike
# Array.prototype.splice, this operation does not return the deleted items as a new array in
# order to save creating a new array. Note that this operation may shift all values in the list
# in the worst case.
# @param start The index to delete and/or insert.
# @param deleteCount The number of elements to delete.
# @param items The items to insert.
func splice(start: int, delete_count: int, items: Array = []) -> void:
# Delete items
if delete_count:
for i in range(start, length - delete_count):
_array[_get_cyclic_index(i)] = _array[_get_cyclic_index(i + delete_count)]
length -= delete_count
# Add items
var i = length - 1
while i >= start:
_array[_get_cyclic_index(i + items.size())] = _array[_get_cyclic_index(i)]
i -= 1
for i in range(items.size()):
_array[_get_cyclic_index(start + i)] = items[i]
# Adjust length as needed
if length + items.size() > max_length:
var count_to_trim = (length + items.size()) - max_length
_start_index += count_to_trim
length = max_length
emit_signal("trimmed", count_to_trim)
else:
length += items.size()
# Trims a number of items from the start of the list.
# @param count The number of items to remove.
func trim_start(count: int) -> void:
if count > length:
count = length
_start_index += count
length -= count
emit_signal("trimmed", count)
func shift_elements(start: int, count: int, offset: int) -> void:
if count <= 0:
return
if start < 0 or start >= length:
self.push_error("start argument out of range")
if start + offset < 0:
self.push_error("cannot shift elements in list beyond index 0")
if offset > 0:
for i in range(count - 1, -1, -1):
set_el(start + i + offset, get_el(start + i))
var expand_list_by = (start + count + offset) - length
if expand_list_by > 0:
length += expand_list_by
while length > max_length:
length -= 1
_start_index += 1
emit_signal("trimmed", 1)
else:
for i in range(0, count):
set_el(start + i + offset, get_el(start + i))
2020-05-10 12:56:49 +02:00
func _get_cyclic_index(index: int) -> int:
return (_start_index + index) % max_length
2020-05-10 12:56:49 +02:00
# Wrapper for `push_error` so we can test for calls to this built-in function.
func push_error(message):
push_error(message)