Files
TeslaRel410/sda4/BRIEF/MACROS/INDENT.CB
T
CydandClaude Fable 5 db7745fcd0 sda4: commit the Glaze developer hard-drive dump
Un-ignored: the dev drive is the ground truth the restoration and
emulator work constantly reference (DPL3/LIBDPL + VRENDER i860 renderer
source, BT/RP live+dev game trees, VGL_LABS pod boot, scene/audio
content). Kept in-repo for the pod-owner community.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-04 19:41:15 -05:00

955 lines
23 KiB
Plaintext

/*
** BRIEF -- Basic Reconfigurable Interactive Editing Facility
**
** Written by Dave Nanian and Michael Strickman.
*/
/*
** indent.cb:
**
** This macro performs automatic indenting for a number of languages.
** Other languages can be supported through the standard BPACKAGES
** interface.
*/
#define MIN_ABBREV 1 // Increase this to specify longer
// default minimum abbreviations.
#define REG_MARK 1 // Regular marks.
#define COL_MARK 2 // Column marks.
#define LINE_MARK 3 // Line marks.
#define NI_MARK 4 // Non-inclusive marks.
#define C_SKIP_PAT "{{/\\*{{[~*/]{/+[~*/]}@}|\\*}@\\*/}|{//*}|{<{[A-Za-z0-9_]+:}|{[ \xc\t]@#*}}|[ \xc\t\n]}@"
/*
** ABBREV_LIST_2 contains the list of abbreviations that don't require
** cursor positioning. ABBREV_LIST_1 requires the cursor to back up a
** character (usually placing it inside a parenthesis).
*/
#define ABBREV_LIST_1 "\xffreturn \xffbreak;\xffdo\xffdefault:\xffelse \xffcase :\xffcontinue;\xff"
#define ABBREV_LIST_2 "\xffei\xffelse if ()\xffif ()\xffwhile ()\xfffor ()\xffswitch ()\xff"
void back_tab (void);
void open_line (void);
string .c_template_first (~int, ~int, ~int, ~int);
string .c_smart_first (~int, ~int, ~int);
string .cb_template_first (~int, ~int, ~int, ~int);
string .cb_smart_first (~int, ~int, ~int);
string _regular_first (void);
int .c_indent (~int indent_query);
void r_indent (void);
void slide_in (void);
void slide_out (void);
void .c_abbrev (void);
void brace_expand (void);
void .c_open_brace (void);
void .c_close_brace (void);
void just_brace (void);
void just_cbrace (void);
void just_space (void);
void comment_block (void);
void uncomment_block (void);
int .c_min_abbrev, // Minimum template abbreviation.
.c_indent_open, // Location of the open brace.
.c_indent_close, // Location of the close brace.
.c_indent_first, // Location of the first braces.
.c_smart, // C smart indenting keymap.
.c_template, // C template editing keymap, expands on <Space>.
.c_alt_template, // C template editing keymap, expands on <Tab>.
_r_keymap; // Regular indenting keymap.
/*
** .c_template_first, .c_smart_first,
** .cb_template_first, .cb_smart_first,
** _regular_first:
**
** These macros are called by the BPACKAGES parser in language.m.
** They initialize the local keymaps for the various indenting functions,
** set the abbreviation length and adjust the indenting style.
*/
string .c_template_first (~int, ~int, ~int, ~int)
{
.c_min_abbrev = MIN_ABBREV;
get_parm (3, .c_min_abbrev);
if (first_time())
{
keyboard_push ();
assign_to_key ("<Enter>", ".c_indent");
assign_to_key ("<Tab>", ".c_abbrev");
assign_to_key ("<Shift-Tab>", "slide_out");
assign_to_key ("<{>", "brace_expand");
assign_to_key ("<}>", ".c_close_brace");
assign_to_key ("<Ctrl-{>", "just_brace");
assign_to_key ("<Ctrl-}>", "just_cbrace");
assign_to_key ("<Ctrl-s>", "just_space");
.c_alt_template = inq_keyboard ();
keyboard_pop (1);
keyboard_push ();
assign_to_key ("<Enter>", ".c_indent");
assign_to_key ("<Tab>", "slide_in");
assign_to_key ("<Shift-Tab>", "slide_out");
assign_to_key ("<Space>", ".c_abbrev");
assign_to_key ("<{>", "brace_expand");
assign_to_key ("<}>", ".c_close_brace");
assign_to_key ("<Ctrl-{>", "just_brace");
assign_to_key ("<Ctrl-}>", "just_cbrace");
assign_to_key ("<Ctrl-s>", "just_space");
.c_template = inq_keyboard ();
keyboard_pop (1);
}
if (.c_min_abbrev == 0)
{
use_local_keyboard (.c_alt_template);
.c_min_abbrev = 1;
}
else
use_local_keyboard (.c_template);
.c_indent_open = 1;
.c_indent_close = 1;
.c_indent_first = 0;
get_parm (0, .c_indent_open);
get_parm (1, .c_indent_close);
get_parm (2, .c_indent_first);
if (!.c_indent_open || !.c_indent_close)
.c_indent_first = 0;
returns ("");
}
string .c_smart_first (~int, ~int, ~int)
{
if (first_time())
{
keyboard_push ();
assign_to_key ("<Enter>", ".c_indent");
assign_to_key ("<Tab>", "slide_in");
assign_to_key ("<Shift-Tab>", "slide_out");
assign_to_key ("<{>", ".c_open_brace");
assign_to_key ("<}>", ".c_close_brace");
assign_to_key ("<Ctrl-{>", "just_brace");
assign_to_key ("<Ctrl-}>", "just_cbrace");
.c_smart = inq_keyboard ();
keyboard_pop (1);
}
use_local_keyboard (.c_smart);
.c_indent_open = 1;
.c_indent_close = 1;
.c_indent_first = 0;
get_parm (0, .c_indent_open);
get_parm (1, .c_indent_close);
get_parm (2, .c_indent_first);
if (!.c_indent_open || !.c_indent_close)
.c_indent_first = 0;
returns ("");
}
string .cb_template_first (~int p1, ~int p2, ~int p3, ~int p4)
{
returns ( .c_template_first(p1, p2, p3, p4) );
}
string .cb_smart_first (~int p1, ~int p2, ~int p3)
{
returns ( .c_smart_first(p1, p2, p3) );
}
string _regular_first (void)
{
if (first_time())
{
keyboard_push ();
assign_to_key ("<Enter>", "r_indent");
assign_to_key ("<Tab>", "slide_in");
assign_to_key ("<Shift-Tab>", "slide_out");
_r_keymap = inq_keyboard ();
keyboard_pop (1);
}
use_local_keyboard (_r_keymap);
returns ("");
}
/*
** .c_indent:
**
** This macro does syntax-sensitive indenting ("smart indenting") for
** C language files.
*/
int .c_indent (~int indent_query)
{
int curr_indent_col, // Current unmodified indent col.
prev_indent_col, // Previous unmodified indent col.
curr_line, // Line cursor is on when called.
curr_col, // Column cursor is on when called.
prev_end_char, // Character at end of current line.
curr_end_char, // Character at end of previous line.
indent_level; // Current indenting level.
string following_text, // All characters following the cursor.
curr_text; // The text of the current line.
/*
** Gather information on the two previous non-blank lines.
*/
if (!inq_mode ())
end_of_line ();
else if ((following_text = ltrim (read ())) != "")
{
/*
** If there are non-whitespace characters following
** the cursor, we save them in following_text. These
** characters are re-inserted after the new indent column
** has been determined.
*/
following_text = substr (following_text, 1, strlen (following_text) - 1);
delete_to_eol ();
}
/*
** We now search back for the previous line, skipping over
** all intervening comments, compiler directives and labels.
** The cursor is left one character after the character we're
** interested in.
*/
prev_end_char = curr_end_char = ';';
prev_indent_col = 1;
inq_position (curr_line, curr_col);
search_back (C_SKIP_PAT, -2);
if (prev_char ())
{
string line_end = read (); // The text after the cursor on this line.
curr_end_char = atoi (substr (line_end, 1, 1), 0);
/*
** We trim the end of the preceding line to ensure that any
** spaces left by template expansion of else statements (or
** accidental user typing) are removed.
*/
delete_to_eol ();
/*
** If we're dealing with an open brace, later indenting code
** needs the entire text of the line. We retrieve that information
** here.
*/
if (curr_end_char == '{')
{
save_position ();
beginning_of_line ();
curr_text = trim (ltrim (read ())) + "{";
restore_position ();
}
insert (trim (line_end));
/*
** Now we move to the first non-blank character on the
** line, and get that line's indent column. We then search
** back for the previous line, which may or may not exist.
** If it doesn't exist, we assume it ends in a semi-colon --
** this ensures that the indent level doesn't change.
*/
beginning_of_line ();
next_char (strlen (read ()) - strlen (ltrim (read ())));
inq_position (NULL, curr_indent_col);
if (prev_char () && search_back (C_SKIP_PAT, -2) > 1)
{
prev_char ();
prev_end_char = atoi (read (1), 0);
if (.c_indent_first && .c_indent_open)
{
beginning_of_line ();
next_char (strlen (read ()) - strlen (ltrim (read ())));
inq_position (NULL, prev_indent_col);
}
}
}
move_abs (curr_line, curr_indent_col);
/*
** We've determined the last two non-blank lines' last characters
** as well as the column position of the first non-blank character.
** Now we position the cursor on the new line's proper level.
*/
if (curr_indent_col == 1)
{
if (!.c_indent_first && curr_end_char == '{')
curr_indent_col += distance_to_tab ();
}
else
{
int prev_char_known;
/*
** Some indenting methods need to know if we've
** actually recognized a given character at all. Rather
** than duplicate the compound if statements required, we
** set a variable here.
*/
switch (prev_end_char)
{
case ';':
case '{':
case '}':
case ':':
++prev_char_known;
case ')':
--prev_char_known;
}
switch (curr_end_char)
{
/*
** If the current line ends with a semicolon, we make
** an indenting decision based on the previous line. If
** it ends with a "known" character, we leave the indenting
** level as is. Otherwise, we assume we've hit the end of
** an indented clause and outdent.
*/
case ';':
{
if (prev_char_known <= 0)
--curr_indent_col;
}
/*
** If close braces are supposed to be indented, we
** outdent after encountering one. Otherwise, we stay
** at the same level.
*/
case '}':
{
if (.c_indent_close)
--curr_indent_col;
}
/*
** The rule for open braces is the opposite of that for
** close braces. If they're supposed to be indented, we
** leave the indenting level the same (since we've already
** indented.
**
** There is a special case, however. If the open brace
** is placed on the same line as the statement, we want to
** indent the block. So, we assume that if the open brace
** isn't the only text on the line, we're dealing with this
** case, and should indent.
*/
case '{':
{
if (!.c_indent_open || (curr_text != "{" && !(.c_indent_first && prev_indent_col == 1)))
curr_indent_col += distance_to_tab ();
}
/*
** Finally, if we didn't recognize the end of the
** previous line, we assume we're supposed to indent. This
** ends up indenting for statement continuations, case
** statements, if statements, etc.
*/
default:
{
if (prev_char_known)
curr_indent_col += distance_to_tab ();
}
}
}
/*
** Here we move the cursor back to the original
** column we started at (where the newline should go).
** We then execute the correct command for the pressed
** key. If we were assigned to "<Enter>", we insert the
** Enter keystroke, thereby ensuring that overstrike and
** insert modes are handled properly. If not, we insert
** the keystroke for Ctrl-m, which always inserts a newline.
*/
move_abs (0, curr_col);
/*
** Since <Enter> actually differently in insert and overstrike modes,
** we try to ensure that we do the right thing based on the command that's
** running.
**
** We assume that a direct assignment to .c_indent means we should
** use "normal" <Enter> processing: otherwise, we assume we're assigned
** to something like open_line, and use <Ctrl-m>.
*/
if (!indent_query)
{
if (inq_command () == ".c_indent")
self_insert (key_to_int ("<Enter>"));
else
self_insert (key_to_int ("<Ctrl-m>"));
}
/*
** We now move the cursor to the new indenting level on
** the next line, and we retain the indenting "level" so that
** we can return it to the calling function.
*/
beginning_of_line ();
curr_col = distance_to_tab () + 1;
while (curr_col <= curr_indent_col)
{
move_abs (0, curr_col);
++indent_level;
curr_col += distance_to_tab ();
}
/*
** Finally, if we split the original line, we insert the
** characters that were following the cursor here. They're
** inserted, of course, at the new, indented column.
*/
save_position ();
insert (following_text);
restore_position ();
returns (indent_level);
}
/*
** r_indent:
**
** This macro does "standard" style indenting, indenting new lines
** to the same column as the previous non-blank line.
*/
void r_indent (void)
{
int curr_indent_col,
curr_line,
curr_col;
string following_text;
/*
** First, check to see if there are any following characters
** on the line. If so, read them into a temporary variable and
** delete from the first line. Then, get the column of the first
** non-blank line.
*/
if (!inq_mode ())
end_of_line ();
else if ((following_text = ltrim (read ())) != "")
{
/*
** If there are non-whitespace characters following
** the cursor, we save them in following_text. These
** characters are re-inserted after the new indent column
** has been determined.
*/
following_text = substr (following_text, 1, strlen (following_text) - 1);
delete_to_eol ();
}
inq_position (curr_line, curr_col);
if (search_back ("<*\\c[~ \\t\\n]"))
inq_position (NULL, curr_indent_col);
else
curr_indent_col = 1;
move_abs (curr_line, curr_col);
/*
** We've determined the last non-blank lines' indent level --
** do mode-sensitive return, indent, line split, and clean up.
*/
if (inq_command () == "r_indent")
self_insert (key_to_int ("<Enter>"));
else
self_insert (key_to_int ("<Ctrl-m>"));
move_abs (0, curr_indent_col);
insert (following_text);
move_abs (0, curr_indent_col);
}
/*
** slide_in:
**
** This macros slides a marked block of text in one tab stop per press
** of the tab key (-->|).
*/
void slide_in (void)
{
int start_line,
start_col,
end_line,
mark_type,
tab_key = key_to_int ("<Tab>");
if (mark_type = inq_marked (start_line, start_col, end_line))
{
int cursor_line,
cursor_col,
mark_line,
mark_col,
num_lines,
old_mode;
inq_position (cursor_line, cursor_col);
swap_anchor ();
inq_position (mark_line, mark_col);
if (mark_type != COL_MARK)
start_col = 1;
move_abs (start_line, start_col);
raise_anchor ();
old_mode = inq_mode ();
insert_mode (1);
while (start_line <= end_line)
{
search_fwd ("[~ \t]");
/*
** Note that self_inserting the complete
** key definition ensures the Tab is converted
** to spaces if -t is used.
*/
if (read (1) != "\n")
self_insert (tab_key);
move_abs (++start_line, start_col);
}
move_abs (mark_line, mark_col);
drop_anchor (mark_type);
move_abs (cursor_line, cursor_col);
insert_mode (old_mode);
}
else
self_insert (tab_key);
}
/*
** slide_out:
**
** This macros slides a marked block of text out one tab stop per press
** of the back-tab key (|<--).
*/
void slide_out (void)
{
int start_line,
start_col,
end_line,
mark_type;
if (mark_type = inq_marked (start_line, start_col, end_line))
{
int num_lines;
save_position ();
if (mark_type != COL_MARK)
start_col = 1;
move_abs (start_line, start_col);
while (start_line <= end_line)
{
int curr_col;
search_fwd ("[~ \t]");
inq_position (NULL, curr_col);
if (curr_col > start_col)
{
drop_anchor (4);
back_tab ();
inq_position (NULL, curr_col);
if (curr_col < start_col)
move_abs (0, start_col);
delete_block ();
}
move_abs (++start_line, start_col);
}
restore_position ();
}
else
back_tab ();
}
/*
** Template editing macros:
**
** These macro performs simple template editing for C programs. Each
** time the space bar is pressed, the characters before the cursor are
** checked to see if they are "if", "else if", "while", "for", "do",
** "switch" or "case" (or abbreviations for them). These keywords must
** only be preceded with spaces or tabs, and be typed at the end of a
** line. If a match is found, the remainder of the statement is filled
** in automatically.
**
** In addition, a brace pairer is included -- this automatically
** inserts a matching brace at the proper indentation level when an
** opening brace is typed in. To insert a brace without a matching
** brace (it attempts to be smart about matching braces, but you never
** can make this type of thing quite smart enough), type either Ctrl-{
** or quote the brace with Alt-q.
*/
/*
** .c_abbrev:
**
** This function checks to see if the characters before the space just
** typed (and actually inserted by this function) are destined to be
** followed by the remainder of a C construct.
*/
void .c_abbrev (void)
{
int done;
if (read (1) == "\n")
{
int loc;
string line;
save_position ();
beginning_of_line ();
line = trim (ltrim (read ()));
restore_position ();
if (!index (" \t\n", substr (line, 1, 1)) && (strlen (line) && strlen (line) < 8))
{
int length;
string abbrev_list = ABBREV_LIST_1;
line = "\xff" + line;
if (.c_min_abbrev < strlen (line)
&& ((loc = search_string (line, abbrev_list, length, 0))
|| (loc = search_string (line, abbrev_list = ABBREV_LIST_2, length, 0))))
{
string completion;
completion = substr (abbrev_list, loc + length);
completion = substr (completion, 1, index (completion, "\xff") - 1);
if (line + completion == "\xffei")
{
left ();
delete_char ();
completion = "lse if ()";
}
done = strlen (completion);
insert (completion);
if (line + completion == "\xffdo")
{
open_line ();
brace_expand ();
save_position ();
move_rel (1, 0);
open_line ();
insert ("while ();");
restore_position ();
done++;
}
/*
** Only back up if the last character of the completion is
** ) or :.
*/
else if (strlen (completion) && index (":)", substr (completion, strlen (completion))))
prev_char ();
}
}
}
if (!done)
if (inq_local_keyboard () == .c_alt_template)
slide_in ();
else
self_insert ();
}
/*
** brace_expand:
**
** This function checks to see if the typed brace should be indented
** and matched to the current indenting level.
*/
void brace_expand (void)
{
string line;
save_position ();
beginning_of_line ();
line = read ();
restore_position ();
.c_open_brace ();
if (search_string ("<|{{else}|{do}}|{{typedef}|{struct}[ \t]@[~ \t\n]@}|[):][ \t]@>", line) && "" == trim (ltrim (read ())))
{
int col;
inq_position (NULL, col);
.c_indent ();
save_position ();
if (col == 2)
insert ("\n}");
else
{
inq_position (NULL, col);
insert ("\n");
if (.c_indent_close == 1)
{
move_abs (0, col);
insert ("}");
}
else
.c_close_brace ();
}
restore_position ();
}
}
/*
** .c_open_brace:
**
** This function inserts an open brace at the correct column, given the
** current indenting conventions.
*/
void .c_open_brace (void)
{
save_position ();
beginning_of_line ();
if (trim (ltrim (read ())) == "")
{
restore_position ();
.c_indent (1);
if (!.c_indent_open)
back_tab ();
else if (.c_indent_first)
{
int curr_col;
inq_position (NULL, curr_col);
if (curr_col == 1)
move_rel (0, distance_to_tab ());
}
}
else
restore_position ();
self_insert ('{');
}
/*
** .c_close_brace:
**
** This function inserts a close brace at the correct column, given the
** current indenting conventions.
*/
void .c_close_brace (void)
{
save_position ();
beginning_of_line ();
if ("" == trim (ltrim (read ())))
{
restore_position ();
if (!.c_indent_first && .c_indent (1) == 1 || !.c_indent_close)
back_tab ();
}
else
restore_position ();
self_insert ('}');
}
/*
** just_brace, just_cbrace, just_space:
**
** These functions insert unexpanded and expanded braces and spaces.
*/
void just_brace (void)
{
insert ("{");
}
void just_cbrace (void)
{
insert ("}");
}
void just_space (void)
{
insert (" ");
}
/*
** comment_block:
**
** This macro comments out a block of code. It tries to be intelligent
** about things like comments inside the block -- if it finds one, it
** escapes it and moves on.
**
** Two different types of blocks are created, depending on the cursor
** start and end positions. If both are in column one, comment_block
** assumes a "block" type comment is desired. Block comments have "**"
** inserted in rows other than the first and last.
**
** If the start and end columns are not both in column one, the area is
** simply bracketed with "/*" and "* /".
*/
void comment_block (void)
{
int start_line,
start_col,
end_line,
end_col,
curr_col;
if (inq_marked (start_line, start_col, end_line, end_col))
{
string pattern;
raise_anchor ();
save_position ();
move_abs (end_line, end_col);
insert ("\xff");
move_abs (start_line, start_col);
if (start_col == 1)
insert ("/* ");
else
insert (" /* ");
if (start_col == 1 && end_col == 1)
pattern = "{/\\*}|{\\*/}|{\\*\\*}|<|\xff";
else
pattern = "{/\\*}|{\\*/}|{\\*\\*}|\xff";
while (search_fwd (pattern) && "\xff" != read (1))
{
inq_position (NULL, curr_col);
if (curr_col == 1 && (start_col == 1 && end_col == 1))
insert ("**\t");
else
{
insert ("\\");
right ();
}
}
delete_char ();
if (end_col == 1)
insert ("*/ ");
else
insert (" */ ");
restore_position ();
}
else
error ("No marked block.");
}
/*
** uncomment_block:
**
** This routine uncomments a block of code that was commented out by
** comment_block. It removed the comment and leading "**" characters (if
** appropriate), and restores the internal comments to their original,
** un-escaped state.
*/
void uncomment_block (void)
{
int start_line,
start_col,
end_line,
end_col;
if (inq_marked (start_line, start_col, end_line, end_col))
{
raise_anchor ();
save_position ();
move_abs (end_line, end_col);
insert ("\xff");
move_abs (start_line, start_col);
while (read (1) != "\xff")
{
search_fwd ("{</\\* }|{<\\*/ }|{ /\\* }|{<\\*\\*\t}|{ \\*/ }|{\\\\\\*}|{\\\\/\\*}|\xff");
if (read (1) != "\xff")
{
if (read (1) == "\\")
{
delete_char ();
right ();
}
else
{
drop_anchor ();
if (read (1) != " " || read (2) == "**")
move_rel (0, 2);
else
move_rel (0, 3);
delete_block ();
}
}
}
delete_char ();
restore_position ();
}
else
error ("No marked block.");
}