diff options
| -rw-r--r-- | INSTALL | 9 | ||||
| -rw-r--r-- | README | 2 | ||||
| -rw-r--r-- | configure.ac | 41 | ||||
| m--------- | contrib/scinterm | 0 | ||||
| m--------- | contrib/scintilla | 0 | ||||
| -rw-r--r-- | doc/tutorial.ms.in | 4 | ||||
| -rw-r--r-- | fallback.teco_ini | bin | 2511 -> 2607 bytes | |||
| -rw-r--r-- | freebsd/pkg-plist | 1 | ||||
| -rw-r--r-- | lib/Makefile.am | 2 | ||||
| -rw-r--r-- | lib/fnkeys.tes | bin | 4391 -> 5102 bytes | |||
| -rw-r--r-- | lib/tank.tes | 211 | ||||
| -rw-r--r-- | m4/ax_with_ncurses.m4 | 4 | ||||
| -rw-r--r-- | src/cmdline.c | 8 | ||||
| -rw-r--r-- | src/core-commands.c | 37 | ||||
| -rw-r--r-- | src/interface-curses/curses-info-popup.c | 32 | ||||
| -rw-r--r-- | src/interface-curses/curses-info-popup.h | 2 | ||||
| -rw-r--r-- | src/interface-curses/curses-utils.c | 16 | ||||
| -rw-r--r-- | src/interface-curses/curses-utils.h | 9 | ||||
| -rw-r--r-- | src/interface-curses/interface.c | 183 | ||||
| -rw-r--r-- | src/interface-gtk/interface.c | 76 | ||||
| -rw-r--r-- | src/interface.h | 2 | ||||
| -rw-r--r-- | src/main.c | 7 | ||||
| -rw-r--r-- | src/qreg.c | 6 | ||||
| -rw-r--r-- | src/ring.c | 9 | ||||
| -rw-r--r-- | tests/testsuite.at | 13 | ||||
| -rwxr-xr-x | www/build.tes | 25 |
26 files changed, 521 insertions, 178 deletions
@@ -21,13 +21,14 @@ SciTECO Build and Runtime Dependencies * Glib 2 as a cross-platform runtime library (v2.44 or later): https://developer.gnome.org/glib/ * When choosing the Curses interface, you need one of: - * NCurses (http://www.gnu.org/software/ncurses/). - If you plan to use the ncurses MinGW port, - I recommend ncurses 6.0 or later. + * ncurses (http://www.gnu.org/software/ncurses/). + Should be built with wide-character support (--enable-widec). + If you plan to use the ncurses MinGW port, I recommend ncurses 6.0 or later. * NetBSD Curses (https://github.com/sabotage-linux/netbsd-curses). - This is the default on NetBSD. + This is the default on NetBSD and should always support wide characters. * PDCursesMod v4.5.4 or later (https://github.com/Bill-Gray/PDCursesMod.git). This is the recommended flavor of PDCurses to use. + It must be built with WIDE=Y and UTF8=Y. * PDCurses/EMCurses (https://github.com/rhaberkorn/emcurses). * PDCurses/XCurses (http://pdcurses.sourceforge.net/). Note that XCurses v3.4 appears to be broken, you may have to @@ -23,7 +23,7 @@ The Curses frontend is verified to work with [ncurses](https://www.gnu.org/softw [PDCurses/XCurses](https://github.com/wmcbrine/PDCurses), [PDCursesMod](https://github.com/Bill-Gray/PDCursesMod) and [EMCurses](https://github.com/rhaberkorn/emcurses). -All X/Open-compatible libraries should be supported. +All X/Open-compatible libraries with enhanced and wide-character support should be supported. SVr4 curses without enhanced definitions is **not** supported. Linux, FreeBSD, NetBSD, [Mac OS X](https://sciteco.fmsbw.de/knowledge/Mac%20OS%20Support), diff --git a/configure.ac b/configure.ac index ed5e056..673fb33 100644 --- a/configure.ac +++ b/configure.ac @@ -215,7 +215,7 @@ case $host in *-*-linux* | *-*-*bsd* | *-*-darwin* | *-*-cygwin* | *-*-haiku*) # NOTE: Keep this on a single line for compatibility # with ancient versions of Autoconf. - AC_CHECK_FUNCS([realpath readlink pathconf fchown dup dup2 getpid open read kill mmap popen pclose isatty fork setsid], , [ + AC_CHECK_FUNCS([realpath readlink pathconf fchown dup dup2 getpid open read kill mmap popen pclose isatty fork execv setsid], , [ AC_MSG_ERROR([Missing libc function]) ]) AC_SEARCH_LIBS(dladdr, [dl], , [ @@ -289,7 +289,8 @@ case $INTERFACE in case $INTERFACE in ncurses) # This gives precendence to the widechar version of ncurses, - # which is necessary for Unicode support even when not using widechar APIs. + # which is necessary for Unicode support and when using more than + # 256 color pairs. # However we also accept libncurses.so if it also contains the # enhanced definitions. # NOTE: This also defines CURSES_CFLAGS and CURSES_LIBS arguments, @@ -303,6 +304,9 @@ case $INTERFACE in CXXFLAGS="$CXXFLAGS $CURSES_CFLAGS" LIBS="$LIBS $CURSES_LIBS" + # Scinterm cares about the correct flags itself. + AC_DEFINE(NCURSES_WIDECHAR, 1, [Provide wide-character functions]) + AC_CHECK_FUNCS([tigetstr]) ;; @@ -312,10 +316,6 @@ case $INTERFACE in # installed, we want this to be an explicit setting. AC_DEFINE(NETBSD_CURSES, 1, [Building against netbsd-curses]) - CFLAGS="$CFLAGS $CURSES_CFLAGS" - CXXFLAGS="$CXXFLAGS $CURSES_CFLAGS" - LIBS="$LIBS $CURSES_LIBS" - if [[ "x$CURSES_LIBS" = "x" ]]; then # libncurses.pc is only shipped by Void Linux' fork, # not in NetBSD itself. @@ -325,9 +325,12 @@ case $INTERFACE in else AC_MSG_CHECKING([checking for netbsd-curses (CURSES_LIBS)]) AC_MSG_RESULT([$CURSES_LIBS]) - LIBS="$LIBS $CURSES_LIBS" fi + CFLAGS="$CFLAGS $CURSES_CFLAGS" + CXXFLAGS="$CXXFLAGS $CURSES_CFLAGS" + LIBS="$LIBS $CURSES_LIBS" + AC_CHECK_FUNCS([tigetstr]) ;; @@ -353,9 +356,11 @@ case $INTERFACE in LIBS="$LIBS $CURSES_LIBS" # It is crucial to define XCURSES before including curses.h. + # This is not important for Scinterm. AC_DEFINE(XCURSES, 1, [Enable PDCurses/XCurses extensions]) AC_CHECK_FUNC([has_mouse], [ + # not important to pass to Scinterm AC_DEFINE(PDC_NCMOUSE, 1, [PDCurses built with ncurses mouse API]) ]) @@ -363,9 +368,6 @@ case $INTERFACE in ;; pdcurses*) - CFLAGS="$CFLAGS $CURSES_CFLAGS" - CXXFLAGS="$CXXFLAGS $CURSES_CFLAGS" - if [[ "x$CURSES_LIBS" = "x" ]]; then AC_CHECK_LIB(pdcurses, PDC_get_version, , [ AC_MSG_ERROR([libpdcurses missing!]) @@ -373,21 +375,26 @@ case $INTERFACE in else AC_MSG_CHECKING([checking for PDCurses (CURSES_LIBS)]) AC_MSG_RESULT([$CURSES_LIBS]) - LIBS="$LIBS $CURSES_LIBS" fi # It is crucial to define PDC_WIDE before including curses.h. # FIXME: MinGW has a pdcurses.h that already defines all necessary macros, # but it's not in upstream PDCurses/PDCursesMod. - AC_CHECK_FUNC([add_wch], [ - AC_DEFINE(PDC_WIDE, 1, [PDCurses built with wide-character support]) - # FIXME: It would be better to check for PDC_FORCE_UTF8. - # Theoretically, we could check for endwin_u[32|64]_4302, - # but I'm not sure this will work reliably in the future. - AC_DEFINE(PDC_FORCE_UTF8, 1, [PDCursesMod forces use of UTF8]) + # FIXME: It would be better to check for PDC_FORCE_UTF8. + # Theoretically, we could check for endwin_u[32|64]_4302, + # but I'm not sure this will work reliably in the future. + CURSES_CFLAGS="$CURSES_CFLAGS -DPDC_WIDE -DPDC_FORCE_UTF8" + + CFLAGS="$CFLAGS $CURSES_CFLAGS" + CXXFLAGS="$CXXFLAGS $CURSES_CFLAGS" + LIBS="$LIBS $CURSES_LIBS" + + AC_CHECK_FUNC([add_wch], [], [ + AC_MSG_ERROR([libpdcurses does not include wide-character support!]) ]) AC_CHECK_FUNC([has_mouse], [ + # not important to pass to Scinterm AC_DEFINE(PDC_NCMOUSE, 1, [PDCurses built with ncurses mouse API]) ]) diff --git a/contrib/scinterm b/contrib/scinterm -Subproject 26f5d3f5290a2910e44dec16d05da2db33c1272 +Subproject 7f5b0e2bdfb23a2806ed0cf9c6d25c1f37c0392 diff --git a/contrib/scintilla b/contrib/scintilla -Subproject b64652b857d3a5922c72ccc801ac77aa9cff27c +Subproject 748f9cbc4f2a9d7a4216feaa37aee8e834123f0 diff --git a/doc/tutorial.ms.in b/doc/tutorial.ms.in index 92da0af..4016605 100644 --- a/doc/tutorial.ms.in +++ b/doc/tutorial.ms.in @@ -36,6 +36,10 @@ . ns .. . +.\" Work around problems with early device extension commands. +.\" See groff(7) and https://savannah.gnu.org/bugs/?67992 +\c +. .\" The entire document is monospaced as users are supposed to .\" navigate through it and perhaps even modify it inplace. .SCITECO_TT diff --git a/fallback.teco_ini b/fallback.teco_ini Binary files differindex 229bc77..e1e263b 100644 --- a/fallback.teco_ini +++ b/fallback.teco_ini diff --git a/freebsd/pkg-plist b/freebsd/pkg-plist index ab74145..dd3e7cb 100644 --- a/freebsd/pkg-plist +++ b/freebsd/pkg-plist @@ -13,6 +13,7 @@ share/man/man7/%%PROGRAM_PREFIX%%sciteco.7.gz %%DATADIR%%/lib/getopt.tes %%DATADIR%%/lib/lexer.tes %%DATADIR%%/lib/repl.tes +%%DATADIR%%/lib/tank.tes %%DATADIR%%/lib/tecat.tes %%LEXILLA%%%%DATADIR%%/lib/lexers/abaqus.tes %%LEXILLA%%%%DATADIR%%/lib/lexers/ada.tes diff --git a/lib/Makefile.am b/lib/Makefile.am index 0da616a..04e83ad 100644 --- a/lib/Makefile.am +++ b/lib/Makefile.am @@ -1,5 +1,5 @@ dist_scitecolib_DATA = color.tes lexer.tes session.tes opener.tes \ - fnkeys.tes string.tes getopt.tes + fnkeys.tes string.tes getopt.tes tank.tes # Standalone scripts. # These are not installed via _SCRIPTS as it would add the --program-prefix. diff --git a/lib/fnkeys.tes b/lib/fnkeys.tes Binary files differindex 937caf9..f58a26a 100644 --- a/lib/fnkeys.tes +++ b/lib/fnkeys.tes diff --git a/lib/tank.tes b/lib/tank.tes new file mode 100644 index 0000000..8573e22 --- /dev/null +++ b/lib/tank.tes @@ -0,0 +1,211 @@ +!* + * TANK MODE + * + * Drive an ASCII tank around your buffer. + * Invoke via EIQ[$SCITECOPATH]/tank.tes + *! +[tank.sprite.0] !* background *! + +!* FIXME: Perhaps encode the gun's top in the integer cell *! +[tank.sprite.1] +█▓█▓ +╠════○ +▓█▓█ +[tank.sprite.2] + █▓█▓ +○════╣ + ▓█▓█ +[tank.sprite.3] + ┌┐ +▓││█ +█││▓ +▓┴┴█ +[tank.sprite.4] +█┬┬▓ +▓││█ +█││▓ + └┘ + +!* + * f,tM[tank.rand] - LCG pseudo random number generator for values between f and t + * This is based on the version in the BSD libc. + *! +::U[tank.rand] +@[tank.rand]{ + U.tU.f + (1103515245*Q[tank.rand]+12345) & (2^*31-1)U[tank.rand] + (Q[tank.rand]^/(Q.t-Q.f+1)+Q.f) +} + +!* x,yM[tank.goto] -> success|failure *! +@[tank.goto]{ + U.yU.x + Q.x"< 0 ' Q.y"< 0 ' + J Q.y:L"F 0 ' + Q.x<0A"< I F> ' 0A-10"= I F> ' C> + -1 +} + +!* x,y,spriteM[tank.blit] *! +@[tank.blit]{ + U.sU.yU.x Q.xU.#x0 + Q.s"N + 10[tank.sprite.0] !* background *! + %[tank.blit] + ' + < + %.iQ[tank.sprite.\.s]U.c Q.c:; + Q[tank.blit]&1"= + !* toggle the chain character to simulate movements *! + Q.c-█"= ▓U.c | Q.c-▓"= █U.c ' ' + ' + + Q.c-10"= + %.y Q.#x0U.x + Q.s"N 10:[tank.sprite.0] ' + | + Q.x,Q.yM[tank.goto]; + Q.s"N !* not background sprite *! + Q.c- "= + !* space is "transparent" - copy background unmodified *! + 0A"< I | 0A-10"= I | C ' ' + -A:[tank.sprite.0] + %.x F< + ' + !* "destroy" fields we have touched *! + U.d + 0A"F 0A-10"N 0A- "N 33,126M[tank.rand]U.d ' ' ' + Q.d:[tank.sprite.0] + ' + 0A"F 0A-10"N D ' ' Q.cI + %.x + ' + > +} + +@[tank.explode]{ + !!.[explosion]*oO%. + .[explosion]✹✸✶✧· + :Q.[explosion]< + 0A"F 0A-10"N D ' ' + %.i-1Q.[explosion]IR + > +} + +0U[tank.score] + +@[tank.shoot]{ + U.#dr U.yU.x + + !* FIXME: Could also be encoded into a map *! + Q.#dr-1Oright,left,up,down + !right! + 6-1%.x %.y + 1U.#dx 0U.#dy + Oend + !left! + %.y + -U.#dx 0U.#dy + Oend + !up! + 2-1%.x + 0U.#dx -U.#dy + Oend + !down! + 2-1%.x 3%.y + 0U.#dx 1U.#dy + !* fall through *! + !end! + < + (Q.#dx%.x),(Q.#dy%.y)M[tank.goto]"F ' + 0A"< ' Q.#dy"= 0A-10"= ' ' + 0A- "N 0A-10"N 1; ' ' + 0A"F 0A-10"N D ' ' I● + -DI + > + M[tank.explode] + + %[tank.score] SCORE: \[tank.score] +} + +@[tank.selfdestruct]{ + U.yU.x Q.x+2U.#cx Q.y+2U.#cy + .[explosion]✹✹✸✸✶✶✧✧· + 10< + Q.#cx-10U.x 20< + Q.x-1"< %.x F> ' + Q.#cy-10U.y 20< + !* d = sqrt((x - cx)^2 + ((y - cy) * aspect)^2) *! + (Q.x-Q.#cx)^*2 + ((Q.y-Q.#cy)*2)^*2 U.#d2 + + Q.r^*2 - Q.#d2"< + Q.#d2 - (Q.r+2)^*2"< + !* on shock wave *! + Q.x,Q.yM[tank.goto]"F %.y F> ' + -D Q.rQ.[explosion]I + ' + | + !* within shock wave *! + Q.x,Q.yM[tank.goto]"F %.y F> ' + -D I + ' + %.y> + %.x> + 1 + %.r> + Q.#cx,Q.#cyM[tank.goto] +} + +ESGETCOLUMNU.x :-1U.y +Q.xU.#xo Q.yU.#yo + +!* + * Normalize identations to spaces -- assumed by M[goto] + * FIXME: Will not work well with character representations. + *! +ESGETTABDRAWMODE-2"N + J <S^I; + ESGETCOLUMN-(-1ESGETCOLUMN)U.w + -D Q.w<I > + > +' + +CONTROL: W A S D. SHOOT: SPACE. QUIT: ESCAPE. PRESS ANY KEY TO START. + +ESGETCARETSTYLEU.[caretstyle] +0ESSETCARETSTYLE + +1U.[sprite] +< + Q.#xo,Q.#yo,0M[tank.blit] !* redraw background *! + Q.xU.#xo Q.yU.#yo + + Q.x,Q.y,Q.[sprite]M[tank.blit] + 0 U.c + :Okey.U.c + F> + !key.w! + Q.y"> -%.y ' 3U.[sprite] + F> + !key.a! + Q.x,Q.y,0M[tank.blit] !* redraw background *! + Q.x"> -%.x ' 2U.[sprite] + F> + !key.s! + Q.x,Q.y,0M[tank.blit] !* redraw background *! + %.y 4U.[sprite] + F> + !key.d! + Q.x,Q.y,0M[tank.blit] !* redraw background *! + %.x 1U.[sprite] + F> + !key. ! + !* FIXME: this also scrolls due to *! + Q.x,Q.y,Q.[sprite]M[tank.shoot] + F> + !key.! + Q.x,Q.yM[tank.selfdestruct] + 1; +> + +Q.[caretstyle]ESSETCARETSTYLE diff --git a/m4/ax_with_ncurses.m4 b/m4/ax_with_ncurses.m4 index 4dc2f33..cdcfd04 100644 --- a/m4/ax_with_ncurses.m4 +++ b/m4/ax_with_ncurses.m4 @@ -5,8 +5,8 @@ # DESCRIPTION # # This macro checks for an ncurses library with enhanced definitions -# providing a curses.h either in the default search path or as -# established by pkg-config. +# (including wide-char support) providing a curses.h either in the default +# search path or as established by pkg-config. # # It is based on the AX_WITH_CURSES macro but does not attempt # to find any non-standard header, which would require #ifdefing diff --git a/src/cmdline.c b/src/cmdline.c index 9a7b356..b944b5e 100644 --- a/src/cmdline.c +++ b/src/cmdline.c @@ -99,6 +99,14 @@ teco_cmdline_init(void) teco_cmdline_ssm(SCI_SETTABDRAWMODE, SCTD_CONTROLCHAR, 0); /* + * FIXME: This works around a problem where the caret is not + * scrolled in multi-line mode after it wraps at the end of the + * line. You cannot scroll the command-line with the mouse, so the + * user won't see any difference. + */ + teco_cmdline_ssm(SCI_SETENDATLASTLINE, FALSE, 0); + + /* * FIXME: Something resets the margin text, so we have to set it last. */ teco_cmdline_ssm(SCI_MARGINSETTEXT, 0, (sptr_t)"*"); diff --git a/src/core-commands.c b/src/core-commands.c index cd9a8fa..528fa64 100644 --- a/src/core-commands.c +++ b/src/core-commands.c @@ -1739,8 +1739,10 @@ TECO_DEFINE_STATE(teco_state_ascii, /*$ ^[^[ ^[$ $$ ^C terminate return * [a1,a2,...]$$ -- Terminate command line or return from macro + * -$$ * [a1,a2,...]^[$ * [a1,a2,...]^C + * -^C * * Returns from the current macro invocation. * This will pass control to the calling macro immediately @@ -1754,6 +1756,8 @@ TECO_DEFINE_STATE(teco_state_ascii, * All braces opened in the current macro invocation will * be closed and their values discarded. * Only the direct arguments to \fB$$\fP will be kept. + * If the prefix sign is set (\(lq-\fB$$\fP\(rq) the command + * will always leave -1 on the stack. * * Returning from the top-level macro in batch mode * will exit the program or start up interactive mode depending @@ -1806,6 +1810,9 @@ teco_return(teco_machine_main_t *ctx, GError **error) ctx->parent.current = &teco_state_start; if (!teco_expressions_eval(FALSE, error)) return NULL; + if (teco_num_sign < 0) + /* only if you wrote -$$ */ + teco_expressions_push(1); teco_error_return_set(error, teco_expressions_args()); return NULL; } @@ -1873,6 +1880,18 @@ TECO_DEFINE_STATE_START(teco_state_escape, .end_of_macro_cb = teco_state_escape_end_of_macro ); +static gboolean +teco_state_ctlc_initial(teco_machine_main_t *ctx, GError **error) +{ + if (G_UNLIKELY(ctx == &teco_cmdline.machine)) { + g_set_error_literal(error, TECO_ERROR, TECO_ERROR_FAILED, + "<^C> is not allowed to terminate command-lines"); + return FALSE; + } + + return TRUE; +} + /* * Just like ^[, ^C actually implements a lookahead, * so a ^C itself does nothing. @@ -1892,20 +1911,20 @@ teco_state_ctlc_input(teco_machine_main_t *ctx, gunichar chr, GError **error) } static gboolean -teco_state_ctlc_initial(teco_machine_main_t *ctx, GError **error) +teco_state_ctlc_end_of_macro(teco_machine_main_t *ctx, GError **error) { - if (G_UNLIKELY(ctx == &teco_cmdline.machine)) { - g_set_error_literal(error, TECO_ERROR, TECO_ERROR_FAILED, - "<^C> is not allowed to terminate command-lines"); - return FALSE; - } - + if (ctx->flags.mode > TECO_MODE_NORMAL) + return TRUE; + if (teco_num_sign < 0) + /* only if you wrote -^C */ + teco_expressions_push(1); return TRUE; } TECO_DEFINE_STATE_START(teco_state_ctlc, .initial_cb = (teco_state_initial_cb_t)teco_state_ctlc_initial, - .input_cb = (teco_state_input_cb_t)teco_state_ctlc_input + .input_cb = (teco_state_input_cb_t)teco_state_ctlc_input, + .end_of_macro_cb = (teco_state_end_of_macro_cb_t)teco_state_ctlc_end_of_macro ); static teco_state_t * @@ -2169,7 +2188,7 @@ teco_state_ecommand_flags(teco_machine_main_t *ctx, GError **error) * .IP 6: * .SCITECO_TOPIC recovery * Interval in seconds for the creation of recovery files - * or 0 if those dumps are disabled (the default is 300 seconds). + * or 0 if those dumps are disabled (the default is 120 seconds). * When enabled all dirty buffers are dumped to files with hash * signs around the original basename (\fB#\fIfilename\fB#\fR). * They are removed automatically when no longer required, diff --git a/src/interface-curses/curses-info-popup.c b/src/interface-curses/curses-info-popup.c index edb6e15..83d4665 100644 --- a/src/interface-curses/curses-info-popup.c +++ b/src/interface-curses/curses-info-popup.c @@ -73,7 +73,7 @@ teco_curses_info_popup_add(teco_curses_info_popup_t *ctx, teco_popup_entry_type_ } static void -teco_curses_info_popup_init_pad(teco_curses_info_popup_t *ctx, attr_t attr) +teco_curses_info_popup_init_pad(teco_curses_info_popup_t *ctx, attr_t attr, gshort pair) { int pad_lines; /**! pad height */ gint pad_cols; /**! entry columns */ @@ -102,11 +102,11 @@ teco_curses_info_popup_init_pad(teco_curses_info_popup_t *ctx, attr_t attr) ctx->pad = newpad(pad_lines, COLS - 2); /* - * NOTE: attr could contain A_REVERSE on monochrome terminals, + * NOTE: attr could contain WA_REVERSE on monochrome terminals, * so we use foreground attributes instead of background attributes. - * This way, we can cancel out the A_REVERSE if necessary. + * This way, we can cancel out the WA_REVERSE if necessary. */ - wattrset(ctx->pad, attr); + wattr_set(ctx->pad, attr, pair, NULL); teco_curses_clrtobot(ctx->pad); /* @@ -161,7 +161,7 @@ teco_curses_info_popup_init_pad(teco_curses_info_popup_t *ctx, attr_t attr) } void -teco_curses_info_popup_show(teco_curses_info_popup_t *ctx, attr_t attr) +teco_curses_info_popup_show(teco_curses_info_popup_t *ctx, attr_t attr, gshort pair) { if (!ctx->length) /* nothing to display */ @@ -171,7 +171,7 @@ teco_curses_info_popup_show(teco_curses_info_popup_t *ctx, attr_t attr) delwin(ctx->window); if (!ctx->pad) - teco_curses_info_popup_init_pad(ctx, attr); + teco_curses_info_popup_init_pad(ctx, attr, pair); gint pad_lines = getmaxy(ctx->pad); /* @@ -183,15 +183,17 @@ teco_curses_info_popup_show(teco_curses_info_popup_t *ctx, attr_t attr) /* window covers message, scintilla and info windows */ ctx->window = newwin(popup_lines, 0, LINES - teco_cmdline.height - popup_lines, 0); - wattrset(ctx->window, attr); + wattr_set(ctx->window, attr, pair, NULL); - wborder(ctx->window, - ACS_VLINE, - ACS_VLINE, /* may be overwritten with scrollbar */ - ACS_HLINE, - ' ', /* no bottom line */ - ACS_ULCORNER, ACS_URCORNER, - ACS_VLINE, ACS_VLINE); + /* + * NOTE: wborder() is broken for large pair numbers, at least on ncurses. + */ + waddch(ctx->window, ACS_ULCORNER); + whline(ctx->window, ACS_HLINE, COLS - 2); + mvwaddch(ctx->window, 0, COLS - 1, ACS_URCORNER); + mvwvline(ctx->window, 1, 0, ACS_VLINE, getmaxy(ctx->window)-1); + /* may be overwritten with scrollbar */ + mvwvline(ctx->window, 1, COLS - 1, ACS_VLINE, getmaxy(ctx->window)-1); copywin(ctx->pad, ctx->window, ctx->pad_first_line, 0, @@ -214,7 +216,7 @@ teco_curses_info_popup_show(teco_curses_info_popup_t *ctx, attr_t attr) * Instead, simply draw reverse blanks. */ wmove(ctx->window, bar_y, COLS-1); - wattrset(ctx->window, attr ^ A_REVERSE); + wattr_set(ctx->window, attr ^ WA_REVERSE, pair, NULL); wvline(ctx->window, ' ', bar_height); } diff --git a/src/interface-curses/curses-info-popup.h b/src/interface-curses/curses-info-popup.h index fd923e9..898ba70 100644 --- a/src/interface-curses/curses-info-popup.h +++ b/src/interface-curses/curses-info-popup.h @@ -49,7 +49,7 @@ teco_curses_info_popup_init(teco_curses_info_popup_t *ctx) void teco_curses_info_popup_add(teco_curses_info_popup_t *ctx, teco_popup_entry_type_t type, const gchar *name, gsize name_len, gboolean highlight); -void teco_curses_info_popup_show(teco_curses_info_popup_t *ctx, attr_t attr); +void teco_curses_info_popup_show(teco_curses_info_popup_t *ctx, attr_t attr, gshort pair); const teco_string_t *teco_curses_info_popup_getentry(teco_curses_info_popup_t *ctx, gint y, gint x); void teco_curses_info_popup_scroll_page(teco_curses_info_popup_t *ctx); void teco_curses_info_popup_scroll(teco_curses_info_popup_t *ctx, gint delta); diff --git a/src/interface-curses/curses-utils.c b/src/interface-curses/curses-utils.c index 875c332..3b25d56 100644 --- a/src/interface-curses/curses-utils.c +++ b/src/interface-curses/curses-utils.c @@ -53,9 +53,9 @@ teco_curses_format_str(WINDOW *win, const gchar *str, gsize len, gint max_width) /* * The entire background might be in reverse, especially * on monochrome terminals. - * In those cases, we have to __remove__ the A_REVERSE flag. + * In those cases, we have to __remove__ the WA_REVERSE flag. */ - attr_t attrs = A_NORMAL; + attr_t attrs = WA_NORMAL; short pair = 0; wattr_get(win, &attrs, &pair, NULL); @@ -81,28 +81,28 @@ teco_curses_format_str(WINDOW *win, const gchar *str, gsize len, gint max_width) chars_added++; if (chars_added > max_width) goto truncate; - wattr_set(win, attrs ^ A_REVERSE, pair, NULL); + wattr_set(win, attrs ^ WA_REVERSE, pair, NULL); waddch(win, '$'); break; case '\r': chars_added += 2; if (chars_added > max_width) goto truncate; - wattr_set(win, attrs ^ A_REVERSE, pair, NULL); + wattr_set(win, attrs ^ WA_REVERSE, pair, NULL); waddstr(win, "CR"); break; case '\n': chars_added += 2; if (chars_added > max_width) goto truncate; - wattr_set(win, attrs ^ A_REVERSE, pair, NULL); + wattr_set(win, attrs ^ WA_REVERSE, pair, NULL); waddstr(win, "LF"); break; case '\t': chars_added += 3; if (chars_added > max_width) goto truncate; - wattr_set(win, attrs ^ A_REVERSE, pair, NULL); + wattr_set(win, attrs ^ WA_REVERSE, pair, NULL); waddstr(win, "TAB"); break; default: @@ -110,7 +110,7 @@ teco_curses_format_str(WINDOW *win, const gchar *str, gsize len, gint max_width) chars_added += 2; if (chars_added > max_width) goto truncate; - wattr_set(win, attrs ^ A_REVERSE, pair, NULL); + wattr_set(win, attrs ^ WA_REVERSE, pair, NULL); waddch(win, '^'); waddch(win, TECO_CTL_ECHO(*str)); } else { @@ -126,7 +126,7 @@ teco_curses_format_str(WINDOW *win, const gchar *str, gsize len, gint max_width) waddnstr(win, str, clen); } } - /* restore original state of A_REVERSE */ + /* restore original state of WA_REVERSE */ wattr_set(win, attrs, pair, NULL); str += clen; diff --git a/src/interface-curses/curses-utils.h b/src/interface-curses/curses-utils.h index 97fc1cc..c6d9d8d 100644 --- a/src/interface-curses/curses-utils.h +++ b/src/interface-curses/curses-utils.h @@ -27,15 +27,12 @@ guint teco_curses_format_str(WINDOW *win, const gchar *str, gsize len, gint max_ guint teco_curses_format_filename(WINDOW *win, const gchar *filename, gint max_width); -/** - * Add Unicode character to window. - * This is just like wadd_wch(), but does not require wide-char APIs. - */ +/** Add Unicode character to window. */ static inline void teco_curses_add_wc(WINDOW *win, gunichar chr) { - gchar buf[6]; - waddnstr(win, buf, g_unichar_to_utf8(chr, buf)); + wchar_t wc = chr; + waddnwstr(win, &wc, 1); } /** diff --git a/src/interface-curses/interface.c b/src/interface-curses/interface.c index 6c8b231..b49540b 100644 --- a/src/interface-curses/interface.c +++ b/src/interface-curses/interface.c @@ -137,6 +137,7 @@ teco_console_ctrl_handler(DWORD type) static gint teco_xterm_version(void) G_GNUC_UNUSED; static gint teco_interface_blocking_getch(void); +static inline gboolean teco_interface_check_key(gint key); /** * Get bright variant of one of the 8 standard @@ -233,63 +234,49 @@ static struct { * Scinterm no longer initializes all color pairs for all combinations of * the builtin foreground and background colors. * Since curses does not guarantee any number of color pairs, we cannot do that either. - * Instead we allocate color pairs beginnig at the second half of - * color pair space on demand (similar to what Scinterm does). - * - * @note Scinterm now also has scintilla_set_color_offsets(), - * so we could use the color pairs beginning with 1 as well. + * Instead we allocate color pairs in the first half of + * color pair space on demand, while the second half is reserved to Scinterm. * + * @param attr attributes to modify (for supporting monochrome terminals) * @param fg curses foreground color * @param bg curses background color * @return curses color pair number */ static gshort -teco_color_pair(gshort fg, gshort bg) +teco_color_pair(attr_t *attr, gshort fg, gshort bg) { - static gshort allocated_pairs = 0; + static guint next_pair = 1; + + /* + * Basic support for monochrome terminals: + * Every background, that is not black is assumed to be a + * dark-on-bright area, rendered in reverse. + * This will at least work with the terminal.tes and contrast.tes + * color schemes. + */ + if (!has_colors()) { + if (bg != COLOR_BLACK) + *attr |= WA_REVERSE; + return 0; + } G_STATIC_ASSERT(sizeof(gshort)*2 <= sizeof(guint)); gpointer key = GUINT_TO_POINTER(((guint)fg << 8*sizeof(bg)) | bg); gpointer value = g_hash_table_lookup(teco_interface.pair_table, key); if (G_LIKELY(value != NULL)) return GPOINTER_TO_UINT(value); - /* - * We are still adding colors with COLOR_PAIR(), which allows - * at most 256 color pairs (1-255). - */ - gshort new_pair = MIN(COLOR_PAIRS, 256)/2 + allocated_pairs; - if (G_UNLIKELY(new_pair >= 256)) + if (G_UNLIKELY(next_pair >= COLOR_PAIRS || next_pair > G_MAXSHORT)) return 0; - allocated_pairs++; - init_pair(new_pair, fg, bg); - g_hash_table_insert(teco_interface.pair_table, key, GUINT_TO_POINTER(new_pair)); - return new_pair; + init_pair(next_pair, fg, bg); + g_hash_table_insert(teco_interface.pair_table, key, GUINT_TO_POINTER(next_pair)); + return next_pair++; } -/** - * Curses attribute for the color combination - * according to the color pairs initialized by - * Scinterm. - * This is equivalent to Scinterm's internal term_color_attr(). - * - * @param fg foreground color - * @param bg background color - * @return curses attribute - */ -static inline attr_t -teco_color_attr(gshort fg, gshort bg) +static inline gint +teco_wattr_set(WINDOW *win, attr_t attr, gshort fg, gshort bg) { - if (has_colors()) - return COLOR_PAIR(teco_color_pair(fg, bg)); - - /* - * Basic support for monochrome terminals: - * Every background, that is not black is assumed to be a - * dark-on-bright area, rendered in reverse. - * This will at least work with the terminal.tes and contrast.tes - * color schemes. - */ - return bg != COLOR_BLACK ? A_REVERSE : 0; + gshort pair = teco_color_pair(&attr, fg, bg); + return wattr_set(win, attr, pair, NULL); } /** @@ -443,7 +430,7 @@ static void teco_interface_set_window_title(const gchar *title); static void teco_interface_draw_info(void); void -teco_interface_init(void) +teco_interface_init(gint argc, gchar **argv) { for (guint i = 0; i < G_N_ELEMENTS(teco_interface.color_table); i++) teco_interface.color_table[i] = -1; @@ -457,7 +444,7 @@ teco_interface_init(void) teco_cmdline_init(); /* * The default INDIC_STRIKE wouldn't be visible. - * Instead we use INDIC_SQUIGGLE, which is rendered as A_UNDERLINE. + * Instead we use INDIC_SQUIGGLE, which is rendered as WA_UNDERLINE. */ teco_cmdline_ssm(SCI_INDICSETSTYLE, INDICATOR_RUBBEDOUT, INDIC_SQUIGGLE); /* @@ -760,6 +747,12 @@ teco_interface_init_interactive(GError **error) teco_interface.pair_table = g_hash_table_new(g_direct_hash, g_direct_equal); start_color(); + /* + * Scinterm uses shorts for color pairs, so G_MAXSHORT is the maximum. + * Some Curses implementations (NetBSD, PDCurses) may support less, + * but set COLOR_PAIRS accordingly. + */ + scintilla_set_color_offsets(0, MIN(COLOR_PAIRS, G_MAXSHORT)/2); /* * On UNIX terminals, the escape key is usually @@ -950,7 +943,7 @@ teco_interface_msg_literal(teco_msg_t type, const gchar *str, gsize len) teco_interface_stdio_msg(type, str, len); #endif - short fg, bg; + gshort fg, bg; fg = teco_rgb2curses(teco_interface_ssm(SCI_STYLEGETBACK, STYLE_DEFAULT, 0)); @@ -972,7 +965,7 @@ teco_interface_msg_literal(teco_msg_t type, const gchar *str, gsize len) } wmove(teco_interface.msg_window, 0, 0); - wattrset(teco_interface.msg_window, teco_color_attr(fg, bg)); + teco_wattr_set(teco_interface.msg_window, 0, fg, bg); teco_curses_format_str(teco_interface.msg_window, str, len, -1); teco_curses_clrtobot(teco_interface.msg_window); } @@ -983,11 +976,11 @@ teco_interface_msg_clear(void) if (!teco_interface.input_pad) /* batch mode */ return; - short fg = teco_rgb2curses(teco_interface_ssm(SCI_STYLEGETBACK, STYLE_DEFAULT, 0)); - short bg = teco_rgb2curses(teco_interface_ssm(SCI_STYLEGETFORE, STYLE_DEFAULT, 0)); + gshort fg = teco_rgb2curses(teco_interface_ssm(SCI_STYLEGETBACK, STYLE_DEFAULT, 0)); + gshort bg = teco_rgb2curses(teco_interface_ssm(SCI_STYLEGETFORE, STYLE_DEFAULT, 0)); wmove(teco_interface.msg_window, 0, 0); - wattrset(teco_interface.msg_window, teco_color_attr(fg, bg)); + teco_wattr_set(teco_interface.msg_window, 0, fg, bg); teco_curses_clrtobot(teco_interface.msg_window); } @@ -1012,15 +1005,30 @@ teco_interface_getch(gboolean widechar) gint32 cp; do { - cp = teco_interface_blocking_getch(); - if (cp == TECO_CTL_KEY('C')) - teco_interrupted = TRUE; - if (cp == TECO_CTL_KEY('C') || cp == TECO_CTL_KEY('D')) { - cp = -1; - break; - } - if (cp < 0 || cp > 0xFF) - continue; + do { + cp = teco_interface_blocking_getch(); + switch (cp) { +#ifdef KEY_RESIZE + case KEY_RESIZE: + teco_interface_resize_all_windows(); + teco_interface_refresh(FALSE); + /* cursor was moved by the resize */ + wmove(teco_interface.msg_window, 0, 0); + wrefresh(teco_interface.msg_window); + break; +#endif + case KEY_BACKSPACE: + return TECO_CTL_KEY('H'); + case KEY_ENTER: + return '\n'; + case TECO_CTL_KEY('C'): + teco_interrupted = TRUE; + /* fall through */ + case TECO_CTL_KEY('D'): + /* emulates EOF on stdin */ + return -1; + } + } while (cp < 0 || cp > 0xFF || !teco_interface_check_key(cp)); if (!widechar || !cp) break; @@ -1161,11 +1169,11 @@ teco_interface_draw_info(void) * the current buffer's STYLE_DEFAULT. * The same style is used for MSG_USER messages. */ - short fg = teco_rgb2curses(teco_interface_ssm(SCI_STYLEGETBACK, STYLE_DEFAULT, 0)); - short bg = teco_rgb2curses(teco_interface_ssm(SCI_STYLEGETFORE, STYLE_DEFAULT, 0)); + gshort fg = teco_rgb2curses(teco_interface_ssm(SCI_STYLEGETBACK, STYLE_DEFAULT, 0)); + gshort bg = teco_rgb2curses(teco_interface_ssm(SCI_STYLEGETFORE, STYLE_DEFAULT, 0)); wmove(teco_interface.info_window, 0, 0); - wattrset(teco_interface.info_window, teco_color_attr(fg, bg)); + teco_wattr_set(teco_interface.info_window, 0, fg, bg); const gchar *info_type_str; @@ -1734,11 +1742,13 @@ teco_interface_popup_show(gsize prefix_len) /* batch mode */ return; - short fg = teco_rgb2curses(teco_interface_ssm(SCI_STYLEGETFORE, STYLE_CALLTIP, 0)); - short bg = teco_rgb2curses(teco_interface_ssm(SCI_STYLEGETBACK, STYLE_CALLTIP, 0)); + gshort fg = teco_rgb2curses(teco_interface_ssm(SCI_STYLEGETFORE, STYLE_CALLTIP, 0)); + gshort bg = teco_rgb2curses(teco_interface_ssm(SCI_STYLEGETBACK, STYLE_CALLTIP, 0)); teco_interface.popup_prefix_len = prefix_len; - teco_curses_info_popup_show(&teco_interface.popup, teco_color_attr(fg, bg)); + attr_t attr = 0; + gshort pair = teco_color_pair(&attr, fg, bg); + teco_curses_info_popup_show(&teco_interface.popup, attr, pair); } void @@ -1938,9 +1948,11 @@ teco_interface_process_mevent(MEVENT *event, GError **error) else if (event->bstate & BUTTON_NUM(5)) teco_curses_info_popup_scroll(&teco_interface.popup, +2); - short fg = teco_rgb2curses(teco_interface_ssm(SCI_STYLEGETFORE, STYLE_CALLTIP, 0)); - short bg = teco_rgb2curses(teco_interface_ssm(SCI_STYLEGETBACK, STYLE_CALLTIP, 0)); - teco_curses_info_popup_show(&teco_interface.popup, teco_color_attr(fg, bg)); + gshort fg = teco_rgb2curses(teco_interface_ssm(SCI_STYLEGETFORE, STYLE_CALLTIP, 0)); + gshort bg = teco_rgb2curses(teco_interface_ssm(SCI_STYLEGETBACK, STYLE_CALLTIP, 0)); + attr_t attr = 0; + gshort pair = teco_color_pair(&attr, fg, bg); + teco_curses_info_popup_show(&teco_interface.popup, attr, pair); return TRUE; } @@ -2126,6 +2138,31 @@ teco_interface_blocking_getch(void) return key; } +#ifdef __PDCURSES__ + +/* + * Especially PDCurses/WinGUI likes to report two keypresses, + * e.g. for CTRL+Shift+6 (CTRL+^). + * Make sure we don't filter out AltGr, which may be reported as CTRL+ALT. + */ +static inline gboolean +teco_interface_check_key(gint key) +{ + return (PDC_get_key_modifiers() & + (PDC_KEY_MODIFIER_CONTROL | PDC_KEY_MODIFIER_ALT)) != PDC_KEY_MODIFIER_CONTROL || + TECO_IS_CTL(key); +} + +#else /* __PDCURSES__ */ + +static inline gboolean +teco_interface_check_key(gint key) +{ + return TRUE; +} + +#endif + /** * One iteration of the event loop. * @@ -2231,22 +2268,10 @@ teco_interface_event_loop_iter(void) * Control keys and keys with printable representation */ default: - if (key > 0xFF) - /* unhandled function key */ + if (key > 0xFF || !teco_interface_check_key(key)) + /* unhandled function key or bogus key press */ return; -#ifdef __PDCURSES__ - /* - * Especially PDCurses/WinGUI likes to report two keypresses, - * e.g. for CTRL+Shift+6 (CTRL+^). - * Make sure we don't filter out AltGr, which may be reported as CTRL+ALT. - */ - if ((PDC_get_key_modifiers() & - (PDC_KEY_MODIFIER_CONTROL | PDC_KEY_MODIFIER_ALT)) == PDC_KEY_MODIFIER_CONTROL && - !TECO_IS_CTL(key)) - return; -#endif - /* * NOTE: There's also wget_wch(), but it requires * a widechar version of Curses. diff --git a/src/interface-gtk/interface.c b/src/interface-gtk/interface.c index ace3a2f..1540245 100644 --- a/src/interface-gtk/interface.c +++ b/src/interface-gtk/interface.c @@ -22,6 +22,7 @@ #include <stdlib.h> #include <string.h> #include <signal.h> +#include <unistd.h> #include <glib.h> #include <glib/gprintf.h> @@ -132,10 +133,10 @@ static struct { } teco_interface; void -teco_interface_init(void) +teco_interface_init(gint argc, gchar **argv) { #ifdef G_OS_UNIX - if (teco_interface.detach) { + if (teco_interface.detach && !g_getenv("__SCITECO_DETACHED")) { /* * NOTE: There is also daemon() on BSD/Linux, * but the following should be more portable. @@ -148,9 +149,29 @@ teco_interface_init(void) setsid(); - g_freopen("/dev/null", "r", stdin); - g_freopen("/dev/null", "a+", stdout); - g_freopen("/dev/null", "a+", stderr); + if (isatty(0)) { + G_GNUC_UNUSED FILE *stdin_new = g_freopen("/dev/null", "r", stdin); + g_assert(stdin_new != NULL); + } + if (isatty(1)) { + G_GNUC_UNUSED FILE *stdout_new = g_freopen("/dev/null", "a+", stdout); + g_assert(stdout_new != NULL); + } + if (isatty(2)) { + G_GNUC_UNUSED FILE *stderr_new = g_freopen("/dev/null", "a+", stderr); + g_assert(stderr_new != NULL); + } + + /* + * gtk_get_option_group() already initialized GTK and even though the + * display is not yet opened, it's unsafe to continue. + * Instead, we re-exec already in the child process. + * We cannot easily remove --detach from argv, but still guard against + * recursive forks by using an environment variable. + */ + g_setenv("__SCITECO_DETACHED", "1", TRUE); + execv(argv[0], argv); + g_assert_not_reached(); } #endif @@ -430,14 +451,15 @@ teco_interface_getch_commit_cb(GtkIMContext *context, gchar *str, gpointer user_ */ *cp = g_utf8_get_char_validated(str, -1); g_assert(*cp >= 0); - gtk_main_quit(); + + /* we might be invoked outside of a nested event loop */ + if (gtk_main_level() > 1) + gtk_main_quit(); } static gboolean -teco_interface_getch_input_cb(GtkWidget *widget, GdkEvent *event, gpointer user_data) +teco_interface_getch_process_event(GdkEvent *event, teco_int_t *cp) { - teco_int_t *cp = user_data; - g_assert(event->type == GDK_KEY_PRESS); switch (event->key.keyval) { @@ -458,16 +480,31 @@ teco_interface_getch_input_cb(GtkWidget *widget, GdkEvent *event, gpointer user_ teco_interrupted = TRUE; /* fall through */ case TECO_CTL_KEY('D'): + /* emulates EOF on stdin */ *cp = -1; } break; } + /* + * NOTE: The teco_interface_getch_commit_cb() could be called immediately + * without returning to the event loop. + */ gtk_im_context_filter_keypress(teco_interface.input_method, &event->key); - return TRUE; + return *cp >= 0; } - gtk_main_quit(); + return TRUE; +} + +static gboolean +teco_interface_getch_input_cb(GtkWidget *widget, GdkEvent *event, gpointer user_data) +{ + teco_int_t *cp = user_data; + + if (teco_interface_getch_process_event(event, cp)) + gtk_main_quit(); + return TRUE; } @@ -492,6 +529,22 @@ teco_interface_getch(gboolean widechar) G_CALLBACK(teco_interface_getch_commit_cb), &cp); /* + * The original teco_interface_input_cb() could have already enqueued events + * (also between ^T calls). + * This must be done after registering teco_interface_getch_commit_cb() already. + * + * NOTE: Already enqueued mouse events will currently be discarded silently. + */ + for (;;) { + g_autoptr(GdkEvent) event = g_queue_pop_head(teco_interface.event_queue); + if (!event) + break; + if (event->type == GDK_KEY_PRESS && + teco_interface_getch_process_event(event, &cp)) + goto cleanup; + } + + /* * Highlights the first character in the label. * This mimics what the Curses UI does. * Is there a better way to signal that we expect input? @@ -505,6 +558,7 @@ teco_interface_getch(gboolean widechar) gdk_window_freeze_updates(top_window); +cleanup: g_signal_handler_disconnect(teco_interface.input_method, commit_handler); g_signal_handlers_unblock_by_func(teco_interface.input_method, teco_interface_cmdline_commit_cb, NULL); g_signal_handler_disconnect(teco_interface.window, key_handler); diff --git a/src/interface.h b/src/interface.h index f196a83..54f807b 100644 --- a/src/interface.h +++ b/src/interface.h @@ -51,7 +51,7 @@ extern teco_view_t *teco_interface_current_view; /** @pure */ -void teco_interface_init(void); +void teco_interface_init(gint argc, gchar **argv); /** @pure */ GOptionGroup *teco_interface_get_options(void); @@ -54,7 +54,7 @@ /* * Define this to pause the program at the beginning - * of main() (Windows only). + * of main(). * This is a useful hack on Windows, where gdbserver * sometimes refuses to start SciTECO but attaches * to a running process just fine. @@ -382,8 +382,7 @@ main(int argc, char **argv) #endif #ifdef DEBUG_PAUSE - /* Windows debugging hack (see above) */ - system("pause"); + getchar(); #endif signal(SIGINT, teco_sigint_handler); @@ -445,7 +444,7 @@ main(int argc, char **argv) */ teco_qreg_table_init(&teco_qreg_table_globals, TRUE); - teco_interface_init(); + teco_interface_init(argc, argv); /* * QRegister view must be initialized only now @@ -1694,11 +1694,7 @@ teco_machine_qregspec_auto_complete(teco_machine_qregspec_t *ctx, teco_string_t /* two-letter Q-Reg */ restrict_len = 2; - /* - * FIXME: This is not quite right as it will propose even - * lower case single or two-letter Q-Register names. - */ - return teco_rb3str_auto_complete(&ctx->result_table->tree, !restrict_len, + return teco_rb3str_auto_complete(&ctx->result_table->tree, TRUE, ctx->name.data, ctx->name.len, restrict_len, insert) && ctx->nesting == 1; } @@ -312,7 +312,7 @@ teco_ring_save_all_dirty_buffers(GError **error) * Recovery creation interval in seconds or 0 if disabled. * It's not currently enforced in batch mode. */ -guint teco_ring_recovery_interval = 5*60; +guint teco_ring_recovery_interval = 2*60; /** * Create recovery files for all dirty buffers. @@ -731,13 +731,18 @@ teco_state_read_file_done(teco_machine_main_t *ctx, teco_string_t str, GError ** return &teco_state_start; sptr_t pos = teco_interface_ssm(SCI_GETCURRENTPOS, 0, 0); + teco_undo_int(teco_ranges[0].from) = teco_interface_bytes2glyphs(pos); g_autofree gchar *filename = teco_file_expand_path(str.data); /* FIXME: Add wrapper to interface.h? */ if (!teco_view_load(teco_interface_current_view, filename, FALSE, error)) return NULL; - if (teco_interface_ssm(SCI_GETCURRENTPOS, 0, 0) != pos) { + pos = teco_interface_ssm(SCI_GETCURRENTPOS, 0, 0); + teco_undo_int(teco_ranges[0].to) = teco_interface_bytes2glyphs(pos); + teco_undo_guint(teco_ranges_count) = 1; + + if (teco_ranges[0].from != teco_ranges[0].to) { teco_ring_dirtify(); if (teco_current_doc_must_undo()) diff --git a/tests/testsuite.at b/tests/testsuite.at index 23e2dab..6bc33e4 100644 --- a/tests/testsuite.at +++ b/tests/testsuite.at @@ -296,6 +296,13 @@ TE_CHECK([[@I/ABCDEF/J @FS/ABC/1234/ ^S+4"N(0/0)']], 0, ignore, ignore) TE_CHECK([[@^Ua/XYZ/ Ga ^S+3"N(0/0)']], 0, ignore, ignore) # NOTE: EN currently inserts another trailing linefeed. TE_CHECK([[@EN/*/XYZ/ ^S+4"N(0/0)']], 0, ignore, ignore) +AT_DATA([test.txt], [[0123456789 +]]) +TE_CHECK([[@ER"test.txt" ^S+11"N(0/0)']], 0, ignore, ignore) +AT_CLEANUP + +AT_SETUP([Macro calls]) +TE_CHECK([[@^Ua{-$$} Ma+1"N(0/0)']], 0, ignore, ignore) AT_CLEANUP AT_SETUP([Editing local registers in macro calls]) @@ -664,6 +671,12 @@ TE_CHECK([[@^Um{U.a Q.a-100000"<%.aMm'} 0Mm]], 0, ignore, ignore) AT_XFAIL_IF(true) AT_CLEANUP +AT_SETUP([Backtracking in patterns]) +# ^ES should be greedy and posessive +TE_CHECK([[@I/ /J :@S/^ES^X/"S(0/0)']], 0, ignore, ignore) +AT_XFAIL_IF(true) +AT_CLEANUP + AT_SETUP([Rub out from empty string argument]) # Should rub out the modifiers as well. # Will currently fail because it tries to execute `:@{`. diff --git a/www/build.tes b/www/build.tes index 848a63c..89ac70a 100755 --- a/www/build.tes +++ b/www/build.tes @@ -18,6 +18,7 @@ <html> <head> <title>SciTECO - <Website> Q[title]</title> + <link rel="canonical" href="https://sciteco.fmsbw.de/Q[file]"> <link rel="icon" type="image/x-icon" href="https://sciteco.fmsbw.de/graphics/sciteco.ico"> <meta name="description" content="Advanced TECO dialect and interactive screen editor based on Scintilla"> <style> @@ -65,7 +66,7 @@ </html> } -EBindex.html HK +[file] EBindex.html HK [title]Home M[header] EClowdown -thtml --html-no-skiphtml --html-no-escapehtml ../NEWS.md I<hr> @@ -73,7 +74,7 @@ EBindex.html HK M[footer] EW -EBscreenshots.html HK +[file]screenshots.html EBN[file] HK [title]Screenshots M[header] EClowdown -thtml --html-no-skiphtml --html-no-escapehtml screenshots.md M[footer] @@ -87,44 +88,44 @@ EW * FIXME: Support out-of-tree builds. * Perhaps pass in the biuld directory. *! -EBQ[builddir]/doc/sciteco.1.html +[file]sciteco.1.html EBN[builddir]/doc/N[file] S<body>S<h1 L 0,.K [title]sciteco(1) M[header] G[grohtml-header] FD<hr>S</body> .,ZK M[footer] -EWsciteco.1.html +EWQ[file] -EBQ[builddir]/doc/sciteco.7.html +[file]sciteco.7.html EBN[builddir]/doc/N[file] S<body>S<h1 L 0,.K [title]sciteco(7) M[header] G[grohtml-header] FD<hr>S</body> .,ZK M[footer] -EWsciteco.7.html +EWQ[file] !* * These grohtml-generated documents are not in the header bar, * but still postprocessed for consinstency. *! -EBQ[builddir]/doc/grosciteco.tes.1.html +[file]grosciteco.tes.1.html EBN[builddir]/doc/N[file] S<body>S<h1 L 0,.K [title]grosciteco.tes(1) M[header] G[grohtml-header] FD<hr>S</body> .,ZK M[footer] -EWgrosciteco.tes.1.html +EWQ[file] -EBQ[builddir]/doc/tedoc.tes.1.html +[file]tedoc.tes.1.html EBN[builddir]/doc/N[file] S<body>S<h1 L 0,.K [title]tedoc.tes(1) M[header] G[grohtml-header] FD<hr>S</body> .,ZK M[footer] -EWtedoc.tes.1.html +EWQ[file] -EBQ[builddir]/doc/tutorial.html +[file]tutorial.html EBN[builddir]/doc/N[file] S<body>S<h1 L 0,.K [title]Tutorial M[header] G[grohtml-header] FD<hr>S</body> .,ZK M[footer] -EWtutorial.html +EWQ[file] EX |
