text-buffer.sl 24 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733
  1. %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
  2. %
  3. % Text-Buffer.SL
  4. %
  5. % Author: Alan Snyder
  6. % Hewlett-Packard/CRC
  7. % Date: 20 August 1982
  8. % Revised: 23 February 1983
  9. %
  10. % A text buffer. Supports the primitive editing functions. The strings in a
  11. % text buffer are never modified. This allows EQ to be used to minimize
  12. % redisplay.
  13. %
  14. % 23-Feb-83 Alan Snyder
  15. % Revise stream operations to work with any type of object.
  16. % 15-Feb-83 Alan Snyder
  17. % Revise insertion code to reduce unnecessary consing.
  18. % Remove char-blank? macro (NMODE has a function char-blank?).
  19. % 19-Jan-83 Jeff Soreff
  20. % Name made settable in text buffer.
  21. % 3-Dec-82 Alan Snyder
  22. % Added cleanup method.
  23. %
  24. %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
  25. (BothTimes (load objects))
  26. (CompileTime (load fast-int fast-vectors fast-strings))
  27. (de create-text-buffer (name) % not for direct use in NMODE
  28. (let ((buffer (make-instance 'text-buffer 'name name)))
  29. buffer))
  30. (defflavor text-buffer (
  31. (last-line 0) % index of last line in buffer (n >= 0)
  32. (line-pos 0) % index of "current" line (0 <= n <= last-line)
  33. (char-pos 0) % index of "current" character in current line
  34. % (0 <= n <= linelength)
  35. lines % vector of strings
  36. name % string name of buffer
  37. (file-name NIL) % string name of attached file (or NIL)
  38. (modified? NIL) % T => buffer is different than file
  39. marks % ring buffer of marks
  40. (mode NIL) % the buffer's Mode
  41. (previous-buffer NIL) % (optional) previous buffer
  42. (p-list NIL) % association list of properties
  43. )
  44. ()
  45. (gettable-instance-variables line-pos char-pos)
  46. (settable-instance-variables file-name modified? mode previous-buffer name)
  47. (initable-instance-variables name)
  48. )
  49. %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
  50. % Private Macros:
  51. (CompileTime (progn
  52. (defmacro with-current-line ((var) . forms)
  53. `(let ((,var (vector-fetch lines line-pos)))
  54. ,@forms
  55. ))
  56. (defmacro with-current-line ((var) . forms) % avoid compiler bug!
  57. `(let ((**LINES** lines))
  58. (let ((,var (vector-fetch **LINES** line-pos)))
  59. ,@forms
  60. )))
  61. (defmacro with-current-line-copied ((var) . forms)
  62. `(let ((**LINES** lines) (**LINE-POS** line-pos))
  63. (let ((,var (copystring (vector-fetch **LINES** **line-pos**))))
  64. (vector-store **LINES** **line-pos** ,var)
  65. ,@forms
  66. )))
  67. )) % End of CompileTime
  68. %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
  69. (defmethod (text-buffer position) ()
  70. % Return the "current position" in the buffer as a BUFFER-POSITION object.
  71. (buffer-position-create line-pos char-pos)
  72. )
  73. (defmethod (text-buffer set-position) (bp)
  74. % Set the "current position" in the buffer from the specified
  75. % BUFFER-POSITION object. Clips the line-position and char-position.
  76. (=> self goto (buffer-position-line bp) (buffer-position-column bp))
  77. )
  78. (defmethod (text-buffer buffer-end-position) ()
  79. % Return the BUFFER-POSITION object corresponding to the end of the buffer.
  80. (buffer-position-create
  81. last-line
  82. (string-length (vector-fetch lines last-line))
  83. ))
  84. (defmethod (text-buffer goto) (lpos cpos)
  85. % Set the "current position" in the buffer. Clips the line-position and
  86. % char-position.
  87. (if (< lpos 0) (setf lpos 0))
  88. (if (> lpos last-line) (setf lpos last-line))
  89. (setf line-pos lpos)
  90. (=> self set-char-pos cpos)
  91. )
  92. (defmethod (text-buffer set-line-pos) (lpos)
  93. % Set the "current line position" in the buffer. Clips the line-position
  94. % and char-position.
  95. (when (~= lpos line-pos)
  96. (if (< lpos 0) (setf lpos 0))
  97. (if (> lpos last-line) (setf lpos last-line))
  98. (setf line-pos lpos)
  99. (with-current-line (l)
  100. (if (> char-pos (string-length l))
  101. (setf char-pos (string-length l))
  102. ))
  103. ))
  104. (defmethod (text-buffer set-char-pos) (cpos)
  105. % Set the "current character position" in the buffer. Clips the specified
  106. % position to lie in the range 0..line-length.
  107. (if (< cpos 0) (setf cpos 0))
  108. (with-current-line (l)
  109. (if (> cpos (string-length l))
  110. (setf cpos (string-length l))
  111. ))
  112. (setf char-pos cpos)
  113. )
  114. (defmethod (text-buffer clip-position) (bp)
  115. % Return BP if BP is a valid position for this buffer, otherwise return a new
  116. % buffer-position with clipped values.
  117. (let ((lpos (buffer-position-line bp))
  118. (cpos (buffer-position-column bp))
  119. (clipped NIL)
  120. )
  121. (cond ((< lpos 0) (setf lpos 0) (setf clipped T))
  122. ((> lpos last-line) (setf lpos last-line) (setf clipped T))
  123. )
  124. (cond ((< cpos 0) (setf cpos 0) (setf clipped T))
  125. ((> cpos (string-length (vector-fetch lines lpos)))
  126. (setf cpos (string-length (vector-fetch lines lpos)))
  127. (setf clipped T)
  128. ))
  129. (if clipped
  130. (buffer-position-create lpos cpos)
  131. bp
  132. )))
  133. (defmethod (text-buffer size) ()
  134. % Return the actual size of the buffer (number of lines). This number will
  135. % include the "fake" empty line at the end of the buffer, should it exist.
  136. (+ last-line 1)
  137. )
  138. (defmethod (text-buffer visible-size) ()
  139. % Return the apparent size of the buffer (number of lines). This number
  140. % will NOT include the "fake" empty line at the end of the buffer, should it
  141. % exist.
  142. (if (>= (string-upper-bound (vector-fetch lines last-line)) 0)
  143. (+ last-line 1) % The last line is real!
  144. last-line % The last line is fake!
  145. ))
  146. (defmethod (text-buffer contents) ()
  147. % Return the text contents of the buffer (a copy thereof) as a vector of
  148. % strings (the last string is implicitly without a terminating NewLine).
  149. (sub lines 0 last-line)
  150. )
  151. (defmethod (text-buffer current-line) ()
  152. % Return the current line (as a string).
  153. (with-current-line (l)
  154. l))
  155. (defmethod (text-buffer fetch-line) (n)
  156. % Fetch the specified line (as a string). Lines are indexed from 0.
  157. (if (or (< n 0) (> n last-line))
  158. (ContinuableError
  159. 0
  160. (BldMsg "Line index %w out of range." n)
  161. "")
  162. (vector-fetch lines n)
  163. ))
  164. (defmethod (text-buffer store-line) (n new-line)
  165. % Replace the specified line with a new string.
  166. (if (or (< n 0) (> n last-line))
  167. (ContinuableError
  168. 0
  169. (BldMsg "Line index %w out of range." n)
  170. "")
  171. % else
  172. (setf modified? T)
  173. (vector-store lines n new-line)
  174. (if (= line-pos n)
  175. (let ((len (string-length new-line)))
  176. (if (> char-pos len)
  177. (setf char-pos len)
  178. )))
  179. ))
  180. (defmethod (text-buffer select) ()
  181. % Attach the buffer to the current window, making it the current buffer.
  182. (buffer-select self)
  183. )
  184. (defmethod (text-buffer set-mark) (bp)
  185. % PUSH the specified position onto the ring buffer of marks.
  186. % The specified position thus becomes the current "mark".
  187. (ring-buffer-push marks bp)
  188. )
  189. (defmethod (text-buffer set-mark-from-point) ()
  190. % PUSH the current position onto the ring buffer of marks.
  191. % The current position thus becomes the current "mark".
  192. (ring-buffer-push marks (buffer-position-create line-pos char-pos))
  193. )
  194. (defmethod (text-buffer mark) ()
  195. % Return the current "mark".
  196. (ring-buffer-top marks)
  197. )
  198. (defmethod (text-buffer previous-mark) ()
  199. % POP the current mark off the ring buffer of marks.
  200. % Return the new current mark.
  201. (ring-buffer-pop marks)
  202. (ring-buffer-top marks)
  203. )
  204. (defmethod (text-buffer get) (property-name)
  205. % Return the object associated with the specified property name (ID).
  206. % Returns NIL if named property has not been defined.
  207. (let ((pair (atsoc property-name p-list)))
  208. (if (PairP pair) (cdr pair))))
  209. (defmethod (text-buffer put) (property-name property)
  210. % Associate the specified object with the specified property name (ID).
  211. % GET on that property-name will henceforth return the object.
  212. (let ((pair (atsoc property-name p-list)))
  213. (if (PairP pair)
  214. (rplacd pair property)
  215. (setf p-list (cons (cons property-name property) p-list))
  216. )))
  217. (defmethod (text-buffer reset) ()
  218. % Reset the contents of the buffer to empty and "not modified".
  219. (setf lines (MkVect 1))
  220. (vector-store lines 0 "")
  221. (setf last-line 0)
  222. (setf line-pos 0)
  223. (setf char-pos 0)
  224. (setf modified? NIL)
  225. )
  226. (defmethod (text-buffer extract-region) (delete-it bp1 bp2)
  227. % Delete (if delete-it is non-NIL) or copy (otherwise) the text between
  228. % position BP1 and position BP2. Return the deleted (or copied) text as a
  229. % pair (CONS direction-of-deletion vector-of-strings). The returned
  230. % direction is +1 if BP1 <= BP2, and -1 otherwise. The current position is
  231. % set to the beginning of the region if deletion is performed.
  232. (setf bp1 (=> self clip-position bp1))
  233. (setf bp2 (=> self clip-position bp2))
  234. (prog (dir text text-last l1 c1 l2 c2 line1 line2)
  235. (setf dir 1) % the default case
  236. % ensure that BP1 is not beyond BP2
  237. (let ((comparison (buffer-position-compare bp1 bp2)))
  238. (if (> comparison 0)
  239. (psetq dir -1 bp1 bp2 bp2 bp1))
  240. (if (and delete-it (~= comparison 0))
  241. (setf modified? T))
  242. )
  243. (setf l1 (buffer-position-line bp1))
  244. (setf c1 (buffer-position-column bp1))
  245. (setf l2 (buffer-position-line bp2))
  246. (setf c2 (buffer-position-column bp2))
  247. % Ensure the continued validity of the current position.
  248. (if delete-it (=> self set-position bp1))
  249. % Create a vector for the extracted text.
  250. (setf text-last (- l2 l1)) % highest index in TEXT vector
  251. (setf text (MkVect text-last))
  252. (setf line1 (vector-fetch lines l1)) % first line (partially) in region
  253. (cond
  254. ((= l1 l2) % region lies within a single line (easy!)
  255. (vector-store text 0 (substring line1 c1 c2))
  256. (if delete-it
  257. (vector-store lines l1 (string-concat
  258. (substring line1 0 c1)
  259. (string-rest line1 c2)
  260. )))
  261. (return (cons dir text))))
  262. % Here if region spans multiple lines.
  263. (setf line2 (vector-fetch lines l2)) % last line (partially) in region
  264. (vector-store text 0 (string-rest line1 c1))
  265. (vector-store text text-last (substring line2 0 c2))
  266. % Copy remaining text from region.
  267. (for (from i 1 (- text-last 1))
  268. (do (vector-store text i (vector-fetch lines (+ l1 i)))))
  269. (when delete-it
  270. (vector-store lines l1 (string-concat
  271. (substring line1 0 c1)
  272. (string-rest line2 c2)))
  273. (=> self &delete-lines (+ l1 1) text-last)
  274. )
  275. (return (cons dir text))
  276. ))
  277. %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
  278. % The following methods are not really primitive, but are provided as
  279. % a public service.
  280. (defmethod (text-buffer current-line-length) ()
  281. % Return the number of characters in the current line.
  282. (with-current-line (l)
  283. (string-length l)))
  284. (defmethod (text-buffer current-line-empty?) ()
  285. % Return T if the current line contains no characters.
  286. (with-current-line (l)
  287. (string-empty? l)))
  288. (defmethod (text-buffer current-line-blank?) ()
  289. % Return T if the current line contains no non-blank characters.
  290. (with-current-line (l)
  291. (for (from i 0 (string-upper-bound l))
  292. (always (char-blank? (string-fetch l i)))
  293. )))
  294. (defmethod (text-buffer at-line-start?) ()
  295. % Return T if we are positioned at the start of the current line.
  296. (= char-pos 0))
  297. (defmethod (text-buffer at-line-end?) ()
  298. % Return T if we are positioned at the end of the current line.
  299. (with-current-line (l)
  300. (> char-pos (string-upper-bound l))))
  301. (defmethod (text-buffer at-buffer-start?) ()
  302. % Return T if we are positioned at the start of the buffer.
  303. (and (= line-pos 0) (= char-pos 0)))
  304. (defmethod (text-buffer at-buffer-end?) ()
  305. % Return T if we are positioned at the end of the buffer.
  306. (and
  307. (>= line-pos last-line)
  308. (> char-pos (string-upper-bound (vector-fetch lines last-line)))))
  309. (defmethod (text-buffer current-line-is-first?) ()
  310. % Return T if the current line is the first line in the buffer.
  311. (= line-pos 0))
  312. (defmethod (text-buffer current-line-is-last?) ()
  313. % Return T if the current line is the last line in the buffer.
  314. (>= line-pos last-line))
  315. (defmethod (text-buffer current-line-fetch) (n)
  316. % Return the character at character position N within the current line.
  317. % An error is generated if N is out of range.
  318. (with-current-line (l)
  319. (if (and (>= n 0) (<= n (string-upper-bound l)))
  320. (string-fetch l n)
  321. (ContinuableError
  322. 0
  323. (BldMsg "Character index %w out of range." n)
  324. "")
  325. )))
  326. (defmethod (text-buffer current-line-store) (n c)
  327. % Store the character C at char position N within the current line.
  328. % An error is generated if N is out of range.
  329. (with-current-line-copied (l)
  330. (if (and (>= n 0) (<= n (string-upper-bound l)))
  331. (progn
  332. (string-store l n c)
  333. (vector-store lines line-pos l)
  334. (setf modified? T)
  335. )
  336. (ContinuableError
  337. 0
  338. (BldMsg "Character index %w out of range." n)
  339. "")
  340. )))
  341. (defmethod (text-buffer move-to-buffer-start) ()
  342. % Move to the beginning of the buffer.
  343. (setf line-pos 0)
  344. (setf char-pos 0)
  345. )
  346. (defmethod (text-buffer move-to-buffer-end) ()
  347. % Move to the end of the buffer.
  348. (setf line-pos last-line)
  349. (with-current-line (l)
  350. (setf char-pos (string-length l)))
  351. )
  352. (defmethod (text-buffer move-to-start-of-line) ()
  353. % Move to the beginning of the current line.
  354. (setf char-pos 0))
  355. (defmethod (text-buffer move-to-end-of-line) ()
  356. % Move to the end of the current line.
  357. (with-current-line (l)
  358. (setf char-pos (string-length l))))
  359. (defmethod (text-buffer move-to-next-line) ()
  360. % Move to the beginning of the next line.
  361. % If already at the last line, move to the end of the line.
  362. (cond ((< line-pos last-line)
  363. (setf line-pos (+ line-pos 1))
  364. (setf char-pos 0))
  365. (t (=> self move-to-end-of-line))))
  366. (defmethod (text-buffer move-to-previous-line) ()
  367. % Move to the beginning of the previous line.
  368. % If already at the first line, move to the beginning of the line.
  369. (if (> line-pos 0)
  370. (setf line-pos (- line-pos 1)))
  371. (setf char-pos 0))
  372. (defmethod (text-buffer move-forward) ()
  373. % Move to the next character in the current buffer.
  374. % Do nothing if already at the end of the buffer.
  375. (if (=> self at-line-end?)
  376. (=> self move-to-next-line)
  377. (setf char-pos (+ char-pos 1))
  378. ))
  379. (defmethod (text-buffer move-backward) ()
  380. % Move to the previous character in the current buffer.
  381. % Do nothing if already at the start of the buffer.
  382. (if (> char-pos 0)
  383. (setf char-pos (- char-pos 1))
  384. (when (> line-pos 0)
  385. (setf line-pos (- line-pos 1))
  386. (=> self move-to-end-of-line)
  387. )))
  388. (defmethod (text-buffer next-character) ()
  389. % Return the character to the right of the current position.
  390. % Return NIL if at the end of the buffer.
  391. (with-current-line (l)
  392. (if (>= char-pos (string-length l))
  393. (if (= line-pos last-line)
  394. NIL
  395. (char EOL)
  396. )
  397. (string-fetch l char-pos)
  398. )))
  399. (defmethod (text-buffer previous-character) ()
  400. % Return the character to the left of the current position.
  401. % Return NIL if at the beginning of the buffer.
  402. (if (= char-pos 0)
  403. (if (= line-pos 0) NIL #\EOL)
  404. (with-current-line (l)
  405. (string-fetch l (- char-pos 1)))
  406. ))
  407. (defmethod (text-buffer insert-character) (c)
  408. % Insert character C at the current position in the buffer and advance past
  409. % that character. Implementation note: some effort is made here to avoid
  410. % unnecessary consing.
  411. (if (= c #\EOL)
  412. (=> self insert-eol)
  413. % else
  414. (with-current-line (l)
  415. (let* ((current-length (string-length l))
  416. (head-string
  417. (when (> char-pos 0)
  418. (if (= char-pos current-length) l (substring l 0 char-pos))))
  419. (tail-string
  420. (when (< char-pos current-length)
  421. (if (= char-pos 0) l (substring l char-pos current-length))))
  422. (s (string c))
  423. )
  424. (when head-string (setf s (string-concat head-string s)))
  425. (when tail-string (setf s (string-concat s tail-string)))
  426. (vector-store lines line-pos s)
  427. (setf char-pos (+ char-pos 1))
  428. (setf modified? T)
  429. ))))
  430. (defmethod (text-buffer insert-eol) ()
  431. % Insert a line-break at the current position in the buffer and advance to
  432. % the beginning of the newly-formed line. Implementation note: some effort
  433. % is made here to avoid unnecessary consing.
  434. (with-current-line (l)
  435. (=> self &insert-gap line-pos 1)
  436. (let* ((current-length (string-length l))
  437. (head-string
  438. (when (> char-pos 0)
  439. (if (= char-pos current-length) l (substring l 0 char-pos))))
  440. (tail-string
  441. (when (< char-pos current-length)
  442. (if (= char-pos 0) l (substring l char-pos current-length))))
  443. )
  444. (vector-store lines line-pos (or head-string ""))
  445. (setf line-pos (+ line-pos 1))
  446. (vector-store lines line-pos (or tail-string ""))
  447. (setf char-pos 0)
  448. (setf modified? T)
  449. )))
  450. (defmethod (text-buffer insert-line) (l)
  451. % Insert the specified string as a new line in front of the current line.
  452. % Advance past the newly inserted line. Note: L henceforth must never be
  453. % modified.
  454. (=> self &insert-gap line-pos 1)
  455. (vector-store lines line-pos l)
  456. (setf line-pos (+ line-pos 1))
  457. (setf modified? T)
  458. )
  459. (defmethod (text-buffer insert-string) (s)
  460. % Insert the string S at the current position. Advance past the
  461. % newly-inserted string. Note: S must not contain EOL characters! Note: S
  462. % henceforth must never be modified. Implementation note: some effort is
  463. % made here to avoid unnecessary consing.
  464. (let ((insert-length (string-length s)))
  465. (when (> insert-length 0)
  466. (with-current-line (l)
  467. (let* ((current-length (string-length l))
  468. (head-string
  469. (when (> char-pos 0)
  470. (if (= char-pos current-length) l (substring l 0 char-pos))))
  471. (tail-string
  472. (when (< char-pos current-length)
  473. (if (= char-pos 0) l (substring l char-pos current-length))))
  474. )
  475. (when head-string (setf s (string-concat head-string s)))
  476. (when tail-string (setf s (string-concat s tail-string)))
  477. (vector-store lines line-pos s)
  478. (setf char-pos (+ char-pos insert-length))
  479. (setf modified? T)
  480. )))))
  481. (defmethod (text-buffer insert-text) (v)
  482. % V is a vector of strings similar to LINES (e.g., the last string in V is
  483. % considered to be an unterminated line). Thus, V must have at least one
  484. % element. Insert this stuff at the current position and advance past it.
  485. (with-current-line (l)
  486. (let ((v-last (vector-upper-bound v)))
  487. (=> self &insert-gap line-pos v-last)
  488. (let ((vec lines)
  489. (prefix-text (substring l 0 char-pos))
  490. (suffix-text (string-rest l char-pos))
  491. )
  492. (vector-store vec line-pos
  493. (string-concat prefix-text (vector-fetch v 0)))
  494. (for (from i 1 v-last)
  495. (do (setf line-pos (+ line-pos 1))
  496. (vector-store vec line-pos (vector-fetch v i))))
  497. (setf char-pos (string-length (vector-fetch vec line-pos)))
  498. (vector-store vec line-pos
  499. (string-concat (vector-fetch vec line-pos) suffix-text))
  500. (setf modified? T)
  501. ))))
  502. (defmethod (text-buffer delete-next-character) ()
  503. % Delete the next character.
  504. % Do nothing if at the end of the buffer.
  505. (with-current-line (l)
  506. (if (= char-pos (string-length l))
  507. (if (= line-pos last-line)
  508. NIL
  509. % else (at end of line other than last)
  510. (vector-store lines line-pos
  511. (string-concat l (vector-fetch lines (+ line-pos 1))))
  512. (=> self &delete-lines (+ line-pos 1) 1)
  513. (setf modified? T)
  514. )
  515. % else (not at the end of a line)
  516. (vector-store lines line-pos
  517. (string-concat
  518. (substring l 0 char-pos)
  519. (string-rest l (+ char-pos 1))
  520. ))
  521. (setf modified? T)
  522. )))
  523. (defmethod (text-buffer delete-previous-character) ()
  524. % Delete the previous character.
  525. % Do nothing if at the beginning of the buffer.
  526. (if (not (=> self at-buffer-start?))
  527. (progn
  528. (=> self move-backward)
  529. (=> self delete-next-character)
  530. (setf modified? T)
  531. )))
  532. % Implementation note: On the 9836, the following implementation of the
  533. % read-from-stream method using GETC is slightly slower than a much simpler
  534. % implementation of read-from-stream using GETL (although the GETL method is
  535. % highly optimized). For a file with 874 lines, using GETC took 7480 ms vs.
  536. % 7130 ms. when using GETL. The problem with GETL, however, is that it does
  537. % not report whether the last line of the file is terminated with a Newline or
  538. % not. This functional difference could conceivably be important. Luckily,
  539. % the improvement in speed is sufficiently small to be irrelevant.
  540. (defmethod (text-buffer read-from-stream) (s)
  541. (=> self reset)
  542. (let* ((line-buffer (make-string 200 0))
  543. (buffer-top 200)
  544. (getc-method (object-get-handler s 'getc))
  545. line-size
  546. ch
  547. )
  548. (while T
  549. (setf line-size 0)
  550. (setf ch (apply getc-method (list s)))
  551. (while (not (or (null ch) (= ch #\LF)))
  552. (cond ((>= line-size buffer-top)
  553. (setf line-buffer (concat line-buffer (make-string 200 0)))
  554. (setf buffer-top (+ buffer-top 200))
  555. ))
  556. (string-store line-buffer line-size ch)
  557. (setf line-size (+ line-size 1))
  558. (setf ch (apply getc-method (list s)))
  559. )
  560. (if (not (and (null ch) (= line-size 0)))
  561. (=> self insert-line (sub line-buffer 0 (- line-size 1)))
  562. )
  563. (when (null ch)
  564. (if (> line-size 0) (=> self delete-previous-character))
  565. (exit)
  566. ))
  567. (=> self move-to-buffer-start)
  568. (=> self set-modified? NIL)
  569. ))
  570. (defmethod (text-buffer write-to-stream) (s)
  571. (let* ((vec lines)
  572. (putl-method (object-get-handler s 'putl))
  573. )
  574. (for (from i 0 (- last-line 1))
  575. (do (apply putl-method (list s (vector-fetch vec i)))))
  576. (=> s puts (vector-fetch vec last-line))
  577. ))
  578. (defmethod (text-buffer cleanup) ()
  579. % Discard any unused storage.
  580. (if (and previous-buffer (not (buffer-is-selectable? previous-buffer)))
  581. (setf previous-buffer NIL))
  582. (TruncateVector lines last-line)
  583. )
  584. %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
  585. % Private methods:
  586. (defmethod (text-buffer init) (init-plist)
  587. (setf lines (MkVect 0))
  588. (vector-store lines 0 "")
  589. (setf marks (ring-buffer-create 16))
  590. (ring-buffer-push marks (buffer-position-create 0 0))
  591. )
  592. (defmethod (text-buffer &insert-gap) (lpos n-lines)
  593. % Insert N-LINES lines at position LPOS, moving the remaining lines upward
  594. % (if any). LPOS may range from 0 (insert at beginning of buffer) to
  595. % LAST-LINE + 1 (insert at end of buffer). The new lines are not
  596. % specifically initialized (they retain their old values).
  597. (when (> n-lines 0)
  598. (=> self &ensure-room n-lines)
  599. (let ((vec lines))
  600. (for (from i last-line lpos -1)
  601. (do (vector-store vec (+ i n-lines) (vector-fetch vec i)))
  602. )
  603. (setf last-line (+ last-line n-lines))
  604. )))
  605. (defmethod (text-buffer &ensure-room) (lines-needed)
  606. % Ensure that the LINES vector is large enough to add the specified number
  607. % of additional lines.
  608. (let* ((current-bound (vector-upper-bound lines))
  609. (lines-available (- current-bound last-line))
  610. (lines-to-add (- lines-needed lines-available))
  611. )
  612. (when (> lines-to-add 0)
  613. (let ((minimum-incr (>> current-bound 2))) % Increase by at least 25%
  614. (if (< minimum-incr 64) (setf minimum-incr 64))
  615. (if (< lines-to-add minimum-incr) (setf lines-to-add minimum-incr))
  616. )
  617. (let ((new-lines (make-vector (+ current-bound lines-to-add) NIL)))
  618. (for (from i 0 current-bound)
  619. (do (vector-store new-lines i (vector-fetch lines i))))
  620. (setf lines new-lines)
  621. ))))
  622. (defmethod (text-buffer &delete-lines) (lpos n-lines)
  623. % Remove N-LINES lines starting at position LPOS, moving the remaining lines
  624. % downward (if any) and NILing out the obsoleted lines at the end of the
  625. % LINES vector (to allow the strings to be reclaimed). LPOS may range from
  626. % 0 to LAST-LINE.
  627. (when (> n-lines 0)
  628. (let ((vec lines))
  629. (for (from i (+ lpos n-lines) last-line)
  630. (do (vector-store vec (- i n-lines) (vector-fetch vec i)))
  631. )
  632. (setf last-line (- last-line n-lines))
  633. (for (from i 1 n-lines)
  634. (do (vector-store vec (+ last-line i) NIL))
  635. )
  636. )))