]> git.neil.brown.name Git - edlib.git/commitdiff
render-lines: fix vline positioning.
authorNeilBrown <neil@brown.name>
Sat, 1 Jul 2023 00:42:06 +0000 (10:42 +1000)
committerNeilBrown <neil@brown.name>
Sat, 1 Jul 2023 00:42:06 +0000 (10:42 +1000)
When we want to position the cursor line precisely, we need
consume_space to consume at most one line each top a bottom else we
might miss the target line.

Also document how find_lines() works with a bit more detail.

Signed-off-by: NeilBrown <neil@brown.name>
DOC/TODO.md
render-lines.c

index 80b1e85563279178c6150e5a3d4c08a50ffe427e..dc8c185834c5b402d95b71d478cab036a545c6e0 100644 (file)
@@ -23,7 +23,7 @@ the file.
       markup support.
 - [X] history: Make it possible to search through history. Maybe Alt-p only shows
       lines containing current content.
-- [ ] emacs: Num-C-l doesn't work if it would require part of a wrapped line
+- [X] emacs: Num-C-l doesn't work if it would require part of a wrapped line
       off top of screen
 - [ ] emacs: :C-q to recognise names of Unicode chars: e.g. WASTEBASKET
        Possibly matches a list which continued :C-q cycles through
@@ -73,6 +73,9 @@ Requirements for a v1.0 release
 Core features
 -------------
 
+- [ ] Send global notify before/after refresh.  LOG must suspend
+      logging (or notifications at least) during refresh if is visible
+      anywhere
 - [ ] Do I want "Display" as in "Display:close", or "window" as in
       "window:notify".  Decide, and make everything consistent.
 - [ ] Do I really need global-multicall- or can I just use
@@ -274,7 +277,7 @@ Module features
 
 ### emacs
 
-- [ ] Num-C-l doesn't work if it would require part of a wrapped line
+- [X] Num-C-l doesn't work if it would require part of a wrapped line
       off top of screen
 - [ ] :C-q to recognise names of Unicode chars: e.g. WASTEBASKET
        Possibly matches a list which continued :C-q cycles through
@@ -688,16 +691,34 @@ Module features
 
 ### git-mode
 
-- [ ] log view which uses --max-count and --skip to only collect enough
-      log entries to fill the display
+- [ ] log view (:C-c l?) which uses --max-count and --skip to only
+      collect enough log entries to fill the display.  Or better: have a
+      shell mode which only reads from pipe if insertion position is
+      visible.
+- [ ] from log-view, view a commit.  From a commit, view the file
+      at that time.  Maybe 'save' creates a fixup commit which is
+      added to the log.
+- [ ] Have a list of paths are top of log and any commit which doesn't
+      touch the paths is greyed out.  "git log --oneline --stat"
+      provides the info, some of which we hide.
+- [ ] when a git-managed file is saved, if there is a "diff" view, it is
+      updated.  Maybe show both cached and uncached in different colours.
 - [ ] action from log-view to reword and commit - with auto rebase and
       update of the view
 - [ ] actions to re-arrange and deleted the commits.  Integrate with
-      git-rebase.
-- [ ] git-commit command which presents the patch and allows it to be
-      edited (with consistency checks and number updates). On :Commit
-      the patch is applied with "git apply --cached" an if successful
-      the message is added with "| git commit -F"
+      git-rebase...  or simulate git-rebase with "git worktree add
+      --no-checkout" on the most recent unchanged commit, then adding
+      each required commit with git-apply--cached and git-commit
+- [ ] diff-mode editing enhancements:
+      - kill the file marker (+++) kills all chunks in the file
+      - kill the chunk marker (@@) kills the whole chunk
+      - insert line in chunk gets '+' prefix and updates line counts
+      - delete line in chunk updates line counts
+      - editing in chunk re-runs word-diff
+- [ ] git-commit command (:Commit) which presents the patch and allows
+      it to be edited (with consistency checks and number updates).  On
+      :Commit the patch is applied with "git apply --cached" an if
+      successful the message is added with "| git commit -F"
 
 ### lang-python
 
@@ -779,6 +800,8 @@ Module features
 - [ ] c-mode to use tabs or spaces for indent
 - [ ] c-mode: use only spaces for bracket-alignment, or tabs as well
 - [ ] shell command regex to choose parsing: diff, git-log, grep/make
+- [ ] shift width for auto and manual left/right display shift
+- [ ] tab width ??
 
 - [ ] colours of whitespace: tab, at-eol, tab-after-space, past-col ....
 - [ ] whitespace max-col (per-line??)
@@ -1079,36 +1102,6 @@ variables as possible.  If I am in an “if” statement in a “for” look,
 then the loop header and the if condition would be displayed if at all
 possible.
 
-### git-mode
-
-Git adds an extra dimension to editing code - the dimension of history.
-Somethings that the editor can help with:
-
-- creating a commit, including writing the comment message and selection
-  which files, or which lines in which files, should be added to the commit.
-- browsing history - a bit like gitk.  So from a 'git log --oneline', open
-  a view on a patch, or on a file at the time of the commit.  This might
-  only fetch 100 lines - or only since origin/master, and fetch more
-  only when it is accessed.  Including rebase functionality here would
-  be cool.
-- editing history - using "git rebase --interactive" or similar.
-  The editor could open either a file or a diff at the time of a commit
-  and allow them to be editing.  For smaller changes, editing the diff
-  directly would be nice.  In either case, having one auto-update when the
-  other is edited would be cool
-- editing just the comments of recent commits is useful.
-- Showing a current 'diff' which is dynamically updated and which distinguishes
-  between staged and unstaged changes - and lets them be toggled - would be
-  part of this.
-
-A view on "git log" would only show the first page until you scroll down.  Then
-more would be requested and displayed.  So we don't generate thousands of commits
-unless that are actually wanted. i.e. "git log --max-count=100 --skip=N"
-This could collect but hide the commit message, and allow them to be seen later.
-The commit-ids could be marked edit, reword, etc and then rebase run.
-Would be useful to limit to certain files.
-
-
 ### a “reflection” document so I can view the internal data structures.
 
 When developing a new pane things go wrong, but it is hard to see what.
index bc7d7307d1772521fffcc2d19001032feac17c0b..b25934330de562ea68f45ca9509046f4926bd1d8 100644 (file)
@@ -426,7 +426,8 @@ static int consume_space(struct pane *p safe, int y,
                         short *y_prep safe, short *y_postp safe,
                         short *lines_above safe, short *lines_below safe,
                         int found_start, int found_end,
-                        int line_height_pre, int line_height_post)
+                        int line_height_pre, int line_height_post,
+                        bool line_at_a_time)
 {
        int y_pre = *y_prep;
        int y_post = *y_postp;
@@ -437,6 +438,12 @@ static int consume_space(struct pane *p safe, int y,
                int above, below;
                if (consume > p->h - y)
                        consume = p->h - y;
+               if (line_at_a_time && consume > 2*line_height_pre &&
+                   line_height_pre > 0)
+                       consume = 2*line_height_pre;
+               if (line_at_a_time && consume > 2*line_height_post &&
+                   line_height_post > 0)
+                       consume = 2*line_height_post;
                if (y_pre > y_post) {
                        above = consume - (consume/2);
                        below = consume/2;
@@ -458,6 +465,9 @@ static int consume_space(struct pane *p safe, int y,
                int consume = p->h - y;
                if (consume > y_pre)
                        consume = y_pre;
+               if (line_at_a_time && consume > line_height_pre &&
+                   line_height_pre > 0)
+                       consume = line_height_pre;
                y_pre -= consume;
                y += consume;
                *lines_above += consume / (line_height_pre?:1);
@@ -466,6 +476,9 @@ static int consume_space(struct pane *p safe, int y,
                int consume = p->h - y;
                if (consume > y_post)
                        consume = y_post;
+               if (line_at_a_time && consume > line_height_post &&
+                   line_height_post > 0)
+                       consume = line_height_post;
                y_post -= consume;
                y += consume;
                *lines_below += consume / (line_height_post?:1);
@@ -498,28 +511,85 @@ static int consume_space(struct pane *p safe, int y,
  *
  * If we decide to stop moving in both directions, but have not
  * reached EOF or full height of display, keep moving downwards.
+ *
+ * "start" is a mark at the start of the first line we currently
+ * intend to display, and y_pre is the number of pixel from the top
+ * of the display of that line, to the top pixel that will be displayed.
+ * We only move 'start' backward when y_pre is zero, and initially y_pre
+ * is the full height of that line.
+ *
+ * Similarly "end" is the start of the last line we currently intend
+ * to display, and y_post is the number of pixel from the bottom of that display
+ * up to the point we currently intend to display.  We only move "end" forward
+ * when y_post is zero, and when we do we set y_post to the full height of the
+ * line.
+ *
+ * Until we decide on the start or end (found_start, found_end), we
+ * repeatedly add equal parts of y_pre and y_post into the total to
+ * be display - consume_space() does this.  The space removed from y_pre
+ * and y_post is added to 'y' - the total height.
+ * It is also included into lines_above and lines_below which count text lines,
+ * rather than pixels, using line_height_pre and line_height_post as scale
+ * factors.  These are used to determine when vline or rl->margin requirements
+ * have been met.
  */
 static void find_lines(struct mark *pm safe, struct pane *p safe,
                       struct pane *focus safe,
                       int vline)
 {
        struct rl_data *rl = p->data;
+       /* orig_top/bot bound what is currently displayed and
+        * are used to determine if the display has been repositioned.
+        * orig_bot is *after* the last displayed line.  Its ->mdata
+        * will be NULL.
+        */
        struct mark *orig_top, *orig_bot;
-       struct mark *top, *bot;  // boundary of previous display
+       /* top and bot are used to enhance stability.  They are NULL
+        * if vline is given, else they match orig_top/bot.
+        */
+       struct mark *top, *bot;
        struct mark *m;
+       /* Current estimate of new display. From y_pre pixels down
+        * from the top of line at 'start', to y_post pixels up
+        * from the end of the line before 'end' there are 'y'
+        * pixel lines that we have committed to display.
+        */
        struct mark *start, *end; // current estimate for new display
+       short y_pre = 0, y_post = 0;
        short y = 0;
+       /* Number of text-lines in the committed region above or below
+        * the baseline of the line containing pm.  These lines might not
+        * all be the same height. line_height_pre/post are the heights of
+        * start and end-1 so changes in y_pre/y_post can be merged into these
+        * counts.
+        */
        short lines_above = 0, lines_below = 0; /* distance from start/end
                                                 * to pm.
                                                 */
-       short offset; // pos of pm in rendering of that line
-       bool found_start = False, found_end = False;
-       /* y_pre and y_post are measurement from start/end that
-        * haven't yet been included into lines_above/lines_below.
-        */
-       short y_pre = 0, y_post = 0;
        short line_height_pre = 1, line_height_post = 1;
 
+       short offset; // pos of pm while measureing the line holding the cursor.
+       /* We set found_start we we don't want to consider anything above the
+        * top that we currently intend to display.  Once it is set,
+        * 'start', y_pre, lines_above are all frozen.
+        * Similarly once found_end is set we freeze end, y_pos, lines_below,
+        * but we mught unfreeze those if there is room for more text at end of
+        * display.
+        * found_start is set:
+        *   - when y_pre is zero and start is at top of file
+        *   - when lines_above reaches positive vline
+        *   - when intended display has grown down into the previous
+        *     display.  This means we have added enough lines above and
+        *     don't want to scroll the display more than we need.
+        *   - When we hit unexpected errors moving backwards
+        * found_end is set:
+        *   - when we hit end-of-file
+        *   - when lines_below reached -vline
+        *   - when the top of the intended display overlaps the
+        *     previous display.
+        */
+       bool found_start = False, found_end = False;
+
        orig_top = vmark_first(focus, rl->typenum, p);
        orig_bot = vmark_last(focus, rl->typenum, p);
        /* Protect top/bot from being freed by call_render_line */
@@ -531,11 +601,16 @@ static void find_lines(struct mark *pm safe, struct pane *p safe,
        start = vmark_new(focus, rl->typenum, p);
        if (!start)
                goto abort;
+       /* FIXME why is this here.  We set ->repositioned at the end
+        * if the marks move.  Maybe we need to check if y_pre moves too.
+        */
        rl->repositioned = 1;
        mark_to_mark(start, pm);
        start = call_render_line_prev(focus, start, 0, &rl->top_sol);
        if (!start)
                goto abort;
+
+       /* Render the cursor line, and find where the cursor is. */
        offset = call_render_line_to_point(focus, pm, start);
        call_render_line(p, focus, start, NULL);
        end = vmark_next(start);
@@ -546,7 +621,6 @@ static void find_lines(struct mark *pm safe, struct pane *p safe,
        if (!rl->shift_locked)
                rl->shift_left = 0;
 
-       /* ->cy is top of cursor, we want to measure from bottom */
        if (start->mdata) {
                struct pane *hp = start->mdata;
                int curs_width;
@@ -554,7 +628,9 @@ static void find_lines(struct mark *pm safe, struct pane *p safe,
 
                curs_width = pane_attr_get_int(
                        start->mdata, "curs_width", 1);
-               while (!rl->do_wrap && !rl->shift_locked && curs_width > 0 &&
+               if (curs_width < 0)
+                       curs_width = 1;
+               while (!rl->do_wrap && !rl->shift_locked &&
                       hp->cx + curs_width >= p->w) {
                        int shift = 8 * curs_width;
                        if (shift > hp->cx)
@@ -562,6 +638,7 @@ static void find_lines(struct mark *pm safe, struct pane *p safe,
                        rl->shift_left += shift;
                        measure_line(p, focus, start, offset);
                }
+               /* ->cy is top of cursor, we want to measure from bottom */
                line_height_pre = attr_find_int(start->mdata->attrs, "line-height");
                if (line_height_pre < 1)
                        line_height_pre = 1;
@@ -573,6 +650,11 @@ static void find_lines(struct mark *pm safe, struct pane *p safe,
                y_post = 0;
        }
        if (!end) {
+               /* When cursor at EOF, leave 10% height of display
+                * blank at bottom to make this more obvious - unless
+                * the display is so small that might push the last line partly
+                * off display at the top.
+                */
                if (p->h > line_height_pre * 2)
                        y_post += p->h / 10;
                else {
@@ -586,7 +668,7 @@ static void find_lines(struct mark *pm safe, struct pane *p safe,
                y = rl->header->mdata->h;
 
        /* We have start/end of the focus line.  When rendered this,
-        * plus header and eof-footed would use y_pre + y + y_post
+        * plus header and eof-footer would use y_pre + y + y_post
         * vertical space.
         */
 
@@ -655,7 +737,8 @@ static void find_lines(struct mark *pm safe, struct pane *p safe,
                y = consume_space(p, y, &y_pre, &y_post,
                                  &lines_above, &lines_below,
                                  found_start, found_end,
-                                 line_height_pre, line_height_post);
+                                 line_height_pre, line_height_post,
+                                 vline && vline != NO_NUMERIC);
        }
        /* We might need to continue downwards even after found_end
         * if there is more space.
@@ -668,7 +751,8 @@ static void find_lines(struct mark *pm safe, struct pane *p safe,
                y = consume_space(p, y, &y_pre, &y_post,
                                  &lines_above, &lines_below,
                                  found_start, found_end,
-                                 line_height_pre, line_height_post);
+                                 line_height_pre, line_height_post,
+                                 vline && vline != NO_NUMERIC);
        }
 
        if (start->mdata && start->mdata->h <= y_pre) {