|
Post by Stefan on Nov 27, 2023 4:16:54 GMT -5
George,
I'd like to suggest some new keyboard primitives to simplify manipulation of columnar data, e.g. data in tabbed columns. Entering such data is straight forward: Having marked the columns with tab stops, simply type the data, press (Tab) for the next column, etc. Positioning is also straight forward: Use either (Tab) or (BackTab) to jump to another column.
It gets more cumbersome when editing such data: (Insert), (DataInsert), (Delete), (DataDelete) operate on one character at a time and with a 'whole-line' scope, (ERASE EOL) operates on the whole line.
This obviously affects data in spreadsheet-like columns, but for me the more relevant application is program code. My profiles have tab stops in columns 50, 55, 60, 65, 70, 80, 89 and 100. Code exists mostly up to column 50 and line comments start thereafter. For lines longer than 50 chars, the comment will start in one of the higher 'columns' Column 89 is a convenient place for the '*/' end-of comment sequence, which can be entered easily using a single key stroke
(SaveCursor)(NewLine)(2:BackTab)[*/](RestoreCursor)
Editing code results in comments shifting about, requiring endless spacing to keep things tidy.
Suggestion: New keyboard primitives that operate with tab/column-width scope
In all cases, if there are no further tab stops defined to the right of the current 'column', END-OF-LINE is treated as the next Tab stop
(TabErase) Like (EraseEOL) but with tab-width scope, i.e. replaces data from cursor position to next tab stop with blanks. If no more tab stops, behaves like (EraseEOL). Note: No need for (BackTabErase) as this can be achieved by (BackTab)(TabErase)
(TabShift) Effectively performs (Tab) but instead of just moving the cursor, it adds blanks (DataInsert-style) so the data at the cursor position is shifted with the cursor to the next tab stop. If no more tab stops, (TabShift) does nothing. (TabInsert) (TabDataInsert)
Like (SetDIN) but uses a tab-width scope. If no more tab stops, behaves like normal (SetDIN).
These are a little tricky in that they have a limit to how many chars can be entered before the next tab stop is reached, not an issue that afflicts (SetINS) and (SetDIN). Good ol' ISPF used to just 'lock' the keyboard if the 'field' size had been reached. After pondering this for some time, I concluded the approach might be to bump all data along in (SetDIN) mode. As usual, either primitive should cancel/replace the current status set by (ClearInsert), (SetDIN), (SetINS), (SetOVR) and any of the (ClearInsert), (SetDIN), (SetINS), (SetOVR) primitives should also cancel/replace the new primitives.
(TabDelete) like (Delete) but uses a tab-width scope. Data beyond the nex tab stop does not shift left. If no more tab stops, behaves like normal (Delete). (TabDataDelete) like (DataDelete) but uses a tab-width scope. Data beyond the next tab stop does not shift left. If no more tab stops, behaves like normal (DataDelete).
What does anyone else think?
|
|
|
Post by George on Nov 28, 2023 11:24:11 GMT -5
Stefan: I'll have to think about all these. Another thought to add. I have been experimenting with the JK-IDE, which is a replacement for the PowerBasic IDE. Lots of things are different KB-wise so it takes a bit of getting used to. But one KB feature I like is specifically geared to handling comments. Lets call it (Comment). JK-IDE is totally oriented to PowerBasic and FreeBasic so it 'knows' comments.
In Options, you set your desired comment column.
While editing, if (Comment) is entered:
If no current text in the line a "'" is entered in the comment column and the cursor placed after the "'"
If text is in the line but no comment yet, same as above (if text < comment col)
If text in the line is past the comment column, a "'" is entered following the text and the cursor placed ater it.
If a comment is already in the line, and it will fit, the comment is shifted left or right to align the "'" in the comment column.
If a comment is already in the line, and it won't fit, the comment is shifted to the right of the text end.
I found it really usefull. It would need some thinking to adapt this to differing comment styles.
George
|
|
|
Post by Stefan on Nov 29, 2023 4:19:52 GMT -5
George,
I'm unfamiliar with the JK-IDE, but from your description I can see how it assists data entry in BASIC statement syntax. Given its focus, I wonder what support there is for identifying alternative 'trigger' characters or even 'trigger' sequences for comments in other languages. Strictly personally, I would also find the concept of a single comment column constraining. Say col 50 is chosen and some statements in a block exceed 50 bytes. A set of comments aligned to col 55 or 60 is neater than a bunch of comments all starting at 'stmt-length+1'.
As a data entry tool, I note that...
If no current text in the line a "'" is entered in the comment column and the cursor placed after the "'" SPFLite: MASK line has the same effect If text is in the line but no comment yet, same as above (if text < comment col) SPFLite: Doesn't happen if MASK line is used, if unwanted, clear it with (EraseEOL)
If text in the line is past the comment column, a "'" is entered following the text and the cursor placed ater it. SPFLite: Type a single quote and a blank. If a comment is already in the line, and it will fit, the comment is shifted left or right to align the "'" in the comment column. SPFLite: no equivalent If a comment is already in the line, and it won't fit, the comment is shifted to the right of the text end. SPFLITE: (SETDIN) or (SETINS) has the same effect
But I'm comparing an 'IDE' with SPFLite and that's a case of apples and pears. I appreciate you may prefer the relevant IDE over a general-purpose editor like SPFLite when creating source code.
However, this doesn't directly address the same use-case as my suggestion, which seeks to enhance SPFLite to assist with data EDITING rather than data ENTRY. Such data can be code (and often is for me), and needs to be independent of language syntax
But it can equally be used with ANY tabular data in columns which you don't want to have to realign every time data in an earlier column changes in length.
By the way....
I've tried using MASK line in both ISPF and SPFLite. The fact that new lines always start with the cursor at the MASK text (i.e. in the comment column in this discussion) is a right posterior pain! I want to enter code first and foremost and thus the cursor should be at the 'start' of an empty line to allow code entry, irrespective of any MASK text. With (SETDIN) the comment 'trigger' will stay put anyway. Having to (BackTab) before typing each statement is just annoying.
Maybe that's another new suggestion to make MASK line remotely useful.
EITHER (1) just place the mask data as needed and always position the cursor 'as normal' BUT crucially ignoring any MASK text OR
(2) allow use of the a single, user-chosen character (e.g. _ ~ & ^) in the MASK line to indicate the desired cursor position and a PROFILE (or perhaps OPTIONS) entry to define said character
|
|
|
Post by Robert on Nov 29, 2023 9:43:21 GMT -5
For MASK, in principle I like the idea of a new MASK code ^ to mean cursor placement. Only problem is, what if you wanted ^ as a mask value? If ^ has a "special" meaning, then it no longer can be used for its ordinary meaning, as mask data.
I think the way that would be least disruptive would be to ignore the contents of the MASK when setting the cursor. But that has a problem, too. Once the mask data has been inserted, it's data, just like any other data. How could the main edit process be able to tell the difference?
This seems like a hard problem to get right. Let's say you have BASIC-like comments, and on SOME lines you want to add them, and others you don't. Suppose there was a new primitive like (MaskTab), which would tab over to the first position where the MASK data WOULD have been, and then insert the mask data THEN. Afterwards, the cursor could be moved to the beginning of the thus-inserted mask data.
This could be a little tricky, but I *think* it could be done. You'd need to pick some good key for (MaskTab). Maybe, Ctrl Tab, or perhaps one of the numeric-pad keys.
R
|
|
|
Post by George on Nov 29, 2023 10:31:58 GMT -5
Stefan: I only mentioned the JK-IDE (Comment) because I found it extremely usefull, not to ignore your other suggestions. Maybe it could work with a double value - the 'comment-column' and an increment value to create other 'comment-stops' for long code lines. The real problem is the old "What does a comment look like". It could be done, after all DIFF can be told to ignore comments - depends on AUTO being active.
The MASK cursor positioning would be handy, but that area is tricky when the logic is deciding when to remove the unused inserted lines. e.g. Enter I10 to get 10 '''''' lines, use only 2-3, and then expect the rest to be deleted. Do-able, just has to be done carefully. And as Robert said, how to specify the chosen column.
George
|
|
|
Post by George on Nov 29, 2023 11:43:21 GMT -5
Stefan: Sounds like you want to create a whole new set of DIN/INS/OVR modes. That could get very messy.
How about a (TabBnds) toggle? It would be a new mode/flag that would affect the existing INS/OVR/DIN modes by imposing an effective Bounds on the other commands. i.e. a temporary BNDS based on the cursor location and TAB settings. It would basically modify the behaviour of the existing Insert, Delete, DataInsert and DataDelete handling.
Not even sure how it might work. Like what happens when simply typing in a column and you enter the last character, can you keep typing? Or do you have to use TAB to get the cursor to the next character position. Could get very confusing.
George
|
|
|
Post by Robert on Nov 29, 2023 12:53:44 GMT -5
It's important to remember that you only get a MASK line if you use the I line command (right?).
Perhaps all that is needed here is write a user line-command macro to simulate the I action, grab the MASK data, overlay it on the new line, then position the cursor where you want.
Stefan, I think you should up to the task.
R
|
|
|
Post by George on Nov 29, 2023 15:28:20 GMT -5
Robert: If you insert lines with a macro (other than with an I command) you won't get the auto-cleanup of unused lines.
Also, I noticed a quirk of the N line command - it puts the cursor onto the 1st non-blank in the MASK, even though it doesn't use the MASK data.
Question: Why doesn't N use the MASK data? That's what the Help says, but Why? N didn't exist in ISPF so it was our call.
George
|
|
|
Post by Robert on Nov 29, 2023 19:00:11 GMT -5
Why doesn't N use MASK data? The Help says this:
"Blank lines created by N are zero-length lines, and do not utilize the contents of the MASK line, even if a non-blank MASK line is defined."
It says this because I wrote it, and because the N command was my proposal. In my mind, the definition of an N line was a NULL line. Help even says it's zero-length. What the concept was, was for there to be an SPFLite equivalent of pressing the standard Enter key and getting a new line, just like in NotePad.
It was my call, and after all these years, I would make the same call. No one has ever objected to this behavior, so it couldn't have caused any problems.
My suggestion to write a macro was to limit the impact to you. I suspect that a keyboard macro could be made that did nearly everything that Stefan was looking for. The main issue is to precisely define what behavior is needed.
Suppose you wanted to "emit" the mask data, AS OF the current cursor location. So, if you are in a "new" line and the cursor is in column 10, then column 10 and after from the MASK data would be inserted into column 10 of the new line. Perhaps AFTER that is done, the cursor would be repositioned at column 10.
That's one possible kind of semantics. We need some exact definition, then making a new keyboard primitive like (EmitMask) should be straight-forward.
|
|
|
Post by Stefan on Nov 30, 2023 3:53:11 GMT -5
Robert, For MASK, in principle I like the idea of a new MASK code ^ to mean cursor placement. Only problem is, what if you wanted ^ as a mask value? If ^ has a "special" meaning, then it no longer can be used for its ordinary meaning, as mask data. I suggested we make the 'cursor placement char' a 'user-chosen character' in the file PROFILE to avoid clashes with 'MASK data'. It should be a PROFILE setting/value because (a) MASK data is defined in the profile and (b) it can then be different according to file type. George, The MASK cursor positioning would be handy, but that area is tricky when the logic is deciding when to remove the unused inserted lines. e.g. Enter I10 to get 10 '''''' lines, use only 2-3, and then expect the rest to be deleted. Do-able, just has to be done carefully. And as Robert said, how to specify the chosen column. You've already cracked that one. And Roberts addition of "n" line command enhances it. Fill in something on the MASK line in the Profile - and try it out. Use ' I3' and three blank MASK lines appear - enter text on just one, press ENTER and the other two vanish as expected. Use ' N3' to create three non-temporary blank lines, without the mask overlay. Best of both worlds! except for the N-bug - see below My only functional issue with MASK is the cursor placement. In the 'I3' example, the cursor on the first inserted line sticks to the first character of the mask. The normal 'indentation hugging' logic places the cursor at the first non-blank char of the line or the first non-blank on the preceeding line, etc. Perhaps you could bypass the 'at-first-non-blank' part of that logic when 'MASK data' is present so that the 'first non-blank on preceeding line' part 'WINS'. Failing that, I'd settle for an addional 'override' rule AFTER the current cursor placement logic like " when new line and 'MASK data' is present", place cursor in col 1'. N-bug There's a bug in the 'N3' scenario when MASK line in the profile is not blank. 'N3' inserts 3 blank lines, but the cursor is placed where the MASK would be, even though the MASK data isn't placed on the line.
|
|
|
Post by Stefan on Nov 30, 2023 6:03:38 GMT -5
George
Back to the original suggestion...
Sounds like you want to create a whole new set of DIN/INS/OVR modes. That could get very messy. Messy is not attractive! I've had a re-think. - The 'next' tab stop is not a hard boundary (like 3270 with hardware tabs); we'll let it overrun if required, like the 3270s with soft tabs. - Each of the proposed (Tabxxxxxx) primitives is basically a shortcut, replacing a variable number of insert-blanks or delete-key presses with one key press.
- The number of bytes to add or remove is the column range between the cursor position and the next tab stop.
How about a (TabBnds) toggle? It would be a new mode/flag that would affect the existing INS/OVR/DIN modes by imposing an effective Bounds on the other commands. i.e. a temporary BNDS based on the cursor location and TAB settings. It would basically modify the behaviour of the existing Insert, Delete, DataInsert and DataDelete handling. Sounds very interesting.
Would be a one-stop-shop as far as the other primitives' effect is concerned.
Not sure it needs to affect OVR. I guess the (TabBnds) primitive should toggle ON/OFF. Maybe (ClearInsert) should also reset it to OFF? The temporary bounds would be 'Current cursor pos' and 'next tab stop or EOL' I like this a LOT!
Not even sure how it might work. Like what happens when simply typing in a column and you enter the last character, can you keep typing? Keep typing.
Alternative is you display a message indicating the 'field' is full and refuse keystrokes until (TabBnds) is reset or cursor pos changes, but I think that's over complicated.
|
|
|
Post by George on Nov 30, 2023 10:41:15 GMT -5
Stefan: The N-bug is a bug and I'll correct that. It's probably because I and N share 95% of the logic. A bit too much sharing.
I also Prefer (TabBnds). It's only 1 new key, instead of 3-4. We really just have to define which of the primitives will be affected. And typing through a tab boundary is best, that's what happens now so we shouldn't change it.
George
|
|
|
Post by George on Nov 30, 2023 12:30:27 GMT -5
OK, how about this: When multiple TAB stops are specified, the data is treated as being in columns, with the bounds of each column defined by the TAB stops. (TabBNDS)
This will toggle ( ON / OFF) a new internal Edit flag. This will affect the operation of the following other KB Primitives. If ON it will show in the StatusBar box which contains INS/OVR/DIN, as TABINS/TABOVR/TABDIN. Editing functions will be altered to be sensitive to these Tab-Columns. The affected primitives will be: (EraseEOL)When TabBNDS is active, it will replace data from cursor position to the end of the current Tab-Column with blanks. If no more tab stops, it behaves like normal (EraseEOL). (Delete)When TabBNDS is active, it will delete data only within the current tab-column. If no more tab stops, it behaves like normal (Delete). (DataDelete)
When TabBNDS is active, it will delete data (in DataDelete mode) only within the current tab-column. If no more tab stops, it behaves like normal (Delete). (Tab) When TabBNDS is active, instead of just moving the cursor, it inserts blanks so the data at the cursor position is shifted with the cursor to the next tab stop. If no more tab stops, it does nothing. When TabBNDS is inactive, (Tab) operates as before Resetting TabBNDS TabBNDS mode is reset by a 2nd (TabBNDS) key or by any of the (SetINS) (SetOVR) (SetDIN) or (ClearInsert) primitives. Typing Characters- When TabBNDS are Active
- In TABINS Mode
- Acts as if a BNDS SCol ECol were in effect, where SCol and ECol are the bounds of the current tab-column. Current data is shifted right. If the data exceeds the tab column width, then the SCol/ECol bnds are removed and shifting continues into the next tab column (which will set new SCol ECol bounds for the next column).
- TABOVR Mode
- There is no change to typing in TABOVR mode
- TABDIN Mode
- Acts as if a BNDS SCol ECol were in effect, where SCol and ECol are the bounds of the current tab column range. Current data is shifted right in DataInsert Mode; that is, stretches of blanks within the column are 'used up' to accommodate shifting. If the data exceeds the tab column width, then the SCol/ECol bnds are removed and DataInsert type shifting continues into the next tab-column (which will set new SCol ECol bounds for the next column).
I'm sure I've missed/overlooked some things, so please review and comment. George
|
|
|
Post by Stefan on Nov 30, 2023 12:44:51 GMT -5
It's important to remember that you only get a MASK line if you use the I line command (right?). Perhaps all that is needed here is write a user line-command macro to simulate the I action, grab the MASK data, overlay it on the new line, then position the cursor where you want. Stefan, I think you should up to the task. R Robert,
LOL! I did try a keyboard macro: {I3}(ENTER)(Column/1)
This does indeed insert 3 lines, including any mask data and leaves the cursor in column 1, but comes at way too high a price,...
- we're all used to just entering 'i' and would need to retrain to press a mapped key instead. - the {i} command is fixed - you can't say i3 or i99 or whatever you need. - you need the ENTER primitive so {i3} executes BEFORE we position the cursor. This also executes any pending line commands and/or primary command as well, so who know where we may end up - the column 1 positioning is fixed, irrespective of whether a 'mask' string was added or not, so you lose the usual "productivity" cursor positioning like 'same column as the first non-blank on previous line".
|
|
|
Post by Stefan on Nov 30, 2023 12:53:02 GMT -5
George, WOW! If you've overlooked something, then so have I! I guess we'll find out quickly enough once we test it, but it looks comprehensive to me (as good as the Maple Syrup I tried for the first time today ) .
|
|