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>
1737 lines
45 KiB
Plaintext
1737 lines
45 KiB
Plaintext
/*
|
|
** BRIEF -- Basic Reconfigurable Interactive Editing Facility
|
|
**
|
|
** Written by Dave Nanian and Michael Strickman.
|
|
**
|
|
** History:
|
|
** 12/10/90 Jim Rodriquez - move assign_to_keys from _init macro
|
|
** to smart_first macro to conserve
|
|
** memory.
|
|
*/
|
|
|
|
/*
|
|
**
|
|
** cobol.cb:
|
|
**
|
|
** Smart indenting and template editing for COBOL. The smart
|
|
** indenter understands (within reasonable limits) code that
|
|
** conforms to both the 1985 and 1974 ANSI standards. The template
|
|
** system fully supports structured constructs from COBOL 85, and
|
|
** their use is encouraged. Line continuation is not currently
|
|
** supported.
|
|
**
|
|
** Keywords may be in upper, lower, or mixed case, as in COBOL 85.
|
|
**
|
|
** This package assumes that there are no literal tab characters
|
|
** in your COBOL source files. Since COBOL compilers are sensitive
|
|
** to the column position of certain characters and keywords, and
|
|
** tabs may take up varying numbers of columns, you must convert
|
|
** existing tab characters in your code to the proper number of
|
|
** spaces, before the indenting will work correctly.
|
|
**
|
|
** You may continue to use tab characters for non-COBOL files.
|
|
** The package will switch tabs on and off, as needed, when you
|
|
** switch from one file to another.
|
|
**
|
|
*/
|
|
|
|
#define FALSE 0
|
|
#define TRUE 1
|
|
|
|
/*
|
|
** The following defines are utilized for COBOL with sequence numbers
|
|
*/
|
|
|
|
#define COB_SKIP_PAT74 "<??????[~\\-+\n] @\\c[~ ]"
|
|
#define COB_MATCH_PAT74 "<??????[~\\-+\n] @\\c{END-}@%s[ .\n]"
|
|
#define COB_CMT_PAT74 "<??????\\*[*=\\->]@ @\\c[~ *\n]"
|
|
#define COB_CMT_COL74 7 // Comment char is in col 7.
|
|
#define COB_TEXT_COL74 8 // Text starts in col 8.
|
|
#define COB_CMT_CONT_COL74 10 // Continue comments at col 10.
|
|
#define COB_PIC_COL74 48 // Line up PIC clauses at col 48.
|
|
|
|
/*
|
|
** The following defines are utilized for COBOL without sequence numbers
|
|
*/
|
|
|
|
#define COB_SKIP_PAT85 "<[~\\-+*/\n] @\\c[~ ]"
|
|
#define COB_MATCH_PAT85 "<[~\\-+*/\n] @\\c{END-}@%s[ .\n]"
|
|
#define COB_CMT_PAT85 "<\\*[*=\\->]@ @\\c[~ *\n]"
|
|
#define COB_CMT_COL85 1
|
|
#define COB_TEXT_COL85 2
|
|
#define COB_CMT_CONT_COL85 4
|
|
#define COB_PIC_COL85 42
|
|
|
|
|
|
/*
|
|
** These defines are used by both ANSI '74 and ANSI '85.
|
|
*/
|
|
|
|
#define COB_RIGHT_MARGIN 73
|
|
#define COB_CMT_MARGIN 81
|
|
#define COB_DIVISION 8 // Column in which divisions should be placed
|
|
#define COB_CMT_CHAR "*" // Comment character is *.
|
|
#define COB_EJECT_CHAR "/" // Page break character.
|
|
|
|
/*
|
|
** Note: the "!" found after the two letter abbreviations (i.e. AG, DR,
|
|
** DG, etc) is an internal convention in order to ensure that the
|
|
** abbreviation's length is less than the completion length. Thus,
|
|
** the user does NOT have to type in the "!" to ensure the two letter
|
|
** abbreviations' expansions.
|
|
*/
|
|
|
|
#define ABBR_LIST_1 "~ADD~AG!~AE!~CALL~CLOSE~COMPUTE~DISPLAY~DATA~DELETE~DIVIDE~DG!~DR!~DF!~ELSE~ENVIRONMENT~EVALUATE~EXIT~EC!"
|
|
#define ABBR_LIST_2 "~GIVING~IF~IDENTIFICATION~MOVE~MULTIPLY~MG!~OPEN~PERFORM~PROCEDURE~READ~REWRITE~RETURN~SET~SEARCH~SELECT~"
|
|
#define ABBR_LIST_3 "~STRING~SUBTRACT~SG!~THRU~UNTIL~UNSTRING~VARYING~WHEN~WRITE~"
|
|
|
|
/*
|
|
** Certain keywords are not capitalized when in mixed case sensitivities.
|
|
*/
|
|
|
|
#define NO_CAPS "~BY~DELIMITED~FROM~GIVING~INTO~REMAINDER~TO~USING~"
|
|
|
|
#define ADD_CHAR_MACRO 0
|
|
#define MIN_ABBREV 1
|
|
#define MAX_COL 20736
|
|
#define NEG_MAX_COL -20736
|
|
|
|
#define CODE_ALIGN 0 // Where to indent comments.
|
|
#define CMT_ALIGN 1
|
|
#define COL_ALIGN 2
|
|
|
|
#define NO_SEQ_NUM 0 // Sequence numbers?
|
|
#define SEQ_NUM 1
|
|
|
|
#define KW_LOWER 0
|
|
#define KW_UPPER 1
|
|
#define KW_MIXED 2
|
|
|
|
/*
|
|
** Function Prototypes
|
|
*/
|
|
|
|
extern void slide_in ();
|
|
extern void open_line ();
|
|
extern int .c_previous_word ();
|
|
extern int .c_next_word ();
|
|
|
|
string .cob_smart_first (~int, ~int);
|
|
string .cob_smart_on ();
|
|
string .cob_template_first (~int, ~int, ~int, ~int);
|
|
string .cob_template_on ();
|
|
void .cob_smart_off ();
|
|
int .cob_indent (~int);
|
|
int .cob_outdent_to_match (string, int);
|
|
int .cob_last_stmt_level (int);
|
|
int .cob_is_level_number (string);
|
|
void .cob_first_nonwhite ();
|
|
int .cob_indent_level (~int);
|
|
void .cob_reindent (int);
|
|
int .cob_is_comment ();
|
|
void .cob_comment ();
|
|
void .cob_stmt_wrap ();
|
|
int .cob_split_stmt (int);
|
|
void .cob_abbrev ();
|
|
void .cob_expand_pair (string, string);
|
|
void .cob_reposition_keyword ();
|
|
void .cob_expand_block (string);
|
|
string .cob_keyword_cvt (string);
|
|
int .cob_next_word ();
|
|
int .cob_previous_word ();
|
|
void .cob_align ();
|
|
|
|
/*
|
|
** Allocate the global variables and set up the keymaps.
|
|
*/
|
|
|
|
int _cob_smart,
|
|
_cob_template,
|
|
_cob_alt_template,
|
|
_cob_min_abbrev,
|
|
_cob_comment_align,
|
|
_cob_old_fill,
|
|
_cob_ansi_85,
|
|
_cob_seq_num,
|
|
_cob_keyword_case;
|
|
|
|
|
|
/*
|
|
** Turn on smart indenting for COBOL. This macro is designed to
|
|
** be run the first time a file is edited, but may also be run from
|
|
** the command line.
|
|
**
|
|
** Parameters:
|
|
**
|
|
** 0 -- Controls whether or not columns 1-6 should be used for
|
|
** sequence numbers.
|
|
**
|
|
** 1 -- Controls where the cursor is left when we are continuing
|
|
** an existing comment. Value of 0 means to leave the cursor at the
|
|
** current indent level (flush with the last line of code). 1
|
|
** means to leave it flush with the indent level of the last comment.
|
|
** 2 means to leave it at COB_CMT_CONT_COL??. Warning: a value of 1
|
|
** here will make indenting for comments slightly slower.
|
|
**
|
|
** 2 -- Whether or not the ANSI '85 standard is supported. If this
|
|
** is 0, ANSI '74 is assumed.
|
|
**
|
|
** Defaults: 0 0 1
|
|
*/
|
|
|
|
string .cob_smart_first (~int, ~int)
|
|
{
|
|
_cob_seq_num = NO_SEQ_NUM;
|
|
_cob_comment_align = CODE_ALIGN;
|
|
_cob_ansi_85 = TRUE;
|
|
get_parm (0, _cob_seq_num);
|
|
get_parm (1, _cob_comment_align);
|
|
get_parm (2, _cob_ansi_85);
|
|
|
|
if (first_time())
|
|
{
|
|
keyboard_push ();
|
|
assign_to_key ("<Enter>", ".cob_indent");
|
|
assign_to_key ("<Tab>", "slide_in");
|
|
assign_to_key ("<Shift-Tab>", "slide_out");
|
|
_cob_smart = inq_keyboard ();
|
|
keyboard_pop (1);
|
|
}
|
|
use_local_keyboard (_cob_smart);
|
|
returns ("");
|
|
}
|
|
|
|
/*
|
|
** Turn on smart indenting for COBOL. This macro is designed to
|
|
** be run every time a COBOL file is edited. It turns on statement
|
|
** wrap for COBOL, and forces empty areas to be filled with spaces.
|
|
*/
|
|
|
|
string .cob_smart_on ()
|
|
{
|
|
register_macro (ADD_CHAR_MACRO, ".cob_stmt_wrap");
|
|
_cob_old_fill = "y" == use_tab_char ("n");
|
|
returns (".cob_smart_off");
|
|
}
|
|
|
|
/*
|
|
** Turn on template editing for COBOL. This macro is designed to
|
|
** be run the first time a file is edited, but may also be run from
|
|
** the command line.
|
|
**
|
|
** Parameters:
|
|
** 0, 1, 2 -- Same as .cob_smart_first.
|
|
**
|
|
** 3 -- The minimum prefix length required for template
|
|
** expansion. Set this parameter to 0 if you want to selectively
|
|
** expand templates by pressing <Tab>.
|
|
**
|
|
** 4 -- The preferred case for verbs that are expanded. Set
|
|
** this to 0 for lower case, 1 for upper case, and 2 for mixed
|
|
** case (initial caps). This option is ignored if you are not
|
|
** using COBOL 85.
|
|
**
|
|
** Defaults: 0 0 1 1 1
|
|
*/
|
|
|
|
string .cob_template_first (~int, ~int, ~int, ~int)
|
|
{
|
|
_cob_seq_num = NO_SEQ_NUM;
|
|
_cob_comment_align = CODE_ALIGN;
|
|
_cob_ansi_85 = TRUE;
|
|
_cob_min_abbrev = MIN_ABBREV;
|
|
_cob_keyword_case = KW_UPPER;
|
|
|
|
get_parm (0, _cob_seq_num);
|
|
get_parm (1, _cob_comment_align);
|
|
get_parm (2, _cob_ansi_85);
|
|
get_parm (3, _cob_min_abbrev);
|
|
|
|
if (_cob_ansi_85)
|
|
get_parm (4, _cob_keyword_case);
|
|
|
|
if (first_time())
|
|
{
|
|
keyboard_push ();
|
|
assign_to_key ("<Enter>", ".cob_indent");
|
|
assign_to_key ("<Tab>", ".cob_abbrev");
|
|
assign_to_key ("<Shift-Tab>", "slide_out");
|
|
assign_to_key ("<Ctrl-s>", "just_space");
|
|
_cob_alt_template = inq_keyboard ();
|
|
keyboard_pop (1);
|
|
keyboard_push ();
|
|
assign_to_key ("<Enter>", ".cob_indent");
|
|
assign_to_key ("<Tab>", "slide_in");
|
|
assign_to_key ("<Shift-Tab>", "slide_out");
|
|
assign_to_key ("<Space>", ".cob_abbrev");
|
|
assign_to_key ("<Ctrl-s>", "just_space");
|
|
_cob_template = inq_keyboard ();
|
|
keyboard_pop (1);
|
|
}
|
|
if (_cob_min_abbrev == 0)
|
|
{
|
|
use_local_keyboard (_cob_alt_template);
|
|
_cob_min_abbrev = 1;
|
|
}
|
|
else
|
|
use_local_keyboard (_cob_template);
|
|
|
|
returns ("");
|
|
}
|
|
|
|
/*
|
|
** When template editing is on, certain functions must be activated
|
|
** every time we switch to a COBOL file.
|
|
*/
|
|
|
|
string .cob_template_on ()
|
|
{
|
|
returns (.cob_smart_on ());
|
|
}
|
|
|
|
/*
|
|
** Some functions must be deactivated every time we leave a COBOL file.
|
|
*/
|
|
|
|
void .cob_smart_off ()
|
|
{
|
|
unregister_macro (ADD_CHAR_MACRO, ".cob_stmt_wrap");
|
|
use_tab_char (_cob_old_fill ? "y" : "n");
|
|
}
|
|
|
|
/*
|
|
** These definitions are used as Cobol "language sensitive" word patterns
|
|
*/
|
|
int .cob_next_word ()
|
|
{
|
|
returns (.c_next_word ());
|
|
}
|
|
|
|
int .cob_previous_word ()
|
|
{
|
|
returns (.c_previous_word ());
|
|
}
|
|
|
|
|
|
/*
|
|
** .cob_indent:
|
|
**
|
|
** This macro does syntax-sensitive indenting ("smart indenting") for
|
|
** COBOL language files.
|
|
**
|
|
** Parameters:
|
|
** 0 -- If FALSE, forces split mode (see below). If TRUE, forces
|
|
** non-split mode. If omitted, split mode depends on whether
|
|
** or not BRIEF is in insert mode.
|
|
**
|
|
** Returns:
|
|
** If the line was split, the number of whitespace
|
|
** characters trimmed from the middle.
|
|
*/
|
|
|
|
int .cob_indent (~int)
|
|
{
|
|
int curr_line, // Line cursor is on when called.
|
|
code_line, // Line number for last code.
|
|
code_indent_level, // Current indent level.
|
|
code_indent_col, // Current indent column.
|
|
code_trail, // Last code char on code line.
|
|
prev_indent_level, // Indent level of last line.
|
|
prev_trail, // Last code char before code line.
|
|
is_comment, // Is this line a comment?
|
|
is_para, // Is this line a paragraph name?
|
|
split_mode, // Should we insert a newline?
|
|
scratch; // Scratch integer.
|
|
|
|
string following_string, // Remainder of line being split.
|
|
code_text, // The trimmed version of the line.
|
|
prev_text, // The trimmed text of the previous line.
|
|
token; // First token in code_text.
|
|
|
|
/*
|
|
** Initialize. The default indent level is 0, which puts the
|
|
** cursor at COB_TEXT_COL??.
|
|
*/
|
|
|
|
inq_position (curr_line);
|
|
code_indent_col = _cob_seq_num ? COB_TEXT_COL74 : COB_TEXT_COL85;
|
|
code_trail = prev_trail = '.';
|
|
|
|
/*
|
|
** .cob_indent has two modes.
|
|
**
|
|
** Both modes will reindent the current line if necessary and
|
|
** position the cursor correctly on the following line.
|
|
**
|
|
** When BRIEF is in insert mode, or this macro is called from
|
|
** open_line, .cob_indent will add a new line to the buffer. The
|
|
** contents depend on the cursor position when .cob_indent is
|
|
** called: if the cursor is at the end of the line, the new line
|
|
** will be blank, but if not, the old line will be split in two.
|
|
** If the old line is a comment, the new line will be a comment,
|
|
** and if the old line is code, the new line will be code.
|
|
**
|
|
** When BRIEF is in overstrike mode and the macro was not called
|
|
** by open_line, BRIEF does not add a new line. Note that open_line
|
|
** doesn't call the macro directly, but calls it via key assignment,
|
|
** which makes life difficult for us.
|
|
**
|
|
** You can pass an integer parameter to .cob_indent to force
|
|
** it into either mode. If the parameter is FALSE, .cob_indent
|
|
** will split; if it's TRUE, it won't.
|
|
*/
|
|
|
|
split_mode = inq_mode () || inq_command () == "open_line";
|
|
|
|
if (get_parm (0, scratch))
|
|
split_mode = !scratch;
|
|
|
|
/*
|
|
** If we're splitting, and not at the end of the line, we save
|
|
** the end of the line (minus its newline) and delete it. Make
|
|
** sure we handle splits in the prefix area correctly.
|
|
*/
|
|
|
|
if (split_mode && read (1) != "\n")
|
|
{
|
|
inq_position (NULL, scratch);
|
|
|
|
if (scratch < (_cob_seq_num ? COB_TEXT_COL74 : COB_TEXT_COL85))
|
|
move_abs (0, (_cob_seq_num ? COB_TEXT_COL74 : COB_TEXT_COL85));
|
|
|
|
following_string = read ();
|
|
following_string = substr (following_string, 1, strlen (following_string) - 1);
|
|
delete_to_eol ();
|
|
}
|
|
else
|
|
end_of_line ();
|
|
|
|
/*
|
|
** Find the beginning of the last code statement, and remember
|
|
** the text of the line, the first token, and the indent level.
|
|
*/
|
|
|
|
beginning_of_line ();
|
|
scratch = curr_line;
|
|
|
|
if (search_back ((_cob_seq_num ? COB_SKIP_PAT74 : COB_SKIP_PAT85), 1))
|
|
{
|
|
inq_position (code_line, code_indent_col);
|
|
code_indent_level = .cob_indent_level ();
|
|
code_text = upper (trim (read ()));
|
|
token = substr (code_text, 1, index (code_text + " ", " ") - 1);
|
|
code_trail = atoi (substr (code_text, strlen (code_text)), FALSE);
|
|
|
|
/*
|
|
** If we found one line of code, we look back for another.
|
|
*/
|
|
|
|
beginning_of_line ();
|
|
|
|
if (up () && search_back ((_cob_seq_num ? COB_SKIP_PAT74 : COB_SKIP_PAT85), 1))
|
|
{
|
|
prev_indent_level = .cob_indent_level ();
|
|
prev_text = upper (trim (read ()));
|
|
prev_trail = atoi (substr (prev_text, strlen (prev_text)), FALSE);
|
|
}
|
|
}
|
|
|
|
/*
|
|
** Figure out if the code is a paragraph name. Our working
|
|
** definition of a paragraph name is a single token (no spaces)
|
|
** preceded and followed by a period. The keywords EXIT, GOBACK,
|
|
** END- are exceptions.
|
|
*/
|
|
|
|
if (prev_trail == '.' && (token == code_text && ((substr (token, 1, 4) != "END-" && strlen (token))
|
|
&& (token != "EXIT." && token != "GOBACK."))))
|
|
is_para = TRUE;
|
|
|
|
move_abs (curr_line, 1);
|
|
|
|
/*
|
|
** If code_line is equal to curr_line, the line is a valid
|
|
** statement; if not, it's either a comment, an old-style
|
|
** continuation line (not supported) or syntactically invalid.
|
|
*/
|
|
|
|
if (code_line == curr_line)
|
|
|
|
/*
|
|
** If the statement began with an ELSE, it must be
|
|
** outdented to the matching IF. WHEN must be paired
|
|
** with a matching EVALUATE, then indented one level.
|
|
** THRU, VARYING, and UNTIL must be indented one level
|
|
** from the previous PERFORM.
|
|
*/
|
|
|
|
switch (token)
|
|
{
|
|
case "ELSE":
|
|
{
|
|
sprintf (prev_text, (_cob_seq_num ? COB_MATCH_PAT74 : COB_MATCH_PAT85), "IF");
|
|
.cob_outdent_to_match (prev_text, code_indent_level);
|
|
}
|
|
|
|
case "WHEN":
|
|
{
|
|
sprintf (prev_text, (_cob_seq_num ? COB_MATCH_PAT74 : COB_MATCH_PAT85), "EVALUATE");
|
|
.cob_outdent_to_match (prev_text, code_indent_level);
|
|
.cob_reindent (++code_indent_level);
|
|
}
|
|
case "THRU":
|
|
case "VARYING":
|
|
case "UNTIL":
|
|
switch (substr (prev_text, 1, index (prev_text + " ", " ") - 1))
|
|
{
|
|
case "PERFORM":
|
|
.cob_reindent (code_indent_level = prev_indent_level + 1);
|
|
|
|
case "THRU":
|
|
case "VARYING":
|
|
case "UNTIL":
|
|
if (code_indent_level != prev_indent_level)
|
|
.cob_reindent (code_indent_level = prev_indent_level);
|
|
}
|
|
/*
|
|
** FD, RD, and SD lines must be indented to level 0.
|
|
*/
|
|
|
|
case "FD":
|
|
case "RD":
|
|
case "SD":
|
|
.cob_reindent (code_indent_level = 0);
|
|
|
|
/*
|
|
** If the line begins with an END- keyword, we reindent
|
|
** it to the last matching keyword (for example, END-IF
|
|
** is aligned with the last IF). This matching will work
|
|
** fine as long as you consistently use END- keywords,
|
|
** rather than periods, to terminate nested constructs.
|
|
*/
|
|
|
|
default:
|
|
if (substr (token, 1, 4) == "END-")
|
|
{
|
|
prev_text = substr (token, 5);
|
|
|
|
if (substr (prev_text, strlen (prev_text)) == ".")
|
|
prev_text = substr (prev_text, 1, strlen (prev_text) - 1);
|
|
sprintf (prev_text, (_cob_seq_num ? COB_MATCH_PAT74 : COB_MATCH_PAT85), prev_text);
|
|
.cob_outdent_to_match (prev_text, code_indent_level);
|
|
}
|
|
|
|
/*
|
|
** If the line began with a number > 0, it may be a
|
|
** data description. These have special rules.
|
|
**
|
|
** Levels 01 and 77 are not indented at all.
|
|
**
|
|
** Other levels may be reindented, depending on the
|
|
** level number and current indenting level of both
|
|
** the current line and the previous line. Of course,
|
|
** if the previous line is not a data description,
|
|
** we don't reindent.
|
|
**
|
|
** If the two lines have the same level numbers,
|
|
** they should be aligned.
|
|
**
|
|
** If the previous line has a lower number, the
|
|
** current line should be indented beyond it; if it
|
|
** already is, fine, but if it's not, we indent one
|
|
** level. The converse is true if the code line has
|
|
** a lower number.
|
|
*/
|
|
|
|
else if (scratch = .cob_is_level_number (code_text))
|
|
|
|
/*
|
|
** If the level number is 1 or 77, we verify it and
|
|
** reindent the line.
|
|
*/
|
|
|
|
if (scratch == 1 || scratch == 77)
|
|
.cob_reindent (code_indent_level = 0);
|
|
else
|
|
{
|
|
int prev_level;
|
|
|
|
/*
|
|
** Now we make sure both lines began with
|
|
** real level numbers.
|
|
*/
|
|
|
|
if (prev_level = .cob_is_level_number (prev_text))
|
|
/*
|
|
** Now we reindent if necessary.
|
|
*/
|
|
{
|
|
scratch -= prev_level;
|
|
prev_level = code_indent_level;
|
|
|
|
if (!scratch)
|
|
code_indent_level = prev_indent_level;
|
|
else if (scratch > 0)
|
|
{
|
|
if (code_indent_level <= prev_indent_level)
|
|
code_indent_level = ++prev_indent_level;
|
|
}
|
|
else if (code_indent_level >= prev_indent_level)
|
|
code_indent_level = --prev_indent_level;
|
|
|
|
if (code_indent_level != prev_level)
|
|
.cob_reindent (code_indent_level);
|
|
}
|
|
}
|
|
/*
|
|
** If the current statement was a division, section,
|
|
** or paragraph name (single token ending in a period)
|
|
** we reindent it to level 0 (margin A).
|
|
**
|
|
** We assume the period comes right after DIVISION
|
|
** or SECTION.
|
|
*/
|
|
|
|
else if (code_trail == '.')
|
|
switch (substr (code_text, rindex (" " + code_text, " ")))
|
|
|
|
{
|
|
case "DIVISION.":
|
|
case "SECTION.":
|
|
.cob_reindent (code_indent_level = 0);
|
|
|
|
default:
|
|
if (is_para || token == "PROCEDURE")
|
|
.cob_reindent (code_indent_level = 0);
|
|
}
|
|
}
|
|
/*
|
|
** Determine if the current line is a comment.
|
|
*/
|
|
|
|
else
|
|
is_comment = .cob_is_comment ();
|
|
|
|
/*
|
|
** Move to the next line, splitting if necessary.
|
|
*/
|
|
|
|
if (split_mode)
|
|
{
|
|
end_of_line ();
|
|
insert ("\n");
|
|
|
|
/*
|
|
** If the current line was a comment, the new line should
|
|
** also be a comment.
|
|
*/
|
|
|
|
if (is_comment)
|
|
{
|
|
move_abs (0, (_cob_seq_num ? COB_CMT_COL74 : COB_CMT_COL85));
|
|
insert (COB_CMT_CHAR);
|
|
}
|
|
}
|
|
else
|
|
move_rel (1, NEG_MAX_COL);
|
|
|
|
/*
|
|
** Compute the cursor position on the next line. If the current
|
|
** line was a comment, the cursor position is controlled by
|
|
** _cob_comment_align.
|
|
**
|
|
** If the current line was code, we start with the same level, but
|
|
** indent if the statement contained certain keywords.
|
|
*/
|
|
|
|
if (is_comment && _cob_comment_align != CODE_ALIGN)
|
|
{
|
|
code_indent_level = _cob_seq_num ? COB_CMT_CONT_COL74 : COB_CMT_CONT_COL85;
|
|
|
|
if (_cob_comment_align == CMT_ALIGN)
|
|
{
|
|
save_position ();
|
|
|
|
if (up () && search_back ((_cob_seq_num ? COB_CMT_PAT74 : COB_CMT_PAT85), 1))
|
|
inq_position (NULL, code_indent_level);
|
|
|
|
restore_position ();
|
|
}
|
|
code_indent_level = 0 - code_indent_level;
|
|
}
|
|
|
|
else
|
|
switch (code_trail)
|
|
{
|
|
case '.':
|
|
/*
|
|
** When the code line ended with a period, and the
|
|
** previous line ended with one too, we are entering
|
|
** single-line sentences that don't change the indenting
|
|
** level. The only special case is a paragraph title,
|
|
** which causes an automatic indent.
|
|
**
|
|
** If the previous line ended with something else,
|
|
** we know we just finished a dangling statement. The
|
|
** correct indent level can be found by looking
|
|
** back for the beginning of the statement.
|
|
*/
|
|
|
|
if (prev_trail == '.')
|
|
{
|
|
if (is_para)
|
|
++code_indent_level;
|
|
}
|
|
else
|
|
code_indent_level = .cob_last_stmt_level (code_line);
|
|
|
|
case ',':
|
|
/*
|
|
** We assume commas are used in parameter lists, and
|
|
** a line ending with a comma probably represents a
|
|
** parameter in a list. The column where the last token
|
|
** on the line begins is a good place to put the cursor
|
|
** on the next line. We assume there are no spaces
|
|
** after the last token on the line, and no tabs in
|
|
** the line.
|
|
*/
|
|
|
|
code_indent_level = 0 - (code_indent_col + (rindex (" " + code_text, " ") - 1));
|
|
|
|
default:
|
|
|
|
/*
|
|
** On lines that do not end with periods or commas, the
|
|
** next indent level is controlled by the first keyword on
|
|
** the line (if any).
|
|
**
|
|
** We indent a level following IF, ELSE, and EVALUATE.
|
|
** We outdent a level following THRU, VARYING, or UNTIL
|
|
** (when we use these in combination, the line becomes
|
|
** reindented properly when we press <Enter>).
|
|
*/
|
|
|
|
switch (token)
|
|
{
|
|
case "ELSE":
|
|
case "EVALUATE":
|
|
case "IF":
|
|
++code_indent_level;
|
|
|
|
case "THRU":
|
|
case "VARYING":
|
|
case "UNTIL":
|
|
if (code_indent_level)
|
|
--code_indent_level;
|
|
|
|
default:
|
|
|
|
switch (prev_trail)
|
|
{
|
|
case '.':
|
|
++code_indent_level;
|
|
|
|
case ',':
|
|
code_indent_level = .cob_last_stmt_level (code_line);
|
|
}
|
|
}
|
|
}
|
|
|
|
/*
|
|
** Move the cursor.
|
|
*/
|
|
|
|
move_abs (0, code_indent_level >= 0 ? .cob_indent_level (code_indent_level) : 0 - code_indent_level);
|
|
|
|
/*
|
|
** Reinsert any characters we split.
|
|
*/
|
|
|
|
scratch = 0;
|
|
|
|
if (following_string != "")
|
|
{
|
|
save_position ();
|
|
scratch = strlen (following_string);
|
|
insert (following_string = ltrim (following_string));
|
|
restore_position ();
|
|
scratch -= strlen (following_string);
|
|
}
|
|
returns (scratch);
|
|
}
|
|
|
|
/*
|
|
** Outdents an END- keyword line until it pairs up with a matching
|
|
** begin keyword.
|
|
**
|
|
** Parameters:
|
|
** 0 -- the search pattern.
|
|
** 1 -- the current indenting level.
|
|
**
|
|
** Returns:
|
|
** Nothing.
|
|
**
|
|
** Puts the new indenting level, if it changes, back into parameter 1.
|
|
*/
|
|
|
|
int .cob_outdent_to_match (string match_pattern, int curr_indent_level)
|
|
{
|
|
int nesting;
|
|
|
|
save_position ();
|
|
nesting = 1;
|
|
|
|
/*
|
|
** Repeat until nesting is zero, or until we can't find another
|
|
** keyword in the search pattern. END- keywords increase the
|
|
** nesting level; other matches of the search pattern decrease it.
|
|
*/
|
|
|
|
while (nesting)
|
|
{
|
|
move_abs (0, MAX_COL);
|
|
|
|
if (!(up () && search_back (match_pattern, 1, 0)))
|
|
{
|
|
restore_position ();
|
|
return (FALSE);
|
|
}
|
|
if (upper (read (4)) == "END-")
|
|
++nesting;
|
|
else
|
|
--nesting;
|
|
}
|
|
/*
|
|
** We are on the matching begin keyword, and we assume that it
|
|
** is the first keyword on the line so that we can determine
|
|
** the line's indenting level. If the line with the END-
|
|
** keyword is indented to another level, reindent, reusing nesting
|
|
** since we don't need it any more.
|
|
*/
|
|
|
|
.cob_first_nonwhite ();
|
|
nesting = .cob_indent_level ();
|
|
restore_position ();
|
|
|
|
if (nesting != curr_indent_level)
|
|
{
|
|
.cob_reindent (nesting);
|
|
put_parm (1, nesting);
|
|
}
|
|
}
|
|
|
|
/*
|
|
** Finds the beginning of the last statement before a specified line,
|
|
** and returns its indenting level (following the convention that a
|
|
** negative indenting level is an absolute column number). Looks for
|
|
** the last line to end in a period, and saves the indent level of
|
|
** each line until it finds it so that one pass over the file is
|
|
** sufficient.
|
|
**
|
|
** Parameters:
|
|
** 0 -- The first line to look at (the number passed is excluded
|
|
** from the scan, which begins one line above it in the buffer).
|
|
*/
|
|
|
|
int .cob_last_stmt_level (int code_line)
|
|
{
|
|
int indent_col;
|
|
|
|
string line_text;
|
|
|
|
if (code_line)
|
|
{
|
|
save_position ();
|
|
move_abs (code_line, 1);
|
|
|
|
while (up () && search_back ((_cob_seq_num ? COB_SKIP_PAT74 : COB_SKIP_PAT85), 1))
|
|
{
|
|
line_text = trim (read ());
|
|
|
|
if (substr (line_text, strlen (line_text)) == ".")
|
|
break;
|
|
|
|
inq_position (NULL, indent_col);
|
|
}
|
|
restore_position ();
|
|
}
|
|
returns (0 - indent_col);
|
|
}
|
|
|
|
/*
|
|
** Determines if a line begins with a two-digit number. If so, reads the
|
|
** number from the line and returns it. Otherwise returns zero.
|
|
*/
|
|
|
|
int .cob_is_level_number (string line)
|
|
{
|
|
int retval;
|
|
|
|
if (retval = atoi (line))
|
|
if (substr (line, 3, 1) != " ")
|
|
retval = FALSE;
|
|
else if (!index ("0123456789", substr (line, 2, 1)))
|
|
retval = FALSE;
|
|
|
|
returns (retval);
|
|
}
|
|
|
|
/*
|
|
** Moves the cursor to the first non-space character in the text area.
|
|
*/
|
|
|
|
void .cob_first_nonwhite ()
|
|
{
|
|
move_abs (0, (_cob_seq_num ? COB_TEXT_COL74 : COB_TEXT_COL85));
|
|
next_char (strlen (read ()) - strlen (ltrim (read ())));
|
|
}
|
|
|
|
/*
|
|
** Maps between indent levels (in tab stops) and column positions in a
|
|
** COBOL file. The mapping accounts for the seven reserved columns
|
|
** at the beginning of each line: indent level 0 extends from column
|
|
** COB_TEXT_COL?? to just before the next tab stop, etc.
|
|
**
|
|
** If a parameter is passed, we treat it as an indent level, and we
|
|
** calculate the column corresponding to it. Otherwise, we calculate
|
|
** the indent level corresponding to the current column.
|
|
*/
|
|
|
|
int .cob_indent_level (~int)
|
|
{
|
|
int curr_col,
|
|
level,
|
|
lev_to_col;
|
|
|
|
save_position ();
|
|
curr_col = _cob_seq_num ? COB_TEXT_COL74 : COB_TEXT_COL85;
|
|
|
|
if (get_parm (0, lev_to_col))
|
|
{
|
|
move_abs (0, (_cob_seq_num ? COB_TEXT_COL74 : COB_TEXT_COL85));
|
|
|
|
while (level < lev_to_col)
|
|
{
|
|
move_abs (0, curr_col += distance_to_tab ());
|
|
++level;
|
|
}
|
|
level = curr_col;
|
|
}
|
|
else
|
|
{
|
|
inq_position (NULL, lev_to_col);
|
|
move_abs (0, (_cob_seq_num ? COB_TEXT_COL74 : COB_TEXT_COL85));
|
|
|
|
while ((curr_col += distance_to_tab ()) <= lev_to_col)
|
|
{
|
|
move_abs (0, curr_col);
|
|
++level;
|
|
}
|
|
}
|
|
restore_position ();
|
|
returns (level);
|
|
}
|
|
|
|
/*
|
|
** Reindents the code part of a COBOL line to a specified level.
|
|
** If the line is already indented correctly, does nothing.
|
|
** Takes a single parameter, the level to reindent the line to.
|
|
*/
|
|
|
|
void .cob_reindent (int new_level)
|
|
{
|
|
.cob_first_nonwhite ();
|
|
|
|
if (new_level != .cob_indent_level ())
|
|
{
|
|
string temp;
|
|
|
|
temp = trim (read ());
|
|
move_abs (0, (_cob_seq_num ? COB_TEXT_COL74 : COB_TEXT_COL85));
|
|
delete_to_eol ();
|
|
move_abs (0, .cob_indent_level (new_level));
|
|
insert (temp);
|
|
.cob_stmt_wrap ();
|
|
}
|
|
}
|
|
|
|
/*
|
|
** Is a line a comment?
|
|
*/
|
|
|
|
int .cob_is_comment ()
|
|
{
|
|
int retval;
|
|
|
|
save_position ();
|
|
move_abs (0, (_cob_seq_num ? COB_CMT_COL74 : COB_CMT_COL85));
|
|
retval = COB_CMT_CHAR == read (1);
|
|
restore_position ();
|
|
returns (retval);
|
|
}
|
|
|
|
/*
|
|
** Toggle whether or not a line is a comment.
|
|
*/
|
|
|
|
void .cob_comment ()
|
|
{
|
|
save_position ();
|
|
move_abs (0, (_cob_seq_num ? COB_CMT_COL74 : COB_CMT_COL85));
|
|
|
|
if (.cob_is_comment ())
|
|
{
|
|
delete_char ();
|
|
insert (" ");
|
|
}
|
|
else
|
|
{
|
|
if (read (1) != "\n")
|
|
delete_char ();
|
|
|
|
insert (COB_CMT_CHAR);
|
|
}
|
|
restore_position ();
|
|
}
|
|
|
|
/*
|
|
** This registered macro checks to see if we have gone past column 72
|
|
** (for code lines) or column 80 (for comments) and breaks the line
|
|
** into two lines of the same type.
|
|
*/
|
|
|
|
void .cob_stmt_wrap ()
|
|
{
|
|
int col;
|
|
|
|
inq_position (NULL, col);
|
|
|
|
if (col > COB_RIGHT_MARGIN)
|
|
{
|
|
int split_col;
|
|
|
|
/*
|
|
** If the line's a comment, it may extend to column 80, so
|
|
** we just warn and return unless the line is even longer.
|
|
** If the line must still be split, we put the actual
|
|
** margin in split_col.
|
|
*/
|
|
|
|
if (.cob_is_comment ())
|
|
if (col <= COB_CMT_MARGIN)
|
|
{
|
|
beep ();
|
|
return;
|
|
}
|
|
else
|
|
split_col = COB_CMT_MARGIN;
|
|
else
|
|
split_col = COB_RIGHT_MARGIN;
|
|
|
|
/*
|
|
** Find the best split point for the line.
|
|
*/
|
|
|
|
col -= .cob_split_stmt (split_col);
|
|
|
|
/*
|
|
** Split the line. If spaces are trimmed from the line
|
|
** when we split it, compensate in the cursor positioning.
|
|
*/
|
|
|
|
col -= .cob_indent (FALSE);
|
|
|
|
/*
|
|
** Put the cursor back after the character that caused
|
|
** the split.
|
|
*/
|
|
|
|
next_char (col);
|
|
}
|
|
}
|
|
|
|
/*
|
|
** Find the best split point for a COBOL statement.
|
|
*/
|
|
|
|
int .cob_split_stmt (int max_split_col)
|
|
{
|
|
int split_col;
|
|
|
|
/*
|
|
** If we can't find a better splitting point, we will split
|
|
** the line at the last possible text character on the line.
|
|
*/
|
|
|
|
.cob_first_nonwhite ();
|
|
next_char ();
|
|
drop_anchor ();
|
|
move_abs (0, max_split_col);
|
|
|
|
/*
|
|
** We prefer to split lines at the beginning of a run of spaces
|
|
** or immediately after a comma.
|
|
*/
|
|
|
|
if (search_back ("{ +}|{,\\c?}", -2, NULL, 1))
|
|
{
|
|
inq_position (NULL, split_col);
|
|
move_abs (0, max_split_col);
|
|
}
|
|
raise_anchor ();
|
|
|
|
/*
|
|
** If we found no split column, or if the split was past the
|
|
** margin, split at the margin. Beep, as a warning.
|
|
*/
|
|
|
|
if (!split_col || split_col > max_split_col)
|
|
{
|
|
beep ();
|
|
split_col = max_split_col;
|
|
}
|
|
move_abs (0, split_col);
|
|
returns (split_col);
|
|
}
|
|
|
|
/*
|
|
** .cob_abbrev:
|
|
**
|
|
** This macro performs template expansion for COBOL. When it is
|
|
** invoked, the text area of the current line is checked to see if
|
|
** it matches the start of a reserved word or larger construct.
|
|
** If so, the remainder of the construct is filled in automatically.
|
|
**
|
|
** Expansion is only done when we're in the text area of a non-comment
|
|
** line, when there are 8 or fewer code characters on the line, and
|
|
** when we're at the end of the line. This makes expansion faster
|
|
** and avoids unwanted expansion.
|
|
*/
|
|
|
|
void .cob_abbrev ()
|
|
{
|
|
int done,
|
|
col;
|
|
|
|
/*
|
|
** Expand only when we're at the end of the line, and it's not
|
|
** a comment line.
|
|
*/
|
|
|
|
inq_position (NULL, col);
|
|
|
|
if ((col > (_cob_seq_num ? COB_TEXT_COL74 : COB_TEXT_COL85) && read (1) == "\n") && !.cob_is_comment ())
|
|
{
|
|
int loc;
|
|
|
|
string line,
|
|
which_abbrev,
|
|
completion;
|
|
|
|
/*
|
|
** If the last four characters in the buffer were " PIC",
|
|
** we realign the PIC to the proper column. If not, we
|
|
** check for a keyword that needs to be expanded. If we
|
|
** are already past the PIC column, we don't need to
|
|
** realign it.
|
|
*/
|
|
|
|
if (col < (_cob_seq_num ? COB_PIC_COL74 : COB_PIC_COL85) + 3)
|
|
{
|
|
move_abs (0, col - 4);
|
|
|
|
if (upper (read (4)) == " PIC")
|
|
{
|
|
.cob_reposition_keyword ();
|
|
inq_position (NULL, col);
|
|
move_abs (0, col -= 4);
|
|
|
|
while (++col < (_cob_seq_num ? COB_PIC_COL74 : COB_PIC_COL85))
|
|
insert (" ");
|
|
|
|
move_rel (0, 4);
|
|
insert (" ");
|
|
return;
|
|
}
|
|
move_abs (0, col);
|
|
}
|
|
/*
|
|
** Get a trimmed representation of the line into a string.
|
|
*/
|
|
|
|
save_position ();
|
|
move_abs (0, (_cob_seq_num ? COB_TEXT_COL74 : COB_TEXT_COL85));
|
|
loc = strlen (line = upper (trim (ltrim (read ()))));
|
|
|
|
/*
|
|
** Only do template expansion if the trimmed version
|
|
** of the line is at least _cob_min_abbrev characters long,
|
|
** at most 8 characters long, matches an expansion in
|
|
** ABBR_LIST, and is shorter than that token.
|
|
*/
|
|
|
|
if (line != "")
|
|
{
|
|
if (done = index (ABBR_LIST_1, "~" + line))
|
|
which_abbrev = ABBR_LIST_1;
|
|
|
|
else if (done = index (ABBR_LIST_2, "~" + line))
|
|
which_abbrev = ABBR_LIST_2;
|
|
|
|
else if (done = index (ABBR_LIST_3, "~" + line))
|
|
which_abbrev = ABBR_LIST_3;
|
|
}
|
|
|
|
|
|
/*
|
|
** Extract the full, expanded keyword from the
|
|
** abbreviation list, and make sure it's longer
|
|
** than the abbreviation.
|
|
*/
|
|
|
|
|
|
if ((loc <= 8) && (loc >= _cob_min_abbrev) && done && loc)
|
|
{
|
|
completion = substr (which_abbrev, ++done);
|
|
completion = substr (completion, 1, index (completion, "~") - 1);
|
|
|
|
if (loc < strlen (completion))
|
|
{
|
|
/*
|
|
** Delete the abbreviation from the buffer.
|
|
*/
|
|
|
|
.cob_first_nonwhite ();
|
|
col = .cob_indent_level ();
|
|
delete_to_eol ();
|
|
|
|
/*
|
|
** Insert each expanded keyword in context.
|
|
*/
|
|
|
|
switch (completion)
|
|
{
|
|
/*
|
|
** For each of the divisions, insert a full
|
|
** template including all standard sections
|
|
** and paragraphs, and a page eject. You
|
|
** can modify these strings to your own
|
|
** tastes.
|
|
*/
|
|
case "DATA":
|
|
{
|
|
move_abs (0, COB_DIVISION);
|
|
insert (.cob_keyword_cvt ("DATA DIVISION.\n"));
|
|
move_abs (0, COB_DIVISION);
|
|
insert (.cob_keyword_cvt ("FILE SECTION.\n\n"));
|
|
move_abs (0, COB_DIVISION);
|
|
save_position ();
|
|
insert (.cob_keyword_cvt ("WORKING-STORAGE SECTION.\n\n"));
|
|
move_abs (0, COB_DIVISION);
|
|
insert (.cob_keyword_cvt ("LINKAGE SECTION.\n"));
|
|
move_abs (0, COB_DIVISION);
|
|
insert (COB_EJECT_CHAR);
|
|
restore_position ();
|
|
up ();
|
|
}
|
|
case "ENVIRONMENT":
|
|
{
|
|
move_abs (0, COB_DIVISION);
|
|
insert (.cob_keyword_cvt ("ENVIRONMENT DIVISION.\n"));
|
|
move_abs (0, COB_DIVISION);
|
|
insert (.cob_keyword_cvt ("CONFIGURATION SECTION.\n"));
|
|
move_abs (0, COB_DIVISION);
|
|
insert (.cob_keyword_cvt ("SOURCE-COMPUTER. IBM-PERSONAL-COMPUTER.\n"));
|
|
move_abs (0, COB_DIVISION);
|
|
insert (.cob_keyword_cvt ("OBJECT-COMPUTER. IBM-PERSONAL-COMPUTER.\n\n"));
|
|
move_abs (0, COB_DIVISION);
|
|
insert (.cob_keyword_cvt ("INPUT-OUTPUT SECTION.\n"));
|
|
move_abs (0, COB_DIVISION);
|
|
save_position ();
|
|
insert (.cob_keyword_cvt ("FILE-CONTROL.\n\n"));
|
|
move_abs (0, COB_DIVISION);
|
|
insert (.cob_keyword_cvt (COB_EJECT_CHAR));
|
|
restore_position ();
|
|
.cob_indent (TRUE);
|
|
}
|
|
|
|
case "IDENTIFICATION":
|
|
{
|
|
move_abs (0, COB_DIVISION);
|
|
insert (.cob_keyword_cvt ("IDENTIFICATION DIVISION.\n"));
|
|
move_abs (0, COB_DIVISION);
|
|
insert (.cob_keyword_cvt ("PROGRAM-ID. "));
|
|
|
|
/*
|
|
** This code automatically constructs a
|
|
** program name from the file name.
|
|
*/
|
|
|
|
inq_names (NULL, NULL, completion);
|
|
|
|
if (loc = index (completion, "."))
|
|
completion = substr (completion, 1, --loc);
|
|
|
|
insert (.cob_keyword_cvt (completion + ".\n"));
|
|
|
|
/*
|
|
** Modify these strings to insert
|
|
** your own name and installation
|
|
** information.
|
|
*/
|
|
|
|
if (_cob_ansi_85)
|
|
.cob_comment ();
|
|
|
|
move_abs (0, COB_DIVISION);
|
|
insert (.cob_keyword_cvt ("AUTHOR. <YOUR NAME>.\n"));
|
|
|
|
if (_cob_ansi_85)
|
|
.cob_comment ();
|
|
|
|
move_abs (0, COB_DIVISION);
|
|
insert (.cob_keyword_cvt ("INSTALLATION. <YOUR COMPANY>.\n"));
|
|
move_abs (0, COB_DIVISION);
|
|
|
|
/*
|
|
** We automatically insert today's
|
|
** date here.
|
|
*/
|
|
|
|
if (_cob_ansi_85)
|
|
.cob_comment ();
|
|
|
|
date (loc, NULL, col, completion);
|
|
sprintf (completion, "%s %d, %d.\n", upper (completion), col, loc);
|
|
insert (.cob_keyword_cvt ("DATE-WRITTEN. " + completion));
|
|
|
|
if (_cob_ansi_85)
|
|
.cob_comment ();
|
|
|
|
move_abs (0, COB_DIVISION);
|
|
insert (.cob_keyword_cvt ("DATE-COMPILED. " + completion));
|
|
|
|
if (_cob_ansi_85)
|
|
.cob_comment ();
|
|
|
|
move_abs (0, COB_DIVISION);
|
|
insert (.cob_keyword_cvt ("REMARKS. \n"));
|
|
move_abs (0, COB_DIVISION);
|
|
insert (COB_EJECT_CHAR + "\n");
|
|
move_abs (0, COB_DIVISION);
|
|
}
|
|
|
|
case "PROCEDURE":
|
|
{
|
|
move_abs (0, COB_DIVISION);
|
|
insert (.cob_keyword_cvt ("PROCEDURE DIVISION."));
|
|
left ();
|
|
}
|
|
/*
|
|
** The arithmetic operators require a
|
|
** second keyword, not counting GIVING,
|
|
** which is a separate template. Though
|
|
** COBOL 85 supports an END- keyword for
|
|
** each operator, those are seldom used.
|
|
**
|
|
** Many other verbs are most commonly
|
|
** used in a similar syntax.
|
|
*/
|
|
|
|
case "ADD":
|
|
case "MOVE":
|
|
case "SET":
|
|
.cob_expand_pair (completion, "TO");
|
|
|
|
case "CALL":
|
|
.cob_expand_pair (completion, "USING");
|
|
|
|
case "COMPUTE":
|
|
.cob_expand_pair (completion, "=");
|
|
|
|
case "DIVIDE":
|
|
.cob_expand_pair (completion, "INTO");
|
|
|
|
case "MULTIPLY":
|
|
.cob_expand_pair (completion, "BY");
|
|
|
|
case "SEARCH":
|
|
{
|
|
insert (.cob_keyword_cvt ("SEARCH"));
|
|
save_position ();
|
|
.cob_align ();
|
|
move_rel (0, distance_to_tab ());
|
|
insert (.cob_keyword_cvt (" WHEN \n"));
|
|
restore_position ();
|
|
right ();
|
|
}
|
|
case "SELECT":
|
|
.cob_expand_pair (completion, "ASSIGN TO");
|
|
|
|
case "SUBTRACT":
|
|
.cob_expand_pair (completion, "FROM");
|
|
|
|
/*
|
|
** ELSE may need to be outdented, and
|
|
** we want to continue entering text on
|
|
** the next line, so we just call
|
|
** .cob_indent in split mode.
|
|
*/
|
|
|
|
case "ELSE":
|
|
{
|
|
insert (.cob_keyword_cvt (completion));
|
|
.cob_indent (FALSE);
|
|
}
|
|
/*
|
|
** EVALUATE expands to a multi-line
|
|
** construct, with the default (WHEN
|
|
** OTHER) clause and END- keyword already
|
|
** inserted.
|
|
*/
|
|
|
|
case "EVALUATE":
|
|
{
|
|
insert (.cob_keyword_cvt (completion));
|
|
|
|
if (_cob_ansi_85)
|
|
{
|
|
.cob_first_nonwhite ();
|
|
inq_position (NULL, col);
|
|
.cob_align ();
|
|
save_position ();
|
|
move_rel (0, distance_to_tab ());
|
|
insert (.cob_keyword_cvt ("WHEN OTHER \n"));
|
|
move_abs (0, col);
|
|
insert (.cob_keyword_cvt ("END-EVALUATE") + " ");
|
|
restore_position ();
|
|
end_of_line ();
|
|
}
|
|
else
|
|
insert (" ");
|
|
}
|
|
/*
|
|
** EXIT almost always has a period after it.
|
|
*/
|
|
|
|
case "EXIT":
|
|
{
|
|
insert (.cob_keyword_cvt ("EXIT."));
|
|
.cob_indent (FALSE);
|
|
}
|
|
case "DELETE":
|
|
insert (.cob_keyword_cvt ("DELETE "));
|
|
|
|
case "GIVING":
|
|
insert (.cob_keyword_cvt ("GIVING "));
|
|
|
|
/*
|
|
** With ANSI 85 COBOL, we automatically
|
|
** insert a matching END-IF. We do the
|
|
** same for PERFORM.
|
|
*/
|
|
|
|
case "IF":
|
|
case "PERFORM":
|
|
.cob_expand_block (completion);
|
|
|
|
/*
|
|
** STRING and UNSTRING expand to a multi-line construct.
|
|
** For ANSI 85 COBOL, we line the INTO
|
|
** clause up with the DELIMITED BY
|
|
** clauses and use an END-STRING.
|
|
*/
|
|
case "UNSTRING":
|
|
case "STRING":
|
|
{
|
|
insert (.cob_keyword_cvt (completion));
|
|
save_position ();
|
|
.cob_first_nonwhite ();
|
|
inq_position (NULL, col);
|
|
.cob_align ();
|
|
move_rel (0, distance_to_tab ());
|
|
insert (.cob_keyword_cvt (" DELIMITED BY \n"));
|
|
move_abs (0, col);
|
|
|
|
if (_cob_ansi_85)
|
|
{
|
|
move_rel (0, distance_to_tab ());
|
|
insert (.cob_keyword_cvt ("INTO \n"));
|
|
move_abs (0, col);
|
|
insert (.cob_keyword_cvt ("END-STRING"));
|
|
}
|
|
else
|
|
insert (.cob_keyword_cvt ("INTO "));
|
|
|
|
restore_position ();
|
|
right ();
|
|
}
|
|
|
|
/*
|
|
** THRU checks to see if the last line
|
|
** was a PERFORM, and if so, it reads
|
|
** the paragraph name after the PERFORM,
|
|
** adds an -EXIT, and inserts it after
|
|
** the THRU.
|
|
**
|
|
** It also makes sure the THRU and PERFORM
|
|
** are properly lined up.
|
|
*/
|
|
|
|
case "THRU":
|
|
{
|
|
insert (.cob_keyword_cvt (completion));
|
|
.cob_reposition_keyword ();
|
|
insert (" ");
|
|
save_position ();
|
|
move_rel (-1, NEG_MAX_COL);
|
|
.cob_first_nonwhite ();
|
|
|
|
if (upper (read (7)) == "PERFORM")
|
|
{
|
|
move_rel (0, 7);
|
|
completion = ltrim (trim (read ())) + " ";
|
|
restore_position ();
|
|
insert (.cob_keyword_cvt (substr (completion, 1, index (completion, " ") - 1)));
|
|
insert (.cob_keyword_cvt ("-EXIT"));
|
|
}
|
|
else
|
|
restore_position ();
|
|
}
|
|
case "UNTIL":
|
|
{
|
|
insert (.cob_keyword_cvt (completion));
|
|
.cob_reposition_keyword ();
|
|
insert (" ");
|
|
}
|
|
/*
|
|
** Most PERFORM VARYING loops run from
|
|
** 1 by 1, so we insert that as a default.
|
|
*/
|
|
|
|
case "VARYING":
|
|
{
|
|
insert (.cob_keyword_cvt (completion));
|
|
.cob_reposition_keyword ();
|
|
save_position ();
|
|
insert (.cob_keyword_cvt (" FROM 1 BY 1"));
|
|
restore_position ();
|
|
right ();
|
|
}
|
|
|
|
/*
|
|
** Note: the "!" is an internal convention to ensure
|
|
** that the abbreviation length is less than the
|
|
** completion length.
|
|
*/
|
|
case "AG!":
|
|
.cob_expand_pair ("ADD", "TO GIVING");
|
|
|
|
/*
|
|
** Note: the "!" is an internal convention to ensure
|
|
** that the abbreviation length is less than the
|
|
** completion length.
|
|
*/
|
|
case "AE!":
|
|
{
|
|
insert (.cob_keyword_cvt ("AT END") + " ");
|
|
.cob_align ();
|
|
move_rel (0, distance_to_tab ());
|
|
}
|
|
|
|
/*
|
|
** Note: the "!" is an internal convention to ensure
|
|
** that the abbreviation length is less than the
|
|
** completion length.
|
|
*/
|
|
case "DF!":
|
|
.cob_expand_pair ("DELETE", "FILE");
|
|
|
|
/*
|
|
** Note: the "!" is an internal convention to ensure
|
|
** that the abbreviation length is less than the
|
|
** completion length.
|
|
*/
|
|
case "DG!":
|
|
.cob_expand_pair ("DIVIDE", "INTO GIVING REMAINDER");
|
|
|
|
/*
|
|
** Note: the "!" is an internal convention to ensure
|
|
** that the abbreviation length is less than the
|
|
** completion length.
|
|
*/
|
|
case "DR!":
|
|
.cob_expand_pair ("DELETE", "RECORD");
|
|
|
|
/*
|
|
** Note: the "!" is an internal convention to ensure
|
|
** that the abbreviation length is less than the
|
|
** completion length.
|
|
*/
|
|
case "MG!":
|
|
.cob_expand_pair ("MULTIPLY", "BY GIVING");
|
|
|
|
/*
|
|
** Note: the "!" is an internal convention to ensure
|
|
** that the abbreviation length is less than the
|
|
** completion length.
|
|
*/
|
|
case "SG!":
|
|
.cob_expand_pair ("SUBTRACT", "FROM GIVING");
|
|
|
|
/*
|
|
** Note: the "!" is an internal convention to ensure
|
|
** that the abbreviation length is less than the
|
|
** completion length.
|
|
*/
|
|
case "EC!":
|
|
{
|
|
insert (.cob_keyword_cvt ("EXEC CICS") + " ");
|
|
save_position ();
|
|
.cob_align ();
|
|
insert (.cob_keyword_cvt ("END-EXEC") + " ");
|
|
restore_position ();
|
|
}
|
|
|
|
default:
|
|
insert (.cob_keyword_cvt (completion) + " ");
|
|
}
|
|
}
|
|
|
|
else
|
|
done = FALSE;
|
|
}
|
|
restore_position (!done);
|
|
}
|
|
/*
|
|
** If we couldn't expand an abbreviation, we perform the
|
|
** normal task associated with the key that called us: insert
|
|
** a space, or shift a marked block.
|
|
*/
|
|
|
|
if (!done)
|
|
if (inq_local_keyboard () == _cob_alt_template)
|
|
slide_in ();
|
|
else
|
|
self_insert ();
|
|
}
|
|
|
|
/*
|
|
** Insert a pair of matching keywords, convert the case of the first
|
|
** one (it must be a verb) and place the cursor between them.
|
|
*/
|
|
|
|
void .cob_expand_pair (string keyword1, string keyword2)
|
|
{
|
|
insert (.cob_keyword_cvt (keyword1));
|
|
save_position ();
|
|
insert (" " + (.cob_keyword_cvt (keyword2) + " "));
|
|
restore_position ();
|
|
right ();
|
|
}
|
|
|
|
/*
|
|
** Reindents the current line if necessary, leaving the cursor
|
|
** at the end of the line.
|
|
*/
|
|
|
|
void .cob_reposition_keyword ()
|
|
{
|
|
.cob_indent (TRUE);
|
|
move_rel (-1, NEG_MAX_COL);
|
|
end_of_line ();
|
|
}
|
|
|
|
/*
|
|
** Inserts a matching END- keyword at the same level, below and in the
|
|
** same column as the expanded keyword. Only works for COBOL 85.
|
|
*/
|
|
|
|
void .cob_expand_block (string keyword)
|
|
{
|
|
insert (keyword = .cob_keyword_cvt (keyword));
|
|
|
|
if (_cob_ansi_85)
|
|
{
|
|
int col;
|
|
|
|
save_position ();
|
|
.cob_first_nonwhite ();
|
|
inq_position (NULL, col);
|
|
end_of_line ();
|
|
insert ("\n");
|
|
move_abs (0, col);
|
|
insert (.cob_keyword_cvt ("END-") + keyword);
|
|
restore_position ();
|
|
}
|
|
insert (" ");
|
|
}
|
|
|
|
/*
|
|
** Converts keywords into the user-specified preferred case. If the
|
|
** case is MIXED, the first separate letter of each "word" in the
|
|
** keyword string is converted to upper case, and the others are
|
|
** converted to lower case.
|
|
*/
|
|
|
|
string .cob_keyword_cvt (string keyword)
|
|
{
|
|
switch (_cob_keyword_case)
|
|
{
|
|
case KW_UPPER:
|
|
returns (upper (keyword));
|
|
|
|
case KW_LOWER:
|
|
returns (lower (keyword));
|
|
|
|
case KW_MIXED:
|
|
{
|
|
int space;
|
|
|
|
string retval;
|
|
|
|
keyword = upper (keyword);
|
|
|
|
while (space = search_string ("[ \\-]", keyword, NULL, 1))
|
|
{
|
|
/*
|
|
** Certain words in the NO_CAPS list do not get mixed
|
|
** capitalization.
|
|
*/
|
|
if (index (NO_CAPS, substr (keyword, 1, space - 1) + "~"))
|
|
retval += lower (substr (keyword, 1, space));
|
|
else
|
|
{
|
|
retval += substr (keyword, 1, 1);
|
|
retval += lower (substr (keyword, 2, space - 1));
|
|
}
|
|
keyword = substr (keyword, ++space);
|
|
}
|
|
if (index (NO_CAPS, substr (keyword, 1) + "~"))
|
|
retval += lower (keyword);
|
|
else
|
|
{
|
|
retval += substr (keyword, 1, 1);
|
|
retval += lower (substr (keyword, 2));
|
|
}
|
|
returns (retval);
|
|
}
|
|
}
|
|
}
|
|
|
|
/*
|
|
** Aligns the cursor to the next row, same column.
|
|
*/
|
|
void
|
|
.cob_align ()
|
|
{
|
|
int col;
|
|
|
|
.cob_first_nonwhite ();
|
|
inq_position (NULL, col);
|
|
end_of_line ();
|
|
insert ("\n");
|
|
move_abs (0, col);
|
|
}
|
|
|