mirror of
https://git.code.sf.net/p/quake/quakeforge
synced 2024-12-11 13:21:10 +00:00
70aa970c32
It should have been this way all along, and it seems I thought they were when I did rua_gui.c as it already freed its resource block, which would have been a double free (oops). Fixes an invalid write when shutting down progs in qwaq-cmd (relevant change not committed).
1079 lines
28 KiB
C
1079 lines
28 KiB
C
/*
|
|
editbuffer.c
|
|
|
|
High level wrapper for TextBuffer_Destroy
|
|
|
|
Copyright (C) 2020 Bill Currie <bill@taniwha.org>
|
|
|
|
Author: Bill Currie <bill@taniwha.org>
|
|
Date: 2020/03/22
|
|
|
|
This program is free software; you can redistribute it and/or
|
|
modify it under the terms of the GNU General Public License
|
|
as published by the Free Software Foundation; either version 2
|
|
of the License, or (at your option) any later version.
|
|
|
|
This program is distributed in the hope that it will be useful,
|
|
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
|
|
|
|
See the GNU General Public License for more details.
|
|
|
|
You should have received a copy of the GNU General Public License
|
|
along with this program; if not, write to:
|
|
|
|
Free Software Foundation, Inc.
|
|
59 Temple Place - Suite 330
|
|
Boston, MA 02111-1307, USA
|
|
|
|
*/
|
|
#ifdef HAVE_CONFIG_H
|
|
# include "config.h"
|
|
#endif
|
|
|
|
#include <ctype.h>
|
|
#include <stdlib.h>
|
|
#include <string.h>
|
|
|
|
#include "QF/progs.h"
|
|
#include "QF/ruamoko.h"
|
|
#include "QF/quakeio.h"
|
|
|
|
#include "QF/ui/txtbuffer.h"
|
|
|
|
#include "ruamoko/qwaq/qwaq.h"
|
|
#include "ruamoko/qwaq/editor/editbuffer.h"
|
|
|
|
#define always_inline inline __attribute__((__always_inline__))
|
|
|
|
typedef struct editbuffer_s {
|
|
struct editbuffer_s *next;
|
|
struct editbuffer_s **prev;
|
|
txtbuffer_t *txtbuffer;
|
|
int modified;
|
|
int tabSize;
|
|
} editbuffer_t;
|
|
|
|
typedef struct qwaq_ebresources_s {
|
|
progs_t *pr;
|
|
PR_RESMAP (editbuffer_t) buffers;
|
|
editbuffer_t *buffer_list;
|
|
} qwaq_ebresources_t;
|
|
|
|
static editbuffer_t *
|
|
editbuffer_new (qwaq_ebresources_t *res)
|
|
{
|
|
editbuffer_t *buffer = PR_RESNEW (res->buffers);
|
|
buffer->next = res->buffer_list;
|
|
buffer->prev = &res->buffer_list;
|
|
if (res->buffer_list) {
|
|
res->buffer_list->prev = &buffer->next;
|
|
}
|
|
res->buffer_list = buffer;
|
|
return buffer;
|
|
}
|
|
|
|
static void
|
|
editbuffer_free (qwaq_ebresources_t *res, editbuffer_t *buffer)
|
|
{
|
|
if (buffer->next) {
|
|
buffer->next->prev = buffer->prev;
|
|
}
|
|
*buffer->prev = buffer->next;
|
|
PR_RESFREE (res->buffers, buffer);
|
|
}
|
|
|
|
static void
|
|
editbuffer_reset (qwaq_ebresources_t *res)
|
|
{
|
|
PR_RESRESET (res->buffers);
|
|
}
|
|
|
|
static inline editbuffer_t *
|
|
editbuffer_get (qwaq_ebresources_t *res, unsigned index)
|
|
{
|
|
return PR_RESGET (res->buffers, index);
|
|
}
|
|
|
|
static inline int __attribute__((pure))
|
|
editbuffer_index (qwaq_ebresources_t *res, editbuffer_t *buffer)
|
|
{
|
|
return PR_RESINDEX (res->buffers, buffer);
|
|
}
|
|
|
|
static always_inline editbuffer_t * __attribute__((pure))
|
|
get_editbuffer (qwaq_ebresources_t *res, const char *name, int handle)
|
|
{
|
|
editbuffer_t *buffer = editbuffer_get (res, handle);
|
|
|
|
if (!buffer || !buffer->txtbuffer) {
|
|
PR_RunError (res->pr, "invalid edit buffer passed to %s", name + 3);
|
|
}
|
|
return buffer;
|
|
}
|
|
|
|
static always_inline int
|
|
isword (unsigned char c)
|
|
{
|
|
return c >= 128 || c == '_' || isalnum(c);
|
|
}
|
|
|
|
static always_inline unsigned
|
|
spanGap (txtbuffer_t *buffer, unsigned ptr)
|
|
{
|
|
return ptr < buffer->gapOffset ? ptr : ptr + buffer->gapSize;
|
|
}
|
|
|
|
static always_inline char
|
|
getChar (txtbuffer_t *buffer, unsigned ptr)
|
|
{
|
|
return buffer->text[spanGap (buffer, ptr)];
|
|
}
|
|
|
|
static always_inline unsigned
|
|
nextChar (txtbuffer_t *buffer, unsigned ptr)
|
|
{
|
|
if (ptr < buffer->textSize && getChar (buffer, ptr) != '\n') {
|
|
return ptr + 1;
|
|
}
|
|
return ptr;
|
|
}
|
|
|
|
static always_inline unsigned
|
|
prevChar (txtbuffer_t *buffer, unsigned ptr)
|
|
{
|
|
if (ptr > 0 && getChar (buffer, ptr - 1) != '\n') {
|
|
return ptr - 1;
|
|
}
|
|
return ptr;
|
|
}
|
|
|
|
static always_inline unsigned __attribute__((pure))
|
|
nextNonSpace (txtbuffer_t *buffer, unsigned ptr)
|
|
{
|
|
while (ptr < buffer->textSize) {
|
|
char c = getChar (buffer, ptr);
|
|
if (!isspace (c) || c == '\n') {
|
|
break;
|
|
}
|
|
ptr++;
|
|
}
|
|
return ptr;
|
|
}
|
|
|
|
static always_inline unsigned
|
|
prevNonSpace (txtbuffer_t *buffer, unsigned ptr)
|
|
{
|
|
while (ptr > 0) {
|
|
char c = getChar (buffer, ptr - 1);
|
|
if (!isspace (c) || c == '\n') {
|
|
break;
|
|
}
|
|
ptr--;
|
|
}
|
|
return ptr;
|
|
}
|
|
|
|
static always_inline unsigned __attribute__((pure))
|
|
nextWord (txtbuffer_t *buffer, unsigned ptr)
|
|
{
|
|
if (buffer->textSize && ptr < buffer->textSize - 1) {
|
|
while (ptr < buffer->textSize) {
|
|
char c = getChar (buffer, ptr);
|
|
if (!isword (c)) {
|
|
break;
|
|
}
|
|
ptr++;
|
|
}
|
|
while (ptr < buffer->textSize) {
|
|
char c = getChar (buffer, ptr);
|
|
if (c == '\n') {
|
|
return ptr;
|
|
}
|
|
if (isword (c)) {
|
|
break;
|
|
}
|
|
ptr++;
|
|
}
|
|
}
|
|
return ptr;
|
|
}
|
|
|
|
static always_inline unsigned
|
|
prevWord (txtbuffer_t *buffer, unsigned ptr)
|
|
{
|
|
if (ptr > 0) {
|
|
while (ptr > 0) {
|
|
char c = getChar (buffer, ptr - 1);
|
|
if (c == '\n') {
|
|
return ptr;
|
|
}
|
|
if (isword (c)) {
|
|
break;
|
|
}
|
|
ptr--;
|
|
}
|
|
while (ptr > 0) {
|
|
char c = getChar (buffer, ptr - 1);
|
|
if (c == '\n') {
|
|
return ptr;
|
|
}
|
|
if (!isword (c)) {
|
|
break;
|
|
}
|
|
ptr--;
|
|
}
|
|
}
|
|
return ptr;
|
|
}
|
|
|
|
static always_inline unsigned __attribute__((pure))
|
|
nextLine (txtbuffer_t *buffer, unsigned ptr)
|
|
{
|
|
unsigned oldptr = ptr;
|
|
while (ptr < buffer->textSize && getChar (buffer, ptr++) != '\n') {
|
|
}
|
|
if (ptr == buffer->textSize && ptr > 0
|
|
&& getChar (buffer, ptr - 1) != '\n') {
|
|
return oldptr;
|
|
}
|
|
return ptr;
|
|
}
|
|
|
|
static always_inline unsigned
|
|
prevLine (txtbuffer_t *buffer, unsigned ptr)
|
|
{
|
|
if (ptr) {
|
|
ptr--;
|
|
while (ptr > 0 && getChar (buffer, ptr - 1) != '\n') {
|
|
ptr--;
|
|
}
|
|
}
|
|
return ptr;
|
|
}
|
|
|
|
static always_inline unsigned
|
|
charPos (txtbuffer_t *buffer, unsigned ptr, unsigned target, int tabSize)
|
|
{
|
|
unsigned pos = 0;
|
|
|
|
while (ptr < target) {
|
|
if (getChar (buffer, ptr) == '\t') {
|
|
pos += tabSize - (pos % tabSize) - 1; // -1 for ++
|
|
}
|
|
pos++;
|
|
ptr++;
|
|
}
|
|
return pos;
|
|
}
|
|
|
|
static always_inline unsigned __attribute__((pure))
|
|
charPtr (txtbuffer_t *buffer, unsigned ptr, unsigned target, int tabSize)
|
|
{
|
|
unsigned pos = 0;
|
|
while (pos < target && ptr < buffer->textSize
|
|
&& getChar (buffer, ptr) != '\n') {
|
|
if (getChar (buffer, ptr) == '\t') {
|
|
pos += tabSize - (pos % tabSize) - 1; // -1 for ++
|
|
}
|
|
pos++;
|
|
ptr++;
|
|
}
|
|
if (pos > target) {
|
|
ptr--;
|
|
}
|
|
return ptr;
|
|
}
|
|
|
|
static always_inline unsigned __attribute__((pure))
|
|
getEOW (txtbuffer_t *buffer, unsigned ptr)
|
|
{
|
|
while (ptr < buffer->textSize) {
|
|
char c = getChar (buffer, ptr);
|
|
if (!isword (c)) {
|
|
break;
|
|
}
|
|
ptr++;
|
|
}
|
|
return ptr;
|
|
}
|
|
|
|
static always_inline unsigned
|
|
getBOW (txtbuffer_t *buffer, unsigned ptr)
|
|
{
|
|
while (ptr > 0) {
|
|
char c = getChar (buffer, ptr - 1);
|
|
if (!isword (c)) {
|
|
break;
|
|
}
|
|
ptr--;
|
|
}
|
|
return ptr;
|
|
}
|
|
|
|
static always_inline unsigned __attribute__((pure))
|
|
getEOL (txtbuffer_t *buffer, unsigned ptr)
|
|
{
|
|
while (ptr < buffer->textSize) {
|
|
char c = getChar (buffer, ptr);
|
|
if (c == '\n') {
|
|
break;
|
|
}
|
|
ptr++;
|
|
}
|
|
return ptr;
|
|
}
|
|
|
|
static always_inline void
|
|
readString (txtbuffer_t *buffer, eb_sel_t *sel, char *str)
|
|
{
|
|
unsigned start = sel->start;
|
|
unsigned length = sel->length;
|
|
unsigned end = sel->start + sel->length;
|
|
const char *ptr = buffer->text + spanGap (buffer, start);
|
|
if ((start < buffer->gapOffset && end <= buffer->gapOffset)
|
|
|| start > buffer->gapOffset) {
|
|
memcpy (str, ptr, length);
|
|
} else {
|
|
length = buffer->gapOffset - start;
|
|
memcpy (str, ptr, length);
|
|
str += length;
|
|
ptr += length + buffer->gapOffset;
|
|
memcpy (str, ptr, sel->length - length);
|
|
}
|
|
}
|
|
|
|
static always_inline unsigned
|
|
getBOL (txtbuffer_t *buffer, unsigned ptr)
|
|
{
|
|
while (ptr > 0) {
|
|
char c = getChar (buffer, ptr - 1);
|
|
if (c == '\n') {
|
|
break;
|
|
}
|
|
ptr--;
|
|
}
|
|
return ptr;
|
|
}
|
|
|
|
static always_inline unsigned
|
|
_countLines (txtbuffer_t *buffer, unsigned start, unsigned len)
|
|
{
|
|
unsigned count = 0;
|
|
char *ptr = buffer->text + start;
|
|
|
|
while (len-- > 0) {
|
|
count += *ptr++ == '\n';
|
|
}
|
|
return count;
|
|
}
|
|
|
|
static always_inline unsigned
|
|
countLines (txtbuffer_t *buffer, unsigned start, unsigned len)
|
|
{
|
|
if (start < buffer->gapOffset) {
|
|
if (start + len <= buffer->gapOffset) {
|
|
return _countLines (buffer, start, len);
|
|
} else {
|
|
return _countLines (buffer, start, buffer->gapOffset - start)
|
|
+ _countLines (buffer, buffer->gapOffset + buffer->gapSize,
|
|
len - (buffer->gapOffset - start));
|
|
}
|
|
} else {
|
|
return _countLines (buffer, start + buffer->gapOffset, len);
|
|
}
|
|
}
|
|
|
|
static const char *
|
|
_search (txtbuffer_t *buffer, const char *ptr, unsigned len,
|
|
const char *str, unsigned slen, int dir,
|
|
int (*cmp)(const char *, const char *, size_t))
|
|
{
|
|
while (len-- > 0) {
|
|
if (*ptr == *str) {
|
|
unsigned offset = ptr - buffer->text;
|
|
if (offset > buffer->gapOffset
|
|
|| offset + slen < buffer->gapOffset) {
|
|
// search does not span gap
|
|
if (cmp (ptr, str, slen) == 0) {
|
|
return ptr;
|
|
}
|
|
} else {
|
|
// search spans gap
|
|
unsigned l = buffer->gapOffset - offset;
|
|
if (cmp (ptr, str, l) == 0
|
|
&& cmp (ptr + l + buffer->gapSize,
|
|
str + l, slen - l) == 0) {
|
|
return ptr;
|
|
}
|
|
}
|
|
} else {
|
|
ptr += dir;
|
|
}
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
static int
|
|
search (txtbuffer_t *buffer, const eb_sel_t *sel, const char *str, int dir,
|
|
eb_sel_t *find, int (*cmp)(const char *, const char *, size_t))
|
|
{
|
|
unsigned start = sel->start;
|
|
unsigned slen = strlen (str);
|
|
unsigned end = start + sel->length - slen;
|
|
const char *found = 0;
|
|
|
|
find->start = 0;
|
|
find->length = 0;
|
|
|
|
if (sel->length >= slen) {
|
|
if (dir < 0) {
|
|
const char *s = buffer->text + spanGap (buffer, end);
|
|
if ((start < buffer->gapOffset && end <= buffer->gapOffset)
|
|
|| start > buffer->gapOffset) {
|
|
found = _search (buffer, s, end - start, str, slen, -1, cmp);
|
|
} else {
|
|
unsigned l = end - (buffer->gapOffset + buffer->gapSize);
|
|
found = _search (buffer, s, l, str, slen, 1, cmp);
|
|
if (!found) {
|
|
s -= l + buffer->gapSize;
|
|
l = buffer->gapOffset - start;
|
|
found = _search (buffer, s, l, str, slen, 1, cmp);
|
|
}
|
|
}
|
|
} else {
|
|
const char *s = buffer->text + spanGap (buffer, start);
|
|
if ((start < buffer->gapOffset && end <= buffer->gapOffset)
|
|
|| start > buffer->gapOffset) {
|
|
found = _search (buffer, s, end - start, str, slen, 1, cmp);
|
|
} else {
|
|
unsigned l = buffer->gapOffset - start;
|
|
found = _search (buffer, s, l, str, slen, 1, cmp);
|
|
if (!found) {
|
|
s += l + buffer->gapSize;
|
|
l = end - start - l;
|
|
found = _search (buffer, s, l, str, slen, 1, cmp);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
if (found) {
|
|
unsigned offset = found - buffer->text;
|
|
if (offset > buffer->gapOffset) {
|
|
offset -= buffer->gapSize;
|
|
}
|
|
find->start = offset;
|
|
find->length = slen;
|
|
return 1;
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
static int
|
|
saveFile (txtbuffer_t *buffer, const char *filename)
|
|
{
|
|
QFile *file = Qopen (filename, "wt");
|
|
|
|
if (file) {
|
|
unsigned offset = buffer->gapOffset + buffer->gapSize;
|
|
Qwrite (file, buffer->text, buffer->gapOffset);
|
|
Qwrite (file, buffer->text + offset, buffer->textSize - offset);
|
|
Qclose (file);
|
|
return 1;
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
static int
|
|
loadFile (txtbuffer_t *buffer, const char *filename)
|
|
{
|
|
QFile *file = Qopen (filename, "rtz");
|
|
char *dst;
|
|
unsigned len;
|
|
|
|
if (file) {
|
|
len = Qfilesize (file);
|
|
// ensure buffer is empty
|
|
TextBuffer_DeleteAt (buffer, 0, buffer->textSize);
|
|
dst = TextBuffer_OpenGap (buffer, 0, len);
|
|
Qread (file, dst, len);
|
|
|
|
buffer->textSize += len;
|
|
buffer->gapOffset += len;
|
|
buffer->gapSize -= len;
|
|
|
|
Qclose (file);
|
|
return 1;
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
static unsigned
|
|
formatLine (txtbuffer_t *buffer, unsigned linePtr, unsigned xpos,
|
|
int *dst, unsigned length, eb_sel_t *selection, eb_color_t *colors,
|
|
int tabSize)
|
|
{
|
|
unsigned pos = 0;
|
|
unsigned ptr = linePtr;
|
|
unsigned sels = selection->start;
|
|
unsigned sele = selection->start + selection->length;
|
|
int coln = (colors->normal & ~0xff);
|
|
int cols = (colors->selected & ~0xff);
|
|
int col;
|
|
byte c = 0;
|
|
int count;
|
|
int *startdst = dst;
|
|
int startlen = length;
|
|
|
|
while (pos < xpos && ptr < buffer->textSize) {
|
|
c = getChar (buffer, ptr);
|
|
if (c == '\n') {
|
|
break;
|
|
}
|
|
if (c == '\t') {
|
|
pos += tabSize - (pos % tabSize) - 1; // -1 for ++
|
|
}
|
|
pos++;
|
|
ptr++;
|
|
}
|
|
col = ptr >= sels && ptr < sele ? cols : coln;
|
|
while (xpos < pos && length > 0) {
|
|
*dst++ = col | ' ';
|
|
length--;
|
|
xpos++;
|
|
}
|
|
while (length > 0 && ptr < buffer->textSize) {
|
|
col = ptr >= sels && ptr < sele ? cols : coln;
|
|
c = getChar (buffer, ptr++);
|
|
if (c == '\n') {
|
|
break;
|
|
}
|
|
count = 1;
|
|
if (c == '\t') {
|
|
c = ' ';
|
|
count = tabSize - (pos % tabSize);
|
|
}
|
|
while (length > 0 && count-- > 0) {
|
|
*dst++ = col | c;
|
|
pos++;
|
|
length--;
|
|
}
|
|
}
|
|
while (c != '\n' && ptr < buffer->textSize) {
|
|
c = getChar (buffer, ptr++);
|
|
}
|
|
while (length-- > 0) {
|
|
*dst++ = col | ' ';
|
|
}
|
|
if (dst - startdst > startlen) {
|
|
Sys_Error ("formatLine wrote too much: %zd %u %d",
|
|
dst - startdst, startlen, length);
|
|
}
|
|
return ptr;
|
|
}
|
|
|
|
//===
|
|
|
|
static void
|
|
bi__i_EditBuffer__init (progs_t *pr, void *_res)
|
|
{
|
|
qwaq_ebresources_t *res = _res;
|
|
__auto_type self = &P_STRUCT (pr, qwaq_editbuffer_t, 0);
|
|
// [super init];
|
|
RUA_obj_increment_retaincount (pr, pr->pr_objective_resources);
|
|
txtbuffer_t *txtbuffer = TextBuffer_Create ();
|
|
editbuffer_t *buffer = editbuffer_new (res);
|
|
|
|
buffer->txtbuffer = txtbuffer;
|
|
buffer->tabSize = 4; // FIXME
|
|
|
|
self->buffer = editbuffer_index (res, buffer);
|
|
R_INT (pr) = PR_SetPointer (pr, self);
|
|
}
|
|
|
|
static void
|
|
bi__i_EditBuffer__initWithFile_ (progs_t *pr, void *_res)
|
|
{
|
|
qwaq_ebresources_t *res = _res;
|
|
__auto_type self = &P_STRUCT (pr, qwaq_editbuffer_t, 0);
|
|
// [super init];
|
|
RUA_obj_increment_retaincount (pr, pr->pr_objective_resources);
|
|
const char *filename = P_GSTRING (pr, 2);
|
|
txtbuffer_t *txtbuffer = TextBuffer_Create ();
|
|
editbuffer_t *buffer = editbuffer_new (res);
|
|
|
|
buffer->txtbuffer = txtbuffer;
|
|
buffer->tabSize = 4; // FIXME
|
|
|
|
loadFile (buffer->txtbuffer, filename);
|
|
|
|
self->buffer = editbuffer_index (res, buffer);
|
|
R_INT (pr) = PR_SetPointer (pr, self);
|
|
}
|
|
|
|
static void
|
|
bi__i_EditBuffer__dealloc (progs_t *pr, void *_res)
|
|
{
|
|
qwaq_ebresources_t *res = _res;
|
|
__auto_type self = &P_STRUCT (pr, qwaq_editbuffer_t, 0);
|
|
int buffer_id = self->buffer;
|
|
editbuffer_t *buffer = get_editbuffer (res, __FUNCTION__, buffer_id);
|
|
|
|
TextBuffer_Destroy (buffer->txtbuffer);
|
|
editbuffer_free (res, buffer);
|
|
}
|
|
|
|
static void
|
|
bi__i_EditBuffer__nextChar_ (progs_t *pr, void *_res)
|
|
{
|
|
qwaq_ebresources_t *res = _res;
|
|
int buffer_id = P_STRUCT (pr, qwaq_editbuffer_t, 0).buffer;
|
|
editbuffer_t *buffer = get_editbuffer (res, __FUNCTION__, buffer_id);
|
|
unsigned ptr = P_UINT (pr, 2);
|
|
|
|
R_INT (pr) = nextChar (buffer->txtbuffer, ptr);
|
|
}
|
|
|
|
static void
|
|
bi__i_EditBuffer__prevChar_ (progs_t *pr, void *_res)
|
|
{
|
|
qwaq_ebresources_t *res = _res;
|
|
int buffer_id = P_STRUCT (pr, qwaq_editbuffer_t, 0).buffer;
|
|
editbuffer_t *buffer = get_editbuffer (res, __FUNCTION__, buffer_id);
|
|
unsigned ptr = P_UINT (pr, 2);
|
|
|
|
R_INT (pr) = prevChar (buffer->txtbuffer, ptr);
|
|
}
|
|
|
|
static void
|
|
bi__i_EditBuffer__nextNonSpace_ (progs_t *pr, void *_res)
|
|
{
|
|
qwaq_ebresources_t *res = _res;
|
|
int buffer_id = P_STRUCT (pr, qwaq_editbuffer_t, 0).buffer;
|
|
editbuffer_t *buffer = get_editbuffer (res, __FUNCTION__, buffer_id);
|
|
unsigned ptr = P_UINT (pr, 2);
|
|
|
|
R_INT (pr) = nextNonSpace (buffer->txtbuffer, ptr);
|
|
}
|
|
|
|
static void
|
|
bi__i_EditBuffer__prevNonSpace_ (progs_t *pr, void *_res)
|
|
{
|
|
qwaq_ebresources_t *res = _res;
|
|
int buffer_id = P_STRUCT (pr, qwaq_editbuffer_t, 0).buffer;
|
|
editbuffer_t *buffer = get_editbuffer (res, __FUNCTION__, buffer_id);
|
|
unsigned ptr = P_UINT (pr, 2);
|
|
|
|
R_INT (pr) = prevNonSpace (buffer->txtbuffer, ptr);
|
|
}
|
|
|
|
static void
|
|
bi__i_EditBuffer__isWord_ (progs_t *pr, void *_res)
|
|
{
|
|
qwaq_ebresources_t *res = _res;
|
|
int buffer_id = P_STRUCT (pr, qwaq_editbuffer_t, 0).buffer;
|
|
editbuffer_t *buffer = get_editbuffer (res, __FUNCTION__, buffer_id);
|
|
unsigned ptr = P_UINT (pr, 2);
|
|
|
|
R_INT (pr) = isword (getChar (buffer->txtbuffer, ptr));
|
|
}
|
|
|
|
static void
|
|
bi__i_EditBuffer__nextWord_ (progs_t *pr, void *_res)
|
|
{
|
|
qwaq_ebresources_t *res = _res;
|
|
int buffer_id = P_STRUCT (pr, qwaq_editbuffer_t, 0).buffer;
|
|
editbuffer_t *buffer = get_editbuffer (res, __FUNCTION__, buffer_id);
|
|
unsigned ptr = P_UINT (pr, 2);
|
|
|
|
R_INT (pr) = nextWord (buffer->txtbuffer, ptr);
|
|
}
|
|
|
|
static void
|
|
bi__i_EditBuffer__prevWord_ (progs_t *pr, void *_res)
|
|
{
|
|
qwaq_ebresources_t *res = _res;
|
|
int buffer_id = P_STRUCT (pr, qwaq_editbuffer_t, 0).buffer;
|
|
editbuffer_t *buffer = get_editbuffer (res, __FUNCTION__, buffer_id);
|
|
unsigned ptr = P_UINT (pr, 2);
|
|
|
|
R_INT (pr) = prevWord (buffer->txtbuffer, ptr);
|
|
}
|
|
|
|
static void
|
|
bi__i_EditBuffer__nextLine_ (progs_t *pr, void *_res)
|
|
{
|
|
qwaq_ebresources_t *res = _res;
|
|
int buffer_id = P_STRUCT (pr, qwaq_editbuffer_t, 0).buffer;
|
|
editbuffer_t *buffer = get_editbuffer (res, __FUNCTION__, buffer_id);
|
|
unsigned ptr = P_UINT (pr, 2);
|
|
|
|
R_INT (pr) = nextLine (buffer->txtbuffer, ptr);
|
|
}
|
|
|
|
static void
|
|
bi__i_EditBuffer__prevLine_ (progs_t *pr, void *_res)
|
|
{
|
|
qwaq_ebresources_t *res = _res;
|
|
int buffer_id = P_STRUCT (pr, qwaq_editbuffer_t, 0).buffer;
|
|
editbuffer_t *buffer = get_editbuffer (res, __FUNCTION__, buffer_id);
|
|
unsigned ptr = P_UINT (pr, 2);
|
|
|
|
R_INT (pr) = prevLine (buffer->txtbuffer, ptr);
|
|
}
|
|
|
|
static void
|
|
bi__i_EditBuffer__nextLine__ (progs_t *pr, void *_res)
|
|
{
|
|
qwaq_ebresources_t *res = _res;
|
|
int buffer_id = P_STRUCT (pr, qwaq_editbuffer_t, 0).buffer;
|
|
editbuffer_t *buffer = get_editbuffer (res, __FUNCTION__, buffer_id);
|
|
unsigned ptr = P_UINT (pr, 2);
|
|
unsigned count = P_UINT (pr, 3);
|
|
|
|
while (count-- > 0) {
|
|
unsigned oldptr = ptr;
|
|
ptr = nextLine (buffer->txtbuffer, ptr);
|
|
if (ptr == buffer->txtbuffer->textSize && ptr > 0
|
|
&& getChar (buffer->txtbuffer, ptr - 1) != '\n') {
|
|
ptr = oldptr;
|
|
break;
|
|
}
|
|
}
|
|
R_INT (pr) = ptr;
|
|
}
|
|
|
|
static void
|
|
bi__i_EditBuffer__prevLine__ (progs_t *pr, void *_res)
|
|
{
|
|
qwaq_ebresources_t *res = _res;
|
|
int buffer_id = P_STRUCT (pr, qwaq_editbuffer_t, 0).buffer;
|
|
editbuffer_t *buffer = get_editbuffer (res, __FUNCTION__, buffer_id);
|
|
unsigned ptr = P_UINT (pr, 2);
|
|
unsigned count = P_UINT (pr, 3);
|
|
|
|
while (ptr && count-- > 0) {
|
|
ptr = prevLine (buffer->txtbuffer, ptr);
|
|
}
|
|
R_INT (pr) = ptr;
|
|
}
|
|
|
|
static void
|
|
bi__i_EditBuffer__charPos_at_ (progs_t *pr, void *_res)
|
|
{
|
|
qwaq_ebresources_t *res = _res;
|
|
int buffer_id = P_STRUCT (pr, qwaq_editbuffer_t, 0).buffer;
|
|
editbuffer_t *buffer = get_editbuffer (res, __FUNCTION__, buffer_id);
|
|
unsigned ptr = P_UINT (pr, 2);
|
|
unsigned target = P_UINT (pr, 3);
|
|
int tabSize = buffer->tabSize;
|
|
|
|
R_INT (pr) = charPos (buffer->txtbuffer, ptr, target, tabSize);
|
|
}
|
|
|
|
static void
|
|
bi__i_EditBuffer__charPtr_at_ (progs_t *pr, void *_res)
|
|
{
|
|
qwaq_ebresources_t *res = _res;
|
|
int buffer_id = P_STRUCT (pr, qwaq_editbuffer_t, 0).buffer;
|
|
editbuffer_t *buffer = get_editbuffer (res, __FUNCTION__, buffer_id);
|
|
unsigned ptr = P_UINT (pr, 2);
|
|
unsigned target = P_UINT (pr, 3);
|
|
int tabSize = buffer->tabSize;
|
|
|
|
R_INT (pr) = charPtr (buffer->txtbuffer, ptr, target, tabSize);
|
|
}
|
|
|
|
static void
|
|
bi__i_EditBuffer__getWord_ (progs_t *pr, void *_res)
|
|
{
|
|
qwaq_ebresources_t *res = _res;
|
|
int buffer_id = P_STRUCT (pr, qwaq_editbuffer_t, 0).buffer;
|
|
editbuffer_t *buffer = get_editbuffer (res, __FUNCTION__, buffer_id);
|
|
unsigned ptr = P_UINT (pr, 2);
|
|
unsigned s = getBOW (buffer->txtbuffer, ptr);
|
|
unsigned e = getEOW (buffer->txtbuffer, ptr);
|
|
|
|
R_PACKED (pr, eb_sel_t).start = s;
|
|
R_PACKED (pr, eb_sel_t).length = e - s;
|
|
}
|
|
|
|
static void
|
|
bi__i_EditBuffer__getLine_ (progs_t *pr, void *_res)
|
|
{
|
|
qwaq_ebresources_t *res = _res;
|
|
int buffer_id = P_STRUCT (pr, qwaq_editbuffer_t, 0).buffer;
|
|
editbuffer_t *buffer = get_editbuffer (res, __FUNCTION__, buffer_id);
|
|
unsigned ptr = P_UINT (pr, 2);
|
|
unsigned s = getBOL (buffer->txtbuffer, ptr);
|
|
unsigned e = getEOL (buffer->txtbuffer, ptr);
|
|
|
|
R_PACKED (pr, eb_sel_t).start = s;
|
|
R_PACKED (pr, eb_sel_t).length = e - s;
|
|
}
|
|
|
|
static void
|
|
bi__i_EditBuffer__getBOL_ (progs_t *pr, void *_res)
|
|
{
|
|
qwaq_ebresources_t *res = _res;
|
|
int buffer_id = P_STRUCT (pr, qwaq_editbuffer_t, 0).buffer;
|
|
editbuffer_t *buffer = get_editbuffer (res, __FUNCTION__, buffer_id);
|
|
unsigned ptr = P_UINT (pr, 2);
|
|
|
|
R_INT (pr) = getBOL (buffer->txtbuffer, ptr);
|
|
}
|
|
|
|
static void
|
|
bi__i_EditBuffer__getEOL_ (progs_t *pr, void *_res)
|
|
{
|
|
qwaq_ebresources_t *res = _res;
|
|
int buffer_id = P_STRUCT (pr, qwaq_editbuffer_t, 0).buffer;
|
|
editbuffer_t *buffer = get_editbuffer (res, __FUNCTION__, buffer_id);
|
|
unsigned ptr = P_UINT (pr, 2);
|
|
|
|
R_INT (pr) = getEOL (buffer->txtbuffer, ptr);
|
|
}
|
|
|
|
static void
|
|
bi__i_EditBuffer__getBOT (progs_t *pr, void *_res)
|
|
{
|
|
//qwaq_ebresources_t *res = _res;
|
|
//int buffer_id = P_STRUCT (pr, qwaq_editbuffer_t, 0).buffer;
|
|
//editbuffer_t *buffer = get_editbuffer (res, __FUNCTION__, buffer_id);
|
|
|
|
R_INT (pr) = 0;
|
|
}
|
|
|
|
static void
|
|
bi__i_EditBuffer__getEOT (progs_t *pr, void *_res)
|
|
{
|
|
qwaq_ebresources_t *res = _res;
|
|
int buffer_id = P_STRUCT (pr, qwaq_editbuffer_t, 0).buffer;
|
|
editbuffer_t *buffer = get_editbuffer (res, __FUNCTION__, buffer_id);
|
|
|
|
R_INT (pr) = buffer->txtbuffer->textSize;
|
|
}
|
|
|
|
static void
|
|
bi__i_EditBuffer__readString_ (progs_t *pr, void *_res)
|
|
{
|
|
qwaq_ebresources_t *res = _res;
|
|
int buffer_id = P_STRUCT (pr, qwaq_editbuffer_t, 0).buffer;
|
|
editbuffer_t *buffer = get_editbuffer (res, __FUNCTION__, buffer_id);
|
|
__auto_type selection = &P_PACKED (pr, eb_sel_t, 2);
|
|
|
|
char *str = alloca (selection->length + 1);
|
|
str[selection->length] = 0;
|
|
readString (buffer->txtbuffer, selection, str);
|
|
|
|
RETURN_STRING (pr, str);
|
|
}
|
|
|
|
static void
|
|
bi__i_EditBuffer__getChar_ (progs_t *pr, void *_res)
|
|
{
|
|
qwaq_ebresources_t *res = _res;
|
|
int buffer_id = P_STRUCT (pr, qwaq_editbuffer_t, 0).buffer;
|
|
editbuffer_t *buffer = get_editbuffer (res, __FUNCTION__, buffer_id);
|
|
unsigned ptr = P_UINT (pr, 2);
|
|
|
|
if (ptr >= buffer->txtbuffer->textSize) {
|
|
PR_RunError (pr, "EditBuffer: character index out of bounds\n");
|
|
}
|
|
R_INT (pr) = (byte) getChar (buffer->txtbuffer, ptr);
|
|
}
|
|
|
|
static void
|
|
bi__i_EditBuffer__putChar_at_ (progs_t *pr, void *_res)
|
|
{
|
|
qwaq_ebresources_t *res = _res;
|
|
int buffer_id = P_STRUCT (pr, qwaq_editbuffer_t, 0).buffer;
|
|
editbuffer_t *buffer = get_editbuffer (res, __FUNCTION__, buffer_id);
|
|
int chr = P_UINT (pr, 2);
|
|
unsigned ptr = P_UINT (pr, 3);
|
|
|
|
if (ptr >= buffer->txtbuffer->textSize) {
|
|
PR_RunError (pr, "EditBuffer: character index out of bounds\n");
|
|
}
|
|
buffer->txtbuffer->text[spanGap (buffer->txtbuffer, ptr)] = chr;
|
|
}
|
|
|
|
static void
|
|
bi__i_EditBuffer__insertChar_at_ (progs_t *pr, void *_res)
|
|
{
|
|
qwaq_ebresources_t *res = _res;
|
|
int buffer_id = P_STRUCT (pr, qwaq_editbuffer_t, 0).buffer;
|
|
editbuffer_t *buffer = get_editbuffer (res, __FUNCTION__, buffer_id);
|
|
char chr = P_UINT (pr, 2);
|
|
unsigned ptr = P_UINT (pr, 3);
|
|
|
|
if (ptr > buffer->txtbuffer->textSize) {
|
|
PR_RunError (pr, "EditBuffer: character index out of bounds\n");
|
|
}
|
|
TextBuffer_InsertAt (buffer->txtbuffer, ptr, &chr, 1);
|
|
}
|
|
|
|
static void
|
|
bi__i_EditBuffer__countLines_ (progs_t *pr, void *_res)
|
|
{
|
|
qwaq_ebresources_t *res = _res;
|
|
int buffer_id = P_STRUCT (pr, qwaq_editbuffer_t, 0).buffer;
|
|
editbuffer_t *buffer = get_editbuffer (res, __FUNCTION__, buffer_id);
|
|
__auto_type selection = &P_PACKED (pr, eb_sel_t, 2);
|
|
|
|
R_INT (pr) = countLines (buffer->txtbuffer,
|
|
selection->start, selection->length);
|
|
}
|
|
|
|
static void
|
|
bi__i_EditBuffer__search_for_direction_ (progs_t *pr, void *_res)
|
|
{
|
|
qwaq_ebresources_t *res = _res;
|
|
int buffer_id = P_STRUCT (pr, qwaq_editbuffer_t, 0).buffer;
|
|
editbuffer_t *buffer = get_editbuffer (res, __FUNCTION__, buffer_id);
|
|
__auto_type selection = &P_PACKED (pr, eb_sel_t, 2);
|
|
const char *str = P_GSTRING (pr, 3);
|
|
int dir = P_INT (pr, 4);
|
|
|
|
search (buffer->txtbuffer, selection, str, dir, &R_PACKED (pr, eb_sel_t),
|
|
strncmp);
|
|
}
|
|
|
|
static void
|
|
bi__i_EditBuffer__isearch_for_direction_ (progs_t *pr, void *_res)
|
|
{
|
|
qwaq_ebresources_t *res = _res;
|
|
int buffer_id = P_STRUCT (pr, qwaq_editbuffer_t, 0).buffer;
|
|
editbuffer_t *buffer = get_editbuffer (res, __FUNCTION__, buffer_id);
|
|
__auto_type selection = &P_PACKED (pr, eb_sel_t, 2);
|
|
const char *str = P_GSTRING (pr, 3);
|
|
int dir = P_INT (pr, 4);
|
|
|
|
search (buffer->txtbuffer, selection, str, dir, &R_PACKED (pr, eb_sel_t),
|
|
strncasecmp);
|
|
}
|
|
|
|
static void
|
|
bi__i_EditBuffer__formatLine_from_into_width_highlight_colors_ (progs_t *pr, void *_res)
|
|
{
|
|
qwaq_ebresources_t *res = _res;
|
|
int buffer_id = P_STRUCT (pr, qwaq_editbuffer_t, 0).buffer;
|
|
editbuffer_t *buffer = get_editbuffer (res, __FUNCTION__, buffer_id);
|
|
unsigned linePtr = P_UINT (pr, 2);
|
|
unsigned xpos = P_UINT (pr, 3);
|
|
int *dst = (int *) P_GPOINTER (pr, 4);
|
|
unsigned length = P_UINT (pr, 5);
|
|
__auto_type selection = &P_PACKED (pr, eb_sel_t, 6);
|
|
__auto_type colors = &P_PACKED (pr, eb_color_t, 7);
|
|
int tabSize = buffer->tabSize;
|
|
unsigned ptr;
|
|
|
|
ptr = formatLine (buffer->txtbuffer, linePtr, xpos, dst, length, selection,
|
|
colors, tabSize);
|
|
R_INT (pr) = ptr;
|
|
}
|
|
|
|
static void
|
|
bi__i_EditBuffer__modified (progs_t *pr, void *_res)
|
|
{
|
|
qwaq_ebresources_t *res = _res;
|
|
int buffer_id = P_STRUCT (pr, qwaq_editbuffer_t, 0).buffer;
|
|
editbuffer_t *buffer = get_editbuffer (res, __FUNCTION__, buffer_id);
|
|
R_INT (pr) = buffer->modified;
|
|
}
|
|
|
|
static void
|
|
bi__i_EditBuffer__textSize (progs_t *pr, void *_res)
|
|
{
|
|
qwaq_ebresources_t *res = _res;
|
|
int buffer_id = P_STRUCT (pr, qwaq_editbuffer_t, 0).buffer;
|
|
editbuffer_t *buffer = get_editbuffer (res, __FUNCTION__, buffer_id);
|
|
R_INT (pr) = buffer->txtbuffer->textSize;
|
|
}
|
|
|
|
static void
|
|
bi__i_EditBuffer__saveFile_ (progs_t *pr, void *_res)
|
|
{
|
|
qwaq_ebresources_t *res = _res;
|
|
int buffer_id = P_STRUCT (pr, qwaq_editbuffer_t, 0).buffer;
|
|
editbuffer_t *buffer = get_editbuffer (res, __FUNCTION__, buffer_id);
|
|
const char *filename = P_GSTRING (pr, 2);
|
|
|
|
if (saveFile (buffer->txtbuffer, filename)) {
|
|
buffer->modified = 0;
|
|
}
|
|
}
|
|
|
|
static void
|
|
qwaq_ebresources_clear (progs_t *pr, void *_res)
|
|
{
|
|
__auto_type res = (qwaq_ebresources_t *) _res;
|
|
|
|
for (editbuffer_t *buffer = res->buffer_list; buffer;
|
|
buffer = buffer->next) {
|
|
TextBuffer_Destroy (buffer->txtbuffer);
|
|
buffer->txtbuffer = 0;
|
|
}
|
|
editbuffer_reset (res);
|
|
}
|
|
|
|
static void
|
|
qwaq_ebresources_destroy (progs_t *pr, void *_res)
|
|
{
|
|
free (_res);
|
|
}
|
|
|
|
#define bi(x,n,np,params...) {#x, bi_##x, n, np, {params}}
|
|
#define p(type) PR_PARAM(type)
|
|
#define P(a, s) { .size = (s), .alignment = BITOP_LOG2 (a), }
|
|
static builtin_t builtins[] = {
|
|
bi(_i_EditBuffer__init, -1, 2, p(ptr), p(ptr)),
|
|
bi(_i_EditBuffer__initWithFile_, -1, 3, p(ptr), p(ptr), p(string)),
|
|
bi(_i_EditBuffer__dealloc, -1, 2, p(ptr), p(ptr)),
|
|
bi(_i_EditBuffer__nextChar_, -1, 3, p(ptr), p(ptr), p(uint)),
|
|
bi(_i_EditBuffer__prevChar_, -1, 3, p(ptr), p(ptr), p(uint)),
|
|
bi(_i_EditBuffer__nextNonSpace_, -1, 3, p(ptr), p(ptr), p(uint)),
|
|
bi(_i_EditBuffer__prevNonSpace_, -1, 3, p(ptr), p(ptr), p(uint)),
|
|
bi(_i_EditBuffer__isWord_, -1, 3, p(ptr), p(ptr), p(uint)),
|
|
bi(_i_EditBuffer__nextWord_, -1, 3, p(ptr), p(ptr), p(uint)),
|
|
bi(_i_EditBuffer__prevWord_, -1, 3, p(ptr), p(ptr), p(uint)),
|
|
bi(_i_EditBuffer__nextLine_, -1, 3, p(ptr), p(ptr), p(uint)),
|
|
bi(_i_EditBuffer__prevLine_, -1, 3, p(ptr), p(ptr), p(uint)),
|
|
bi(_i_EditBuffer__nextLine__, -1, 4, p(ptr), p(ptr), p(uint), p(uint)),
|
|
bi(_i_EditBuffer__prevLine__, -1, 4, p(ptr), p(ptr), p(uint), p(uint)),
|
|
bi(_i_EditBuffer__charPos_at_, -1, 4, p(ptr), p(ptr), p(uint), p(uint)),
|
|
bi(_i_EditBuffer__charPtr_at_, -1, 4, p(ptr), p(ptr), p(uint), p(uint)),
|
|
bi(_i_EditBuffer__getWord_, -1, 3, p(ptr), p(ptr), p(uint)),
|
|
bi(_i_EditBuffer__getLine_, -1, 3, p(ptr), p(ptr), p(uint)),
|
|
bi(_i_EditBuffer__getBOL_, -1, 3, p(ptr), p(ptr), p(uint)),
|
|
bi(_i_EditBuffer__getEOL_, -1, 3, p(ptr), p(ptr), p(uint)),
|
|
bi(_i_EditBuffer__getBOT, -1, 2, p(ptr), p(ptr)),
|
|
bi(_i_EditBuffer__getEOT, -1, 2, p(ptr), p(ptr)),
|
|
bi(_i_EditBuffer__readString_, -1, 3, p(ptr), p(ptr), P(1, 2)),
|
|
bi(_i_EditBuffer__getChar_, -1, 3, p(ptr), p(ptr), p(uint)),
|
|
bi(_i_EditBuffer__putChar_at_, -1, 4, p(ptr), p(ptr), p(int), p(uint)),
|
|
bi(_i_EditBuffer__insertChar_at_, -1, 4, p(ptr), p(ptr), p(int), p(uint)),
|
|
bi(_i_EditBuffer__countLines_, -1, 3, p(ptr), p(ptr), P(1, 2)),
|
|
bi(_i_EditBuffer__search_for_direction_, -1,
|
|
5, p(ptr), p(ptr), P(1, 2), p(string), p(int)),
|
|
bi(_i_EditBuffer__isearch_for_direction_,-1,
|
|
5, p(ptr), p(ptr), P(1, 2), p(string), p(int)),
|
|
bi(_i_EditBuffer__formatLine_from_into_width_highlight_colors_, -1,
|
|
8, p(ptr), p(ptr),
|
|
p(uint), p(uint), p(ptr), p(uint), P(1, 2), P(1, 2)),
|
|
bi(_i_EditBuffer__modified, -1, 2, p(ptr), p(ptr)),
|
|
bi(_i_EditBuffer__textSize, -1, 2, p(ptr), p(ptr)),
|
|
bi(_i_EditBuffer__saveFile_, -1, 3, p(ptr), p(ptr), p(string)),
|
|
{}
|
|
};
|
|
|
|
void
|
|
QWAQ_EditBuffer_Init (progs_t *pr)
|
|
{
|
|
qwaq_ebresources_t *res = calloc (sizeof (*res), 1);
|
|
res->pr = pr;
|
|
|
|
PR_Resources_Register (pr, "qwaq-editbuffer", res, qwaq_ebresources_clear,
|
|
qwaq_ebresources_destroy);
|
|
PR_RegisterBuiltins (pr, builtins, res);
|
|
}
|