source: mainline/uspace/app/edit/sheet.c

Last change on this file was 994f87b, checked in by Jiri Svoboda <jiri@…>, 12 months ago

Add File/New and File/Open to text editor

Change repeat search shortcut to Ctrl-R as Ctrl-N is now used for
New File.

  • Property mode set to 100644
File size: 8.6 KB
RevLine 
[3052ff4]1/*
[994f87b]2 * Copyright (c) 2024 Jiri Svoboda
[3052ff4]3 * All rights reserved.
4 *
5 * Redistribution and use in source and binary forms, with or without
6 * modification, are permitted provided that the following conditions
7 * are met:
8 *
9 * - Redistributions of source code must retain the above copyright
10 * notice, this list of conditions and the following disclaimer.
11 * - Redistributions in binary form must reproduce the above copyright
12 * notice, this list of conditions and the following disclaimer in the
13 * documentation and/or other materials provided with the distribution.
14 * - The name of the author may not be used to endorse or promote products
15 * derived from this software without specific prior written permission.
16 *
17 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
18 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
19 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
20 * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
21 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
22 * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
23 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
24 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
25 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
26 * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
27 */
28
29/** @addtogroup edit
30 * @{
31 */
32/**
33 * @file
34 * @brief Prototype implementation of Sheet data structure.
35 *
36 * The sheet is an abstract data structure representing a piece of text.
37 * On top of this data structure we can implement a text editor. It is
38 * possible to implement the sheet such that the editor can make small
39 * changes to large files or files containing long lines efficiently.
40 *
41 * The sheet structure allows basic operations of text insertion, deletion,
42 * retrieval and mapping of coordinates to position in the file and vice
43 * versa. The text that is inserted or deleted can contain tabs and newlines
44 * which are interpreted and properly acted upon.
45 *
46 * This is a trivial implementation with poor efficiency with O(N+n)
47 * insertion and deletion and O(N) mapping (in both directions), where
48 * N is the size of the file and n is the size of the inserted/deleted text.
49 */
50
51#include <stdlib.h>
[19f857a]52#include <str.h>
[3052ff4]53#include <errno.h>
54#include <adt/list.h>
55#include <align.h>
[c29f20b]56#include <macros.h>
[3052ff4]57
58#include "sheet.h"
[69cf3a4]59#include "sheet_impl.h"
[3052ff4]60
61enum {
[c29f20b]62 TAB_WIDTH = 8,
63
64 /** Initial of dat buffer in bytes */
65 INITIAL_SIZE = 32
[3052ff4]66};
67
68/** Initialize an empty sheet. */
[b7fd2a0]69errno_t sheet_create(sheet_t **rsh)
[3052ff4]70{
[69cf3a4]71 sheet_t *sh;
72
73 sh = calloc(1, sizeof(sheet_t));
74 if (sh == NULL)
75 return ENOMEM;
76
[c29f20b]77 sh->dbuf_size = INITIAL_SIZE;
[3052ff4]78 sh->text_size = 0;
79
80 sh->data = malloc(sh->dbuf_size);
[14a014f]81 if (sh->data == NULL) {
82 free(sh);
[3052ff4]83 return ENOMEM;
[14a014f]84 }
[3052ff4]85
[b72efe8]86 list_initialize(&sh->tags);
[3052ff4]87
[69cf3a4]88 *rsh = sh;
[3052ff4]89 return EOK;
90}
91
[994f87b]92/** Destroy sheet. */
93void sheet_destroy(sheet_t *sh)
94{
95 free(sh->data);
96 free(sh);
97}
98
[3052ff4]99/** Insert text into sheet.
100 *
101 * @param sh Sheet to insert to.
102 * @param pos Point where to insert.
103 * @param dir Whether to insert before or after the point (affects tags).
104 * @param str The text to insert (printable characters, tabs, newlines).
105 *
[cde999a]106 * @return EOK on success or an error code.
[3052ff4]107 *
108 * @note @a dir affects which way tags that were placed on @a pos will
109 * move. If @a dir is @c dir_before, the tags will move forward
110 * and vice versa.
111 */
[b7fd2a0]112errno_t sheet_insert(sheet_t *sh, spt_t *pos, enum dir_spec dir, char *str)
[3052ff4]113{
114 char *ipp;
115 size_t sz;
[c29f20b]116 char *newp;
[3052ff4]117
118 sz = str_size(str);
[c29f20b]119 if (sh->text_size + sz > sh->dbuf_size) {
120 /* Enlarge data buffer. */
121 newp = realloc(sh->data, sh->dbuf_size * 2);
122 if (newp == NULL)
123 return ELIMIT;
124
125 sh->data = newp;
126 sh->dbuf_size = sh->dbuf_size * 2;
127 }
[3052ff4]128
129 ipp = sh->data + pos->b_off;
130
[c29f20b]131 /* Copy data. */
[3052ff4]132 memmove(ipp + sz, ipp, sh->text_size - pos->b_off);
133 memcpy(ipp, str, sz);
134 sh->text_size += sz;
135
[c29f20b]136 /* Adjust tags. */
[3052ff4]137
[feeac0d]138 list_foreach(sh->tags, link, tag_t, tag) {
[3052ff4]139 if (tag->b_off > pos->b_off)
140 tag->b_off += sz;
141 else if (tag->b_off == pos->b_off && dir == dir_before)
142 tag->b_off += sz;
143 }
144
145 return EOK;
146}
147
148/** Delete text from sheet.
149 *
150 * Deletes the range of text between two points from the sheet.
151 *
152 * @param sh Sheet to delete from.
153 * @param spos Starting point.
154 * @param epos Ending point.
155 *
[cde999a]156 * @return EOK on success or an error code.
[7c3fb9b]157 */
[b7fd2a0]158errno_t sheet_delete(sheet_t *sh, spt_t *spos, spt_t *epos)
[3052ff4]159{
160 char *spp;
161 size_t sz;
[c29f20b]162 char *newp;
163 size_t shrink_size;
[3052ff4]164
165 spp = sh->data + spos->b_off;
166 sz = epos->b_off - spos->b_off;
167
168 memmove(spp, spp + sz, sh->text_size - (spos->b_off + sz));
169 sh->text_size -= sz;
170
[c29f20b]171 /* Adjust tags. */
[feeac0d]172 list_foreach(sh->tags, link, tag_t, tag) {
[3052ff4]173 if (tag->b_off >= epos->b_off)
174 tag->b_off -= sz;
175 else if (tag->b_off >= spos->b_off)
176 tag->b_off = spos->b_off;
177 }
[c29f20b]178
179 /* See if we should free up some memory. */
180 shrink_size = max(sh->dbuf_size / 4, INITIAL_SIZE);
181 if (sh->text_size <= shrink_size && sh->dbuf_size > INITIAL_SIZE) {
182 /* Shrink data buffer. */
183 newp = realloc(sh->data, shrink_size);
184 if (newp == NULL) {
185 /* Failed to shrink buffer... no matter. */
186 return EOK;
187 }
188
189 sh->data = newp;
190 sh->dbuf_size = shrink_size;
191 }
192
[3052ff4]193 return EOK;
194}
195
196/** Read text from sheet. */
197void sheet_copy_out(sheet_t *sh, spt_t const *spos, spt_t const *epos,
198 char *buf, size_t bufsize, spt_t *fpos)
199{
200 char *spp;
201 size_t range_sz;
202 size_t copy_sz;
203 size_t off, prev;
[28a5ebd]204 char32_t c;
[3052ff4]205
206 spp = sh->data + spos->b_off;
207 range_sz = epos->b_off - spos->b_off;
208 copy_sz = (range_sz < bufsize - 1) ? range_sz : bufsize - 1;
209
210 prev = off = 0;
211 do {
212 prev = off;
213 c = str_decode(spp, &off, copy_sz);
214 } while (c != '\0');
215
216 /* Crop copy_sz down to the last full character. */
217 copy_sz = prev;
218
219 memcpy(buf, spp, copy_sz);
220 buf[copy_sz] = '\0';
221
222 fpos->b_off = spos->b_off + copy_sz;
223 fpos->sh = sh;
224}
225
226/** Get point preceding or following character cell. */
227void sheet_get_cell_pt(sheet_t *sh, coord_t const *coord, enum dir_spec dir,
228 spt_t *pt)
229{
230 size_t cur_pos, prev_pos;
[28a5ebd]231 char32_t c;
[3052ff4]232 coord_t cc;
233
234 cc.row = cc.column = 1;
235 cur_pos = prev_pos = 0;
236 while (true) {
237 if (prev_pos >= sh->text_size) {
238 /* Cannot advance any further. */
239 break;
240 }
241
242 if ((cc.row >= coord->row && cc.column > coord->column) ||
243 cc.row > coord->row) {
244 /* We are past the requested coordinates. */
245 break;
246 }
247
248 prev_pos = cur_pos;
249
250 c = str_decode(sh->data, &cur_pos, sh->text_size);
251 if (c == '\n') {
252 ++cc.row;
253 cc.column = 1;
254 } else if (c == '\t') {
255 cc.column = 1 + ALIGN_UP(cc.column, TAB_WIDTH);
256 } else {
257 ++cc.column;
258 }
259 }
260
261 pt->sh = sh;
262 pt->b_off = (dir == dir_before) ? prev_pos : cur_pos;
263}
264
265/** Get the number of character cells a row occupies. */
266void sheet_get_row_width(sheet_t *sh, int row, int *length)
267{
268 coord_t coord;
269 spt_t pt;
270
271 /* Especially nasty hack */
272 coord.row = row;
273 coord.column = 65536;
[a35b458]274
[3052ff4]275 sheet_get_cell_pt(sh, &coord, dir_before, &pt);
276 spt_get_coord(&pt, &coord);
[8f6bffdd]277 *length = coord.column;
[3052ff4]278}
279
280/** Get the number of rows in a sheet. */
281void sheet_get_num_rows(sheet_t *sh, int *rows)
282{
283 int cnt;
284 size_t bo;
285
286 cnt = 1;
287 for (bo = 0; bo < sh->dbuf_size; ++bo) {
288 if (sh->data[bo] == '\n')
289 cnt += 1;
290 }
291
292 *rows = cnt;
293}
294
295/** Get the coordinates of an s-point. */
296void spt_get_coord(spt_t const *pos, coord_t *coord)
297{
298 size_t off;
299 coord_t cc;
[28a5ebd]300 char32_t c;
[3052ff4]301 sheet_t *sh;
302
303 sh = pos->sh;
304 cc.row = cc.column = 1;
305
306 off = 0;
307 while (off < pos->b_off && off < sh->text_size) {
308 c = str_decode(sh->data, &off, sh->text_size);
309 if (c == '\n') {
310 ++cc.row;
311 cc.column = 1;
312 } else if (c == '\t') {
313 cc.column = 1 + ALIGN_UP(cc.column, TAB_WIDTH);
314 } else {
315 ++cc.column;
316 }
317 }
318
319 *coord = cc;
320}
321
322/** Test if two s-points are equal. */
323bool spt_equal(spt_t const *a, spt_t const *b)
324{
325 return a->b_off == b->b_off;
326}
327
[7feb86e6]328/** Get a character at spt and return next spt */
[28a5ebd]329char32_t spt_next_char(spt_t spt, spt_t *next)
[7feb86e6]330{
[28a5ebd]331 char32_t ch = str_decode(spt.sh->data, &spt.b_off, spt.sh->text_size);
[7feb86e6]332 if (next)
333 *next = spt;
334 return ch;
335}
336
[28a5ebd]337char32_t spt_prev_char(spt_t spt, spt_t *prev)
[7feb86e6]338{
[28a5ebd]339 char32_t ch = str_decode_reverse(spt.sh->data, &spt.b_off, spt.sh->text_size);
[7feb86e6]340 if (prev)
341 *prev = spt;
342 return ch;
343}
344
[3052ff4]345/** Place a tag on the specified s-point. */
346void sheet_place_tag(sheet_t *sh, spt_t const *pt, tag_t *tag)
347{
348 tag->b_off = pt->b_off;
349 tag->sh = sh;
[b72efe8]350 list_append(&tag->link, &sh->tags);
[3052ff4]351}
352
353/** Remove a tag from the sheet. */
354void sheet_remove_tag(sheet_t *sh, tag_t *tag)
355{
356 list_remove(&tag->link);
357}
358
359/** Get s-point on which the tag is located right now. */
360void tag_get_pt(tag_t const *tag, spt_t *pt)
361{
362 pt->b_off = tag->b_off;
363 pt->sh = tag->sh;
364}
365
366/** @}
367 */
Note: See TracBrowser for help on using the repository browser.