"Sel:" in status bar is off-by-1 on line count?
-
Sometimes I want to select a certain number of whole lines.
When I create my stream selection, the “Sel: X | Y” section of the status bar shows me the number of characters I have blocked (X) and the number of lines (Y).
The problem is that “Y” seems to be off-by-one (too high) and I have to remember and compensate in my mind for this.
For example, If I want to select 7 full lines (including all line endings), I have to select until the Y in the status bar shows 8.
This seems an awful design to me as I can never remember if it is one too high or one too low when I need to use it.
I can remember for the duration of writing this posting because I just looked at it in-depth, but…
Is there a reason it just doesn’t show what I think it should show?
-
- On the right, I have selected from the beginning to the end of line 1, without selecting the EOL. My cursor has stayed on one line, so
Sel: 3|1
tells me my selection of three characters (“o”, “n”, “e”) spans 1 line. - On the left, I have selected all of line one, and moved the cursor to line two. Because the selection includes the EOL, and the cursor is on a second line compared to the start of the selection,
Sel: 5|2
tells me that I’ve selected 5 characters (“o”, “n”, “e”, CR, LF) across a total of 2 lines.
- On the right, I have selected from the beginning to the end of line 1, without selecting the EOL. My cursor has stayed on one line, so
-
I don’t agree with the “spans lines” answer…if I select 7 lines by click-drag on the line numbers margin, I expect to see 7 in the status bar, not 8. There really isn’t a valid argument that this somehow “spans 8 lines” – I’ve got seven complete lines and seven line endings. Thus, off-by-1!
-
I also don’t like the way this works; I don’t know that I would call it a bug, just “how it works”. Some time ago I wrote some Pythonscript to “fix” it. I’ll look at that code when I get some time, and if it is fit for human consumption, I’ll post it. :-)
-
Selecting a line using the line number margin causes the cursor to move to the line below what you have selected. The line that the cursor is on seems to be included in the lines “selected” so this seems like a bug to me.
-
A bug, I don’t know - it depends how you look at it.
As the whole line gets selected it means the EOL get selected as well.
As the cursor needs to be placed to create a target for selection,
it can only be placed after EOL which means next line.Cheers
Claudia -
I guess if you use the line number margin to select lines, to get the proper selection information you would need to move the cursor back one position to move it to the end of the last selected line. Then the numbers would be correct.
-
but then you won’t select the eol.
Cheers
Claudia -
Here’s the Pythonscript I mentioned earlier.
After running it, it will update the “Sel:” area in the status bar as follows:
-
If selection contains partial lines which do not include the first line’s first character, not the last line’s last character (non-line-ending), then NO CHANGE from normal Notepad++ behavior.
-
If the selection contains the first line’s first character, the part after the vertical bar (|) in “Sel: X | Y” will have a caret character (^) before the number of lines, e.g. ^7
-
If the selection contains the last line’s last character (but not the line ending character(s)), the part after the vertical bar will have a dollar sign character ($) after the number of lines, e.g. 7$
-
If the selection contains the last line’s last character AND the line ending character(s), the part after the vertical bar will have a dollar sign character ($) and \R after the number of lines, e.g. 7$\R
-
If the selection contains lines in their entirety, the part after the vertical bar will combine all of these symbols, an example being ^7$\R. Note that this example means that 7 FULL LINES are in the selection, fixing the original “bad” Notepad++ behavior for this case.
Regular expression users will feel very comfortable with the meaning behind the choice of special symbols used here.
Okay, so here’s the Pythonscript code:
# Sel : C | L in the status bar shows an off-by-one L (line count) sometimes (open to debate) # make it better: # Sel : C | L <-- selection spans L partial lines # Sel : C | ^L <-- selection spans L partial lines and includes first character on its first line # Sel : C | L$ <-- selection spans L partial lines and includes last char (but not line-ending) on its last line # Sel : C | L$\R <-- selection spans L partial lines and includes line-ending on its last line # Sel : C | ^L$\R <-- selection spans L FULL lines and includes line-ending on its last line def StatusbarSelOverride(args): curr_pos = editor.getCurrentPos() curr_line = editor.lineFromPosition(curr_pos) curr_col = editor.getColumn(curr_pos) sel_part = 'N/A' if editor.getSelections() == 1: sel_mode = editor.getSelectionMode() rect_sel_mode = True if (sel_mode == SELECTIONMODE.RECTANGLE or sel_mode == SELECTIONMODE.THIN) else False if not rect_sel_mode: if editor.getSelectionEmpty(): sel_part = '0 | 0' else: sel_start_pos = editor.getSelectionStart() sel_end_pos = editor.getSelectionEnd() sel_start_line = editor.lineFromPosition(sel_start_pos) sel_end_line = editor.lineFromPosition(editor.getSelectionEnd()) lines_touched_by_sel = sel_end_line - sel_start_line + 1 start_of_line_symbol = '^' if sel_start_pos == editor.positionFromLine(sel_start_line) else '' end_of_line_symbols = '$' if sel_end_pos == editor.getLineEndPosition(sel_end_line) else '' selected_text = editor.getSelText() line_ending = ['\r\n', '\r', '\n'][notepad.getFormatType()] if selected_text.endswith(line_ending): end_of_line_symbols = r'$\R'; lines_touched_by_sel -= 1 sel_text_len = len(selected_text) sel_part = '{tl} | {sol}{lis}{eol}'.format( tl=sel_text_len, sol=start_of_line_symbol, lis=lines_touched_by_sel, eol=end_of_line_symbols, ) line_col_sel_info_for_sb = 'Ln : {user_line} Col : {user_col} Sel : {sel}'.format( user_line=curr_line+1, user_col=curr_col+1, sel=sel_part, ) notepad.setStatusBar(STATUSBARSECTION.CURPOS, line_col_sel_info_for_sb) editor.callback(StatusbarSelOverride, [SCINTILLANOTIFICATION.UPDATEUI]) # register callback
-