gapbuffer.nim 3.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148
  1. import strutils
  2. # Helpers
  3. proc shift*[T](s: var seq[T]): T {.inline, noSideEffect.} =
  4. ## Remove and return the first element of a seq
  5. result = s[0]
  6. s.delete(0)
  7. # Gap buffer
  8. type
  9. GapBuffer* = object of RootObj
  10. head*, tail*: seq[char]
  11. proc newGapBuffer*: GapBuffer =
  12. GapBuffer(
  13. head: @[],
  14. tail: @[]
  15. )
  16. proc toGapBuffer*(str: string, position = -1): GapBuffer =
  17. ## Creates a `GapBuffer` from a `string`
  18. var
  19. buf: seq[char] = @[]
  20. lastChar = str.len - 1
  21. for i in 0..lastChar: buf.add(str[i])
  22. var head, tail: seq[char] = @[]
  23. if position >= 0:
  24. head = buf[0..position]
  25. if position < lastChar:
  26. tail = buf[position+1..lastChar-1]
  27. GapBuffer(
  28. head: head,
  29. tail: tail
  30. )
  31. proc `$`*(buf: GapBuffer): string {.noSideEffect.} =
  32. ## The stringify operator for a GapBuffer argument. Returns `x`
  33. ## converted to a string.
  34. buf.head.join() & buf.tail.join()
  35. proc len*(buf: GapBuffer): int {.noSideEffect.} =
  36. ## Returns the length of a GapBuffer.
  37. buf.head.len + buf.tail.len
  38. proc pos*(buf: GapBuffer): int {.noSideEffect.} =
  39. ## Returns the position of the gap
  40. buf.head.len - 1
  41. proc move*(buf: var GapBuffer, amt: int) =
  42. ## Move the gap.
  43. if amt < 0:
  44. for _ in 1..(-amt):
  45. buf.tail.insert(buf.head.pop(), 0)
  46. else:
  47. for _ in 1..amt:
  48. buf.head.add(buf.tail.shift())
  49. proc moveTo*(buf: var GapBuffer, pos: int) =
  50. ## Move the gap to an absolute position
  51. var headLen = buf.head.len
  52. if pos < headLen:
  53. var newHead, newTail: seq[char] = @[]
  54. for i in 0..pos:
  55. newHead.add(buf.head[i])
  56. for i in pos+1..headLen-1:
  57. newTail.add(buf.head[i])
  58. newTail &= buf.tail
  59. buf.head = newHead
  60. buf.tail = newTail
  61. elif pos > headLen:
  62. var tailPos = pos - headLen
  63. for i in 0..tailPos:
  64. buf.head.add(buf.tail[i])
  65. var newTail: seq[char] = @[]
  66. for i in tailPos+1..buf.tail.len-1:
  67. newTail.add(buf.tail[i])
  68. buf.tail = newTail
  69. proc insert*(buf: var GapBuffer, ch: char) =
  70. ## Insert a character before the gap
  71. buf.head.add(ch)
  72. proc insert*(buf: var GapBuffer, str: string) =
  73. ## Insert a string before the gap
  74. for ch in str.items():
  75. buf.insert(ch)
  76. proc delete*(buf: var GapBuffer) =
  77. ## Remove the character before the gap
  78. buf.head.setLen(buf.head.len - 1)
  79. proc `[]`*(buf: GapBuffer, i: int): char {.noSideEffect.} =
  80. ## Get the character at the index `i`
  81. var headLen = buf.head.len
  82. if i < headLen:
  83. buf.head[i]
  84. else:
  85. buf.tail[i - headLen]
  86. proc `[]=`*(buf: var GapBuffer, i: int, c: char) =
  87. ## Set ther character at the index `i`
  88. var headLen = buf.head.len
  89. if i < headLen:
  90. buf.head[i] = c
  91. else:
  92. buf.tail[i - headLen] = c
  93. # Iterators
  94. iterator items*(buf: GapBuffer): char =
  95. for c in buf.head: yield c
  96. for c in buf.tail: yield c
  97. iterator splitLines*(buf: GapBuffer): seq[char] =
  98. ## Splits a GapBuffer into lines.
  99. template impl(s: seq[char]): untyped =
  100. for i in 0..s.len-1:
  101. case s[i]:
  102. of '\l':
  103. yield line
  104. line = @[]
  105. of '\c':
  106. if i+1 < buf.len and s[i+1] != '\l':
  107. # If it's not a line feed, treat this as the end of the line,
  108. # if it is, let the line feed case handle it on the next
  109. # character.
  110. yield line
  111. line = @[]
  112. else:
  113. line.add(s[i])
  114. var line: seq[char] = @[]
  115. impl(buf.head)
  116. impl(buf.tail)
  117. yield line