PreviousUpNext

15.4.1405  src/lib/x-kit/widget/edit/fundamental-mode.pkg

## fundamental-mode.pkg
#
# Support fns for textmill -- mostly editing                    # textmill                      is from   src/lib/x-kit/widget/edit/textmill.pkg
# fns to be bound to keystrokes.
#
# See also:
#     src/lib/x-kit/widget/edit/textpane.pkg
#     src/lib/x-kit/widget/edit/millboss-imp.pkg
#     src/lib/x-kit/widget/edit/textmill.pkg

# Compiled by:
#     src/lib/x-kit/widget/xkit-widget.sublib


stipulate
    include package   threadkit;                                # threadkit                     is from   src/lib/src/lib/thread-kit/src/core-thread-kit/threadkit.pkg
    #
#   package ap  =  client_to_atom;                              # client_to_atom                is from   src/lib/x-kit/xclient/src/iccc/client-to-atom.pkg
#   package au  =  authentication;                              # authentication                is from   src/lib/x-kit/xclient/src/stuff/authentication.pkg
#   package cpm =  cs_pixmap;                                   # cs_pixmap                     is from   src/lib/x-kit/xclient/src/window/cs-pixmap.pkg
#   package cpt =  cs_pixmat;                                   # cs_pixmat                     is from   src/lib/x-kit/xclient/src/window/cs-pixmat.pkg
#   package dy  =  display;                                     # display                       is from   src/lib/x-kit/xclient/src/wire/display.pkg
#   package fil =  file__premicrothread;                        # file__premicrothread          is from   src/lib/std/src/posix/file--premicrothread.pkg
#   package fti =  font_index;                                  # font_index                    is from   src/lib/x-kit/xclient/src/window/font-index.pkg
#   package r2k =  xevent_router_to_keymap;                     # xevent_router_to_keymap       is from   src/lib/x-kit/xclient/src/window/xevent-router-to-keymap.pkg
#   package mtx =  rw_matrix;                                   # rw_matrix                     is from   src/lib/std/src/rw-matrix.pkg
#   package rop =  ro_pixmap;                                   # ro_pixmap                     is from   src/lib/x-kit/xclient/src/window/ro-pixmap.pkg
#   package rw  =  root_window;                                 # root_window                   is from   src/lib/x-kit/widget/lib/root-window.pkg
#   package rwv =  rw_vector;                                   # rw_vector                     is from   src/lib/std/src/rw-vector.pkg
#   package sep =  client_to_selection;                         # client_to_selection           is from   src/lib/x-kit/xclient/src/window/client-to-selection.pkg
#   package shp =  shade;                                       # shade                         is from   src/lib/x-kit/widget/lib/shade.pkg
#   package sj  =  socket_junk;                                 # socket_junk                   is from   src/lib/internet/socket-junk.pkg
#   package x2s =  xclient_to_sequencer;                        # xclient_to_sequencer          is from   src/lib/x-kit/xclient/src/wire/xclient-to-sequencer.pkg
#   package tr  =  logger;                                      # logger                        is from   src/lib/src/lib/thread-kit/src/lib/logger.pkg
#   package tsr =  thread_scheduler_is_running;                 # thread_scheduler_is_running   is from   src/lib/src/lib/thread-kit/src/core-thread-kit/thread-scheduler-is-running.pkg
#   package u1  =  one_byte_unt;                                # one_byte_unt                  is from   src/lib/std/one-byte-unt.pkg
#   package v1u =  vector_of_one_byte_unts;                     # vector_of_one_byte_unts       is from   src/lib/std/src/vector-of-one-byte-unts.pkg
#   package v2w =  value_to_wire;                               # value_to_wire                 is from   src/lib/x-kit/xclient/src/wire/value-to-wire.pkg
#   package wg  =  widget;                                      # widget                        is from   src/lib/x-kit/widget/old/basic/widget.pkg
#   package wi  =  window;                                      # window                        is from   src/lib/x-kit/xclient/src/window/window.pkg
#   package wme =  window_map_event_sink;                       # window_map_event_sink         is from   src/lib/x-kit/xclient/src/window/window-map-event-sink.pkg
#   package wpp =  client_to_window_watcher;                    # client_to_window_watcher      is from   src/lib/x-kit/xclient/src/window/client-to-window-watcher.pkg
#   package wy  =  widget_style;                                # widget_style                  is from   src/lib/x-kit/widget/lib/widget-style.pkg
#   package xc  =  xclient;                                     # xclient                       is from   src/lib/x-kit/xclient/xclient.pkg
#   package xj  =  xsession_junk;                               # xsession_junk                 is from   src/lib/x-kit/xclient/src/window/xsession-junk.pkg
#   package xtr =  xlogger;                                     # xlogger                       is from   src/lib/x-kit/xclient/src/stuff/xlogger.pkg
    #

    #
    package evt =  gui_event_types;                             # gui_event_types               is from   src/lib/x-kit/widget/gui/gui-event-types.pkg
    package gts =  gui_event_to_string;                         # gui_event_to_string           is from   src/lib/x-kit/widget/gui/gui-event-to-string.pkg
    package gt  =  guiboss_types;                               # guiboss_types                 is from   src/lib/x-kit/widget/gui/guiboss-types.pkg
    package gtj =  guiboss_types_junk;                          # guiboss_types_junk            is from   src/lib/x-kit/widget/gui/guiboss-types-junk.pkg
    package lms =  list_mergesort;                              # list_mergesort                is from   src/lib/src/list-mergesort.pkg

    package a2r =  windowsystem_to_xevent_router;               # windowsystem_to_xevent_router is from   src/lib/x-kit/xclient/src/window/windowsystem-to-xevent-router.pkg

    package gd  =  gui_displaylist;                             # gui_displaylist               is from   src/lib/x-kit/widget/theme/gui-displaylist.pkg

    package pp  =  standard_prettyprinter;                      # standard_prettyprinter        is from   src/lib/prettyprint/big/src/standard-prettyprinter.pkg
    package tlj =  textlines_junk;                              # textlines_junk                is from   src/lib/x-kit/widget/edit/textlines-junk.pkg

    package err =  compiler::error_message;                     # compiler                      is from   src/lib/core/compiler/compiler.pkg
                                                                # error_message                 is from   src/lib/compiler/front/basics/errormsg/error-message.pkg

#   package sl  =  screenline;                                  # screenline                    is from   src/lib/x-kit/widget/edit/screenline.pkg
    package p2l =  textpane_to_screenline;                      # textpane_to_screenline        is from   src/lib/x-kit/widget/edit/textpane-to-screenline.pkg
    package frm =  frame;                                       # frame                         is from   src/lib/x-kit/widget/leaf/frame.pkg
    package wt  =  widget_theme;                                # widget_theme                  is from   src/lib/x-kit/widget/theme/widget/widget-theme.pkg
    package tp  =  textpane;                                    # textpane                      is from   src/lib/x-kit/widget/edit/textpane.pkg

    package ct  =  cutbuffer_types;                             # cutbuffer_types               is from   src/lib/x-kit/widget/edit/cutbuffer-types.pkg
#   package ct  =  gui_to_object_theme;                         # gui_to_object_theme           is from   src/lib/x-kit/widget/theme/object/gui-to-object-theme.pkg
#   package bt  =  gui_to_sprite_theme;                         # gui_to_sprite_theme           is from   src/lib/x-kit/widget/theme/sprite/gui-to-sprite-theme.pkg

    package psx =  posixlib;                                    # posixlib                      is from   src/lib/std/src/psx/posixlib.pkg
    package sj  =  string_junk;                                 # string_junk                   is from   src/lib/std/src/string-junk.pkg

    package boi =  spritespace_imp;                             # spritespace_imp               is from   src/lib/x-kit/widget/space/sprite/spritespace-imp.pkg
    package cai =  objectspace_imp;                             # objectspace_imp               is from   src/lib/x-kit/widget/space/object/objectspace-imp.pkg
    package pai =  widgetspace_imp;                             # widgetspace_imp               is from   src/lib/x-kit/widget/space/widget/widgetspace-imp.pkg

    #    
    package gtg =  guiboss_to_guishim;                          # guiboss_to_guishim            is from   src/lib/x-kit/widget/theme/guiboss-to-guishim.pkg

    package b2s =  spritespace_to_sprite;                       # spritespace_to_sprite         is from   src/lib/x-kit/widget/space/sprite/spritespace-to-sprite.pkg
    package c2o =  objectspace_to_object;                       # objectspace_to_object         is from   src/lib/x-kit/widget/space/object/objectspace-to-object.pkg

    package s2b =  sprite_to_spritespace;                       # sprite_to_spritespace         is from   src/lib/x-kit/widget/space/sprite/sprite-to-spritespace.pkg
    package o2c =  object_to_objectspace;                       # object_to_objectspace         is from   src/lib/x-kit/widget/space/object/object-to-objectspace.pkg

    package g2p =  gadget_to_pixmap;                            # gadget_to_pixmap              is from   src/lib/x-kit/widget/theme/gadget-to-pixmap.pkg

    package im  =  int_red_black_map;                           # int_red_black_map             is from   src/lib/src/int-red-black-map.pkg
#   package is  =  int_red_black_set;                           # int_red_black_set             is from   src/lib/src/int-red-black-set.pkg
    package idm =  id_map;                                      # id_map                        is from   src/lib/src/id-map.pkg
    package sm  =  string_map;                                  # string_map                    is from   src/lib/src/string-map.pkg

    package r8  =  rgb8;                                        # rgb8                          is from   src/lib/x-kit/xclient/src/color/rgb8.pkg
    package r64 =  rgb;                                         # rgb                           is from   src/lib/x-kit/xclient/src/color/rgb.pkg
    package g2d =  geometry2d;                                  # geometry2d                    is from   src/lib/std/2d/geometry2d.pkg
    package g2j =  geometry2d_junk;                             # geometry2d_junk               is from   src/lib/std/2d/geometry2d-junk.pkg

    package e2g =  millboss_to_guiboss;                         # millboss_to_guiboss           is from   src/lib/x-kit/widget/edit/millboss-to-guiboss.pkg
    package m2d =  mode_to_drawpane;                            # mode_to_drawpane              is from   src/lib/x-kit/widget/edit/mode-to-drawpane.pkg

    package mt  =  millboss_types;                              # millboss_types                is from   src/lib/x-kit/widget/edit/millboss-types.pkg
    package tmc =  textmill_crypts;                             # textmill_crypts               is from   src/lib/x-kit/widget/edit/textmill-crypts.pkg

    package bq  =  bounded_queue;                               # bounded_queue                 is from   src/lib/src/bounded-queue.pkg
    package nl  =  red_black_numbered_list;                     # red_black_numbered_list       is from   src/lib/src/red-black-numbered-list.pkg
    package kmj =  keystroke_macro_junk;                        # keystroke_macro_junk          is from   src/lib/x-kit/widget/edit/keystroke-macro-junk.pkg

    tracefile   =  "widget-unit-test.trace.log";

    nb = log::note_on_stderr;                                   # log                           is from   src/lib/std/src/log.pkg

herein

    package fundamental_mode {                                  # 
        #
        exception FUNDAMENTAL_MODE__STATE;                                                                      # Our per-pane persistent state (currently none).

        fun next_line (arg:     mt::Editfn_In)
            :                   mt::Editfn_Out
            =
            {   arg ->    { args:                       List( mt::Prompted_Arg ),                               # Args read interactively from user per our __editfn.args spec.
                            textlines:                  mt::Textlines,
                            point:                      g2d::Point,                                             # As in Point_And_Mark.
                            mark:                       Null_Or( g2d::Point ),                                  # 
                            lastmark:                   Null_Or( g2d::Point ),                                  # 
                            screen_origin:              g2d::Point,                                             # Origin of pane-visible text relative to textmill contents:  (0,0) means we're showing top of buffer at top of textpane.
                            visible_lines:              Int,                                                    # Number of lines of text visible in pane.
                            readonly:                   Bool,                                                   # TRUE iff contents of textmill are currently marked as read-only.
                            keystring:                  String,                                                 # User keystroke that invoked this editfn.
                            numeric_prefix:             Null_Or( Int ),                                         # ^U "Universal numeric prefix" value for this editfn if supplied by user, else NULL.
                            edit_history:               mt::Edit_History,                                       # Recent visible states of textmill, to support undo functionality.
                            pane_tag:                   Int,                                                    # Tag of pane for which this editfn is being invoked.  This is a small int for human/GUI use.
                            pane_id:                    Id,                                                     # Id  of pane for which this editfn is being invoked.
                            mill_id:                    Id,                                                     # Id  of mill for which this editfn is being invoked.
                            to:                         Replyqueue,                                             # The name makes   foo::pass_something(imp) to {. ... }   syntax read well.
                            widget_to_guiboss:          gt::Widget_To_Guiboss,                                  # 
                            mill_to_millboss:           mt::Mill_To_Millboss,
                            #
                            mainmill_modestate:         mt::Panemode_State,                                     # Any persistent per-mode state (e.g., private state for fundamental-mode.pkg) for main mill is available via this.
                            minimill_modestate:         mt::Panemode_State,                                     # Any persistent per-mode state (e.g., private state for    minimill-mode.pkg) for mini mill is available via this.
                            #
                            mill_extension_state:       Crypt,
                            textpane_to_textmill:       mt::Textpane_To_Textmill,                               # NB: We're running in textmill's microthread to guarantee atomicity, so invoking blocking textpane_to_textmill.* fns is likely to deadlock.  See Note[1].
                            mode_to_drawpane:           Null_Or( m2d::Mode_To_Drawpane ),                       # This will be non-NULL iff we specified a non-NULL draw_*_fn in our mt::PANEMODE value at bottom of file (which we do not do in this package).
                            valid_completions:          Null_Or( String -> List(String) )                       # If this is non-NULL then user is entering a commandname or filename or millname(=buffername) on the modeline, and given fn returns all valid completions of string-entered-so-far.
                          };
                point -> { row, col };

                row = row + 1;

                point =  { row, col };

                WORK [ mt::POINT point ];
            };
        next_line__editfn
            =
            mt::EDITFN (
              mt::PLAIN_EDITFN
                {
                  name   =>  "next_line",
                  doc    =>  "Move point (cursor) to next line.",
                  args   =>  [],
                  editfn =>  next_line
                }
              );                                my _ =
        mt::note_editfn  next_line__editfn;


        fun previous_line (arg: mt::Editfn_In)
            :                   mt::Editfn_Out
            =
            {   arg ->    { args:                       List( mt::Prompted_Arg ),                               # Args read interactively from user per our __editfn.args spec.
                            textlines:                  mt::Textlines,
                            point:                      g2d::Point,                                             # As in Point_And_Mark.
                            mark:                       Null_Or(g2d::Point),                                    # 
                            lastmark:                   Null_Or(g2d::Point),                                    # 
                            screen_origin:              g2d::Point,                                             # Origin of pane-visible text relative to textmill contents:  (0,0) means we're showing top of buffer at top of textpane.
                            visible_lines:              Int,                                                    # Number of lines of text visible in pane.
                            readonly:                   Bool,                                                   # TRUE iff contents of textmill are currently marked as read-only.
                            keystring:                  String,                                                 # User keystroke that invoked this editfn.
                            numeric_prefix:             Null_Or( Int ),                                         # ^U "Universal numeric prefix" value for this editfn if supplied by user, else NULL.
                            edit_history:               mt::Edit_History,                                       # Recent visible states of textmill, to support undo functionality.
                            pane_tag:                   Int,                                                    # Tag of pane for which this editfn is being invoked.  This is a small int for human/GUI use.
                            pane_id:                    Id,                                                     # Id  of pane for which this editfn is being invoked.
                            mill_id:                    Id,                                                     # Id  of mill for which this editfn is being invoked.
                            to:                         Replyqueue,                                             # The name makes   foo::pass_something(imp) to {. ... }   syntax read well.
                            widget_to_guiboss:          gt::Widget_To_Guiboss,                                  # 
                            mill_to_millboss:           mt::Mill_To_Millboss,
                            #
                            mainmill_modestate:         mt::Panemode_State,                                     # Any persistent per-mode state (e.g., private state for fundamental-mode.pkg) for main mill is available via this.
                            minimill_modestate:         mt::Panemode_State,                                     # Any persistent per-mode state (e.g., private state for    minimill-mode.pkg) for mini mill is available via this.
                            #
                            mill_extension_state:       Crypt,
                            textpane_to_textmill:       mt::Textpane_To_Textmill,                               # NB: We're running in textmill's microthread to guarantee atomicity, so invoking blocking textpane_to_textmill.* fns is likely to deadlock.  See Note[1].
                            mode_to_drawpane:           Null_Or( m2d::Mode_To_Drawpane ),                       # This will be non-NULL iff we specified a non-NULL draw_*_fn in our mt::PANEMODE value at bottom of file (which we do not do in this package).
                            valid_completions:          Null_Or( String -> List(String) )                       # If this is non-NULL then user is entering a commandname or filename or millname(=buffername) on the modeline, and given fn returns all valid completions of string-entered-so-far.
                          };
                point -> { row, col };

                row = (row > 0) ?? row - 1 :: row;

                point =  { row, col };

                WORK [ mt::POINT point ];
            };
        previous_line__editfn
            =
            mt::EDITFN (
              mt::PLAIN_EDITFN
                {
                  name   =>  "previous_line",
                  doc    =>  "Move point (cursor) to previous line.",
                  args   =>  [],
                  editfn =>  previous_line
                }
              );                                my _ =
        mt::note_editfn  previous_line__editfn;


        fun previous_char (arg: mt::Editfn_In)
            :                   mt::Editfn_Out
            =
            {   arg ->    { args:                       List( mt::Prompted_Arg ),                               # Args read interactively from user per our __editfn.args spec.
                            textlines:                  mt::Textlines,
                            point:                      g2d::Point,                                             # As in Point_And_Mark.
                            mark:                       Null_Or(g2d::Point),                                    # 
                            lastmark:                   Null_Or(g2d::Point),                                    # 
                            screen_origin:              g2d::Point,                                             # Origin of pane-visible text relative to textmill contents:  (0,0) means we're showing top of buffer at top of textpane.
                            visible_lines:              Int,                                                    # Number of lines of text visible in pane.
                            readonly:                   Bool,                                                   # TRUE iff contents of textmill are currently marked as read-only.
                            keystring:                  String,                                                 # User keystroke that invoked this editfn.
                            numeric_prefix:             Null_Or( Int ),                                         # ^U "Universal numeric prefix" value for this editfn if supplied by user, else NULL.
                            edit_history:               mt::Edit_History,                                       # Recent visible states of textmill, to support undo functionality.
                            pane_tag:                   Int,                                                    # Tag of pane for which this editfn is being invoked.  This is a small int for human/GUI use.
                            pane_id:                    Id,                                                     # Id  of pane for which this editfn is being invoked.
                            mill_id:                    Id,                                                     # Id  of mill for which this editfn is being invoked.
                            to:                         Replyqueue,                                             # The name makes   foo::pass_something(imp) to {. ... }   syntax read well.
                            widget_to_guiboss:          gt::Widget_To_Guiboss,                                  # 
                            mill_to_millboss:           mt::Mill_To_Millboss,
                            #
                            mainmill_modestate:         mt::Panemode_State,                                     # Any persistent per-mode state (e.g., private state for fundamental-mode.pkg) for main mill is available via this.
                            minimill_modestate:         mt::Panemode_State,                                     # Any persistent per-mode state (e.g., private state for    minimill-mode.pkg) for mini mill is available via this.
                            #
                            mill_extension_state:       Crypt,
                            textpane_to_textmill:       mt::Textpane_To_Textmill,                               # NB: We're running in textmill's microthread to guarantee atomicity, so invoking blocking textpane_to_textmill.* fns is likely to deadlock.  See Note[1].
                            mode_to_drawpane:           Null_Or( m2d::Mode_To_Drawpane ),                       # This will be non-NULL iff we specified a non-NULL draw_*_fn in our mt::PANEMODE value at bottom of file (which we do not do in this package).
                            valid_completions:          Null_Or( String -> List(String) )                       # If this is non-NULL then user is entering a commandname or filename or millname(=buffername) on the modeline, and given fn returns all valid completions of string-entered-so-far.
                          };
                point -> { row, col };

                line_key = row;                                                                                 # Internally lines are numbered 0->(N-1) (but we display them to user as 1-N).

                text =  mt::findline (textlines, line_key);

                text = string::chomp text;

                (string::expand_tabs_and_control_chars
                  {
                    utf8text    =>   text,
                    startcol    =>   0,
                    screencol1  =>   col,
                    screencol2  =>  -1,                                                                         # Don't-care.
                    utf8byte    =>  -1                                                                          # Don't-care.
                  })
                  ->
                  { screencol1_firstcol_on_screen:      Int,
                    screencol1_colcount_on_screen:      Int,
                    ...
                  };

                col =  screencol1_firstcol_on_screen - 1;                                                       # The point of this is that if we're on a control-char or tab (which both display on multiple screen columns) we want to move to the previous char, which may mean moving multiple screen columns.

                result =    if   (col >= 0)  WORK [ mt::POINT { row, col } ];                                   # Normal case: moved back a char within current line.
                            elif (row == 1)  FAIL "Start of buffer";                                            # Abnormal case:  Was at start of buffer, couldn't move back.
                            else                                                                                # Moved back beyond start of current line so move cursor to end of previous line.
                                text =  mt::findline (textlines, line_key - 1);

                                text = string::chomp text;

                                (string::expand_tabs_and_control_chars
                                  {
                                    utf8text    =>   text,
                                    startcol    =>   0,
                                    screencol1  =>  -1,                                                         # Don't-care.
                                    screencol2  =>  -1,                                                         # Don't-care.
                                    utf8byte    =>  -1                                                          # Don't-care.
                                  })
                                  ->
                                  { screentext_length_in_screencols:    Int,
                                    ...
                                  };

                                WORK [ mt::POINT { row => row - 1, col => screentext_length_in_screencols } ];
                            fi;

                result;
            };
        previous_char__editfn
            =
            mt::EDITFN (
              mt::PLAIN_EDITFN
                {
                  name   =>  "previous_char",
                  doc    =>  "Move point (cursor) to previous char.",
                  args   =>  [],
                  editfn =>  previous_char
                }
              );                                my _ =
        mt::note_editfn  previous_char__editfn;


        fun forward_char (arg:  mt::Editfn_In)
            :                   mt::Editfn_Out
            =
            {   arg ->    { args:                       List( mt::Prompted_Arg ),                               # Args read interactively from user per our __editfn.args spec.
                            textlines:                  mt::Textlines,
                            point:                      g2d::Point,                                             # As in Point_And_Mark.
                            mark:                       Null_Or(g2d::Point),                                    # 
                            lastmark:                   Null_Or(g2d::Point),                                    # 
                            screen_origin:              g2d::Point,                                             # Origin of pane-visible text relative to textmill contents:  (0,0) means we're showing top of buffer at top of textpane.
                            visible_lines:              Int,                                                    # Number of lines of text visible in pane.
                            readonly:                   Bool,                                                   # TRUE iff contents of textmill are currently marked as read-only.
                            keystring:                  String,                                                 # User keystroke that invoked this editfn.
                            numeric_prefix:             Null_Or( Int ),                                         # ^U "Universal numeric prefix" value for this editfn if supplied by user, else NULL.
                            edit_history:               mt::Edit_History,                                       # Recent visible states of textmill, to support undo functionality.
                            pane_tag:                   Int,                                                    # Tag of pane for which this editfn is being invoked.  This is a small int for human/GUI use.
                            pane_id:                    Id,                                                     # Id  of pane for which this editfn is being invoked.
                            mill_id:                    Id,                                                     # Id  of mill for which this editfn is being invoked.
                            to:                         Replyqueue,                                             # The name makes   foo::pass_something(imp) to {. ... }   syntax read well.
                            widget_to_guiboss:          gt::Widget_To_Guiboss,                                  # 
                            mill_to_millboss:           mt::Mill_To_Millboss,
                            #
                            mainmill_modestate:         mt::Panemode_State,                                     # Any persistent per-mode state (e.g., private state for fundamental-mode.pkg) for main mill is available via this.
                            minimill_modestate:         mt::Panemode_State,                                     # Any persistent per-mode state (e.g., private state for    minimill-mode.pkg) for mini mill is available via this.
                            #
                            mill_extension_state:       Crypt,
                            textpane_to_textmill:       mt::Textpane_To_Textmill,                               # NB: We're running in textmill's microthread to guarantee atomicity, so invoking blocking textpane_to_textmill.* fns is likely to deadlock.  See Note[1].
                            mode_to_drawpane:           Null_Or( m2d::Mode_To_Drawpane ),                       # This will be non-NULL iff we specified a non-NULL draw_*_fn in our mt::PANEMODE value at bottom of file (which we do not do in this package).
                            valid_completions:          Null_Or( String -> List(String) )                       # If this is non-NULL then user is entering a commandname or filename or millname(=buffername) on the modeline, and given fn returns all valid completions of string-entered-so-far.
                          };
                point -> { row, col };

                line_key = row;                                                                                 # Internally lines are numbered 0->(N-1) (but we display them to user as 1-N).

                text =  mt::findline (textlines, line_key);

                text =  string::chomp  text;

                (string::expand_tabs_and_control_chars
                  {
                    utf8text    =>   text,
                    startcol    =>   0,
                    screencol1  =>   col,
                    screencol2  =>  -1,                                                                         # Don't-care.
                    utf8byte    =>  -1                                                                          # Don't-care.
                  })
                  ->
                  { screencol1_firstcol_on_screen:      Int,
                    screencol1_colcount_on_screen:      Int,
                    screentext_length_in_screencols:    Int,
                    ...
                  };

                col =  screencol1_firstcol_on_screen + screencol1_colcount_on_screen;                           # The point of this is that if we're on a control-char or tab (which both display on multiple screen columns) we want to move to the next char, which may mean moving multiple screen columns.

                point =     if (col <= screentext_length_in_screencols)
                                #
                                { row, col };                                                                   # Move right within line.
                            else
                                { row => row + 1, col => 0 };                                                   # Wrap around at end of line to start of next line.
                            fi;

                WORK [ mt::POINT point ];
            };
        forward_char__editfn
            =
            mt::EDITFN (
              mt::PLAIN_EDITFN
                {
                  name   =>  "forward_char",
                  doc    =>  "Move point (cursor) to next char.",
                  args   =>  [],
                  editfn =>  forward_char
                }
              );                                my _ =
        mt::note_editfn  forward_char__editfn;


        fun move_beginning_of_line (arg:        mt::Editfn_In)
            :                                   mt::Editfn_Out
            =
            {   arg ->    { args:                       List( mt::Prompted_Arg ),                               # Args read interactively from user per our __editfn.args spec.
                            textlines:                  mt::Textlines,
                            point:                      g2d::Point,                                             # As in Point_And_Mark.
                            mark:                       Null_Or(g2d::Point),                                    # 
                            lastmark:                   Null_Or(g2d::Point),                                    # 
                            screen_origin:              g2d::Point,                                             # Origin of pane-visible text relative to textmill contents:  (0,0) means we're showing top of buffer at top of textpane.
                            visible_lines:              Int,                                                    # Number of lines of text visible in pane.
                            readonly:                   Bool,                                                   # TRUE iff contents of textmill are currently marked as read-only.
                            keystring:                  String,                                                 # User keystroke that invoked this editfn.
                            numeric_prefix:             Null_Or( Int ),                                         # ^U "Universal numeric prefix" value for this editfn if supplied by user, else NULL.
                            edit_history:               mt::Edit_History,                                       # Recent visible states of textmill, to support undo functionality.
                            pane_tag:                   Int,                                                    # Tag of pane for which this editfn is being invoked.  This is a small int for human/GUI use.
                            pane_id:                    Id,                                                     # Id  of pane for which this editfn is being invoked.
                            mill_id:                    Id,                                                     # Id  of mill for which this editfn is being invoked.
                            to:                         Replyqueue,                                             # The name makes   foo::pass_something(imp) to {. ... }   syntax read well.
                            widget_to_guiboss:          gt::Widget_To_Guiboss,                                  # 
                            mill_to_millboss:           mt::Mill_To_Millboss,
                            #
                            mainmill_modestate:         mt::Panemode_State,                                     # Any persistent per-mode state (e.g., private state for fundamental-mode.pkg) for main mill is available via this.
                            minimill_modestate:         mt::Panemode_State,                                     # Any persistent per-mode state (e.g., private state for    minimill-mode.pkg) for mini mill is available via this.
                            #
                            mill_extension_state:       Crypt,
                            textpane_to_textmill:       mt::Textpane_To_Textmill,                               # NB: We're running in textmill's microthread to guarantee atomicity, so invoking blocking textpane_to_textmill.* fns is likely to deadlock.  See Note[1].
                            mode_to_drawpane:           Null_Or( m2d::Mode_To_Drawpane ),                       # This will be non-NULL iff we specified a non-NULL draw_*_fn in our mt::PANEMODE value at bottom of file (which we do not do in this package).
                            valid_completions:          Null_Or( String -> List(String) )                       # If this is non-NULL then user is entering a commandname or filename or millname(=buffername) on the modeline, and given fn returns all valid completions of string-entered-so-far.
                          };
                point -> { row, col };

                point =  { row, col => 0 };

                WORK [ mt::POINT point ];
            };
        move_beginning_of_line__editfn
            =
            mt::EDITFN (
              mt::PLAIN_EDITFN
                {
                  name   =>  "move_beginning_of_line",
                  doc    =>  "Move point (cursor) to start of current line.",
                  args   =>  [],
                  editfn =>  move_beginning_of_line
                }
              );                                my _ =
        mt::note_editfn  move_beginning_of_line__editfn;


        fun move_end_of_line (arg:              mt::Editfn_In)
            :                                   mt::Editfn_Out
            =
            {   arg ->    { args:                       List( mt::Prompted_Arg ),                               # Args read interactively from user per our __editfn.args spec.
                            textlines:                  mt::Textlines,
                            point:                      g2d::Point,                                             # As in Point_And_Mark.
                            mark:                       Null_Or(g2d::Point),                                    # 
                            lastmark:                   Null_Or(g2d::Point),                                    # 
                            screen_origin:              g2d::Point,                                             # Origin of pane-visible text relative to textmill contents:  (0,0) means we're showing top of buffer at top of textpane.
                            visible_lines:              Int,                                                    # Number of lines of text visible in pane.
                            readonly:                   Bool,                                                   # TRUE iff contents of textmill are currently marked as read-only.
                            keystring:                  String,                                                 # User keystroke that invoked this editfn.
                            numeric_prefix:             Null_Or( Int ),                                         # ^U "Universal numeric prefix" value for this editfn if supplied by user, else NULL.
                            edit_history:               mt::Edit_History,                                       # Recent visible states of textmill, to support undo functionality.
                            pane_tag:                   Int,                                                    # Tag of pane for which this editfn is being invoked.  This is a small int for human/GUI use.
                            pane_id:                    Id,                                                     # Id  of pane for which this editfn is being invoked.
                            mill_id:                    Id,                                                     # Id  of mill for which this editfn is being invoked.
                            to:                         Replyqueue,                                             # The name makes   foo::pass_something(imp) to {. ... }   syntax read well.
                            widget_to_guiboss:          gt::Widget_To_Guiboss,                                  # 
                            mill_to_millboss:           mt::Mill_To_Millboss,
                            #
                            mainmill_modestate:         mt::Panemode_State,                                     # Any persistent per-mode state (e.g., private state for fundamental-mode.pkg) for main mill is available via this.
                            minimill_modestate:         mt::Panemode_State,                                     # Any persistent per-mode state (e.g., private state for    minimill-mode.pkg) for mini mill is available via this.
                            #
                            mill_extension_state:       Crypt,
                            textpane_to_textmill:       mt::Textpane_To_Textmill,                               # NB: We're running in textmill's microthread to guarantee atomicity, so invoking blocking textpane_to_textmill.* fns is likely to deadlock.  See Note[1].
                            mode_to_drawpane:           Null_Or( m2d::Mode_To_Drawpane ),                       # This will be non-NULL iff we specified a non-NULL draw_*_fn in our mt::PANEMODE value at bottom of file (which we do not do in this package).
                            valid_completions:          Null_Or( String -> List(String) )                       # If this is non-NULL then user is entering a commandname or filename or millname(=buffername) on the modeline, and given fn returns all valid completions of string-entered-so-far.
                          };
                point -> { row, col };

                line_key = row;                                                                                 # Internally lines are numbered 0->(N-1) (but we display them to user as 1-N).

                text =  mt::findline (textlines, line_key);

                text =  string::chomp  text;

                (string::expand_tabs_and_control_chars
                  {
                    utf8text    =>  text,
                    startcol    =>  0,
                    screencol1  => -1,                                                                          # Don't-care.
                    screencol2  => -1,                                                                          # Don't-care.
                    utf8byte    => -1                                                                           # Don't-care.
                  })
                  ->
                  { screentext_length_in_screencols:    Int,
                    ...
                  };

                point =  { row, col => screentext_length_in_screencols };

                WORK [ mt::POINT point ];
            };
        move_end_of_line__editfn
            =
            mt::EDITFN (
              mt::PLAIN_EDITFN
                {
                  name   =>  "move_end_of_line",
                  doc    =>  "Move point (cursor) to start of current line.",
                  args   =>  [],
                  editfn =>  move_end_of_line
                }
              );                                my _ =
        mt::note_editfn  move_end_of_line__editfn;


        fun delete_one_char                                                                                     # Implements functionality common to delete_char and delete_backward_char.
              (
                textlines:      mt::Textlines,
                point:          g2d::Point,
                mark:           Null_Or(g2d::Point)                                                             # 
              )
            :                                   mt::Editfn_Out
            =
            {   point -> { row, col };
                #
                line_key = row;                                                                                 # Internally lines are numbered 0->(N-1) (but we display them to user as 1-N).

                result
                    =
                    case (nl::find (textlines, line_key))
                        #
                        THE textline
                            =>
                            {   text         =  mt::visible_line textline;
                                chomped_text =  string::chomp    text;

                                (string::expand_tabs_and_control_chars
                                  {
                                    utf8text    =>  chomped_text,
                                    startcol    =>  0,
                                    screencol1  =>  col,
                                    screencol2  => -1,                                                  # Don't-care.
                                    utf8byte    => -1                                                   # Don't-care.
                                  })
                                  ->
                                  { screentext_length_in_screencols:    Int,
                                    #
                                    screencol1_byteoffset_in_utf8text:  Int,
                                    screencol1_bytescount_in_utf8text:  Int,
                                    ...
                                  };

                                if (col >= screentext_length_in_screencols)
                                    #
                                    WORK [ ];                                                                   # Cursor is on non-existent char past end of existing line.  Don't fail, but don't do anything either. (emacs deletes the end-of-line newline here, but I prefer to have only kill_line do that.)
                                else
                                                                                                                # Cursor is on an existing char, possibly a multibyte utf8 char.  Excise it by replacing the line with the concatenation of the substrings preceding and following the char.
                                    text_before_point
                                        =
                                        string::substring
                                          (
                                            text,                                                               # String from which to extract substring.
                                            0,                                                                  # The substring we want starts at offset 0.
                                            screencol1_byteoffset_in_utf8text                                   # The substring we want runs to location of cursor.  Treating cursor offset as length works (only) because we're starting substring at offset zero.
                                          );

                                    text_beyond_point
                                        =
                                        string::extract
                                          (
                                            text,                                                               # String from which to extract substring.
                                            screencol1_byteoffset_in_utf8text + screencol1_bytescount_in_utf8text,      # Substring starts immediately after the byte(s) under the cursor.  (Cursor will mark multiple bytes only if it is on a multibyte utf8 char.)
                                            NULL                                                                # Substring runs to end of 'text'.
                                          );

                                    updated_text        =  string::cat [ text_before_point,
                                                                         text_beyond_point
                                                                       ];

                                    updated_text        =  mt::MONOLINE { string =>  updated_text,
                                                                          prefix =>  NULL
                                                                        };

                                    updated_textlines                                                           # First remove existing line -- nl::set does NOT remove any previous line at that key.
                                        =
                                        (nl::remove (textlines, line_key))
                                        except _ = textlines;                                                   # This will happen if there is no line 'line_key' in textlines.

                                    updated_textlines                                                           # Now insert updated line.
                                        =
                                        nl::set (updated_textlines, line_key, updated_text);

                                    WORK  [ mt::TEXTLINES updated_textlines,
                                            mt::POINT { row, col }                                              # Needed for delete_backward_char, where cursor position changes.
                                          ];
                                fi;     
                            };

                        NULL     => WORK [ ];                                                                   # Cursor is on non-existent line.  Don't fail, but don't do anything either.
                    esac;

                result;
            };

        fun delete_char (arg:                   mt::Editfn_In)
            :                                   mt::Editfn_Out
            =
            {   arg ->    { args:                       List( mt::Prompted_Arg ),                                       # Args read interactively from user per our __editfn.args spec.
                            textlines:                  mt::Textlines,
                            point:                      g2d::Point,                                                     # As in Point_And_Mark.
                            mark:                       Null_Or(g2d::Point),                                            # 
                            lastmark:                   Null_Or(g2d::Point),                                            # 
                            screen_origin:              g2d::Point,                                                     # Origin of pane-visible text relative to textmill contents:  (0,0) means we're showing top of buffer at top of textpane.
                            visible_lines:              Int,                                                            # Number of lines of text visible in pane.
                            readonly:                   Bool,                                                           # TRUE iff contents of textmill are currently marked as read-only.
                            keystring:                  String,                                                         # User keystroke that invoked this editfn.
                            numeric_prefix:             Null_Or( Int ),                                                 # ^U "Universal numeric prefix" value for this editfn if supplied by user, else NULL.
                            edit_history:               mt::Edit_History,                                               # Recent visible states of textmill, to support undo functionality.
                            pane_tag:                   Int,                                                            # Tag of pane for which this editfn is being invoked.  This is a small int for human/GUI use.
                            pane_id:                    Id,                                                             # Id  of pane for which this editfn is being invoked.
                            mill_id:                    Id,                                                             # Id  of mill for which this editfn is being invoked.
                            to:                         Replyqueue,                                                     # The name makes   foo::pass_something(imp) to {. ... }   syntax read well.
                            widget_to_guiboss:          gt::Widget_To_Guiboss,                                          # 
                            mill_to_millboss:           mt::Mill_To_Millboss,
                            #
                            mainmill_modestate:         mt::Panemode_State,                                             # Any persistent per-mode state (e.g., private state for fundamental-mode.pkg) for main mill is available via this.
                            minimill_modestate:         mt::Panemode_State,                                             # Any persistent per-mode state (e.g., private state for    minimill-mode.pkg) for mini mill is available via this.
                            #
                            mill_extension_state:       Crypt,
                            textpane_to_textmill:       mt::Textpane_To_Textmill,                                       # NB: We're running in textmill's microthread to guarantee atomicity, so invoking blocking textpane_to_textmill.* fns is likely to deadlock.  See Note[1].
                            mode_to_drawpane:           Null_Or( m2d::Mode_To_Drawpane ),                               # This will be non-NULL iff we specified a non-NULL draw_*_fn in our mt::PANEMODE value at bottom of file (which we do not do in this package).
                            valid_completions:          Null_Or( String -> List(String) )                               # If this is non-NULL then user is entering a commandname or filename or millname(=buffername) on the modeline, and given fn returns all valid completions of string-entered-so-far.
                          };

                if readonly
                    #
                    FAIL "Buffer is read-only.";
                else
                    point -> { row, col };

                    delete_one_char (textlines, point, mark);                                                           # Code shared with delete_backward_char.
                fi;
            };
        delete_char__editfn
            =
            mt::EDITFN (
              mt::PLAIN_EDITFN
                {
                  name   =>  "delete_char",
                  doc    =>  "Delete char under point (cursor).",
                  args   =>  [],
                  editfn =>  delete_char
                }
              );                                my _ =
        mt::note_editfn  delete_char__editfn;


        fun delete_backward_char (arg:          mt::Editfn_In)
            :                                   mt::Editfn_Out
            =
            {   arg ->    { args:                       List( mt::Prompted_Arg ),                                       # Args read interactively from user per our __editfn.args spec.
                            textlines:                  mt::Textlines,
                            point:                      g2d::Point,                                                     # As in Point_And_Mark.
                            mark:                       Null_Or(g2d::Point),                                            # 
                            lastmark:                   Null_Or(g2d::Point),                                            # 
                            screen_origin:              g2d::Point,                                                     # Origin of pane-visible text relative to textmill contents:  (0,0) means we're showing top of buffer at top of textpane.
                            visible_lines:              Int,                                                            # Number of lines of text visible in pane.
                            readonly:                   Bool,                                                           # TRUE iff contents of textmill are currently marked as read-only.
                            keystring:                  String,                                                         # User keystroke that invoked this editfn.
                            numeric_prefix:             Null_Or( Int ),                                                 # ^U "Universal numeric prefix" value for this editfn if supplied by user, else NULL.
                            edit_history:               mt::Edit_History,                                               # Recent visible states of textmill, to support undo functionality.
                            pane_tag:                   Int,                                                            # Tag of pane for which this editfn is being invoked.  This is a small int for human/GUI use.
                            pane_id:                    Id,                                                             # Id  of pane for which this editfn is being invoked.
                            mill_id:                    Id,                                                             # Id  of mill for which this editfn is being invoked.
                            to:                         Replyqueue,                                                     # The name makes   foo::pass_something(imp) to {. ... }   syntax read well.
                            widget_to_guiboss:          gt::Widget_To_Guiboss,                                          # 
                            mill_to_millboss:           mt::Mill_To_Millboss,
                            #
                            mainmill_modestate:         mt::Panemode_State,                                             # Any persistent per-mode state (e.g., private state for fundamental-mode.pkg) for main mill is available via this.
                            minimill_modestate:         mt::Panemode_State,                                             # Any persistent per-mode state (e.g., private state for    minimill-mode.pkg) for mini mill is available via this.
                            #
                            mill_extension_state:       Crypt,
                            textpane_to_textmill:       mt::Textpane_To_Textmill,                                       # NB: We're running in textmill's microthread to guarantee atomicity, so invoking blocking textpane_to_textmill.* fns is likely to deadlock.  See Note[1].
                            mode_to_drawpane:           Null_Or( m2d::Mode_To_Drawpane ),                               # This will be non-NULL iff we specified a non-NULL draw_*_fn in our mt::PANEMODE value at bottom of file (which we do not do in this package).
                            valid_completions:          Null_Or( String -> List(String) )                               # If this is non-NULL then user is entering a commandname or filename or millname(=buffername) on the modeline, and given fn returns all valid completions of string-entered-so-far.
                          };

                if readonly
                    #
                    FAIL "Buffer is read-only";
                else    
                    point -> { row, col };

                    if (col > 0)
                        #
                        col = col - 1;

                        point = { row, col };

                        delete_one_char (textlines, point, mark);                                                               # Code shared with delete_char.
                        #
                    elif (row > 1)                                                                                              # Delete preceding newline, appending current line to previous line.
                        #
                        point -> { row, col };

                        line_key2 =        row;                                                                                 # Internally lines are numbered 0->(N-1) (but we display them to user as 1-N).
                        line_key1 =  line_key2 - 1;                                                                             # 

                        result =    case (nl::find (textlines, line_key1), nl::find (textlines, line_key2))
                                        #
                                        (THE textline1, THE textline2)
                                            =>
                                            {   line1 = mt::visible_line textline1;
                                                line2 = mt::visible_line textline2;

                                                chomped_line1 =  string::chomp  line1;
                                                #
                                                (string::expand_tabs_and_control_chars
                                                  {
                                                    utf8text   =>  chomped_line1,
                                                    startcol   =>  0,
                                                    screencol1 =>  col,
                                                    screencol2 => -1,                                                           # Don't-care.
                                                    utf8byte   => -1                                                            # Don't-care.
                                                  })
                                                  ->
                                                  { screentext_length_in_screencols:            Int,
                                                    ...
                                                  };

                                                line12 = string::cat [ chomped_line1, line2 ];                                  # Prepend line1 (sans newline) to line2 to produce replacement for the pair of them.

                                                line12 = mt::MONOLINE { string =>  line12,
                                                                        prefix =>  NULL
                                                                      };

                                                updated_textlines                                                               # First remove existing two lines -- nl::set does NOT remove any previous line at that key.
                                                    =
                                                    {   updated_textlines = nl::remove (textlines,         line_key1);
                                                        updated_textlines = nl::remove (updated_textlines, line_key1);

                                                        updated_textlines;
                                                    }   
                                                    except _ = textlines;                                                       # This will happen if there is no line 'line_key' in textlines.

                                                updated_textlines                                                               # Now insert updated line.
                                                    =
                                                    nl::set (updated_textlines, line_key1, line12);

                                                WORK  [ mt::TEXTLINES updated_textlines,
                                                        mt::POINT { row => row - 1, col => screentext_length_in_screencols }    # Position cursor at end of previous line1 -- start of contents of merged-in former line2.
                                                      ];
                                            };

                                        _  => FAIL "<???>";                                                                     # Should maybe think harder about how/if this case can happen and if it can, what we should be doing. XXX SUCKO FIXME.
                                    esac;

                        result;
                    else
                        FAIL "No preceding char in line to delete";                                                             # Fail: No preceding char to delete in line.
                    fi;
                fi;
            };
        delete_backward_char__editfn
            =
            mt::EDITFN (
              mt::PLAIN_EDITFN
                {
                  name   =>  "delete_backward_char",
                  doc    =>  "Delete char to left of point (cursor).",
                  args   =>  [],
                  editfn =>  delete_backward_char
                }
              );                                my _ =
        mt::note_editfn  delete_backward_char__editfn;


        fun list_mills (arg:                            mt::Editfn_In)
            :                                           mt::Editfn_Out
            =
            {   arg ->    { args:                       List( mt::Prompted_Arg ),                                       # Args read interactively from user per our __editfn.args spec.
                            textlines:                  mt::Textlines,
                            point:                      g2d::Point,                                                     # As in Point_And_Mark.
                            mark:                       Null_Or(g2d::Point),                                            # 
                            lastmark:                   Null_Or(g2d::Point),                                            # 
                            screen_origin:              g2d::Point,                                                     # Origin of pane-visible text relative to textmill contents:  (0,0) means we're showing top of buffer at top of textpane.
                            visible_lines:              Int,                                                            # Number of lines of text visible in pane.
                            readonly:                   Bool,                                                           # TRUE iff contents of textmill are currently marked as read-only.
                            keystring:                  String,                                                         # User keystroke that invoked this editfn.
                            numeric_prefix:             Null_Or( Int ),                                                 # ^U "Universal numeric prefix" value for this editfn if supplied by user, else NULL.
                            edit_history:               mt::Edit_History,                                               # Recent visible states of textmill, to support undo functionality.
                            pane_tag:                   Int,                                                            # Tag of pane for which this editfn is being invoked.  This is a small int for human/GUI use.
                            pane_id:                    Id,                                                             # Id  of pane for which this editfn is being invoked.
                            mill_id:                    Id,                                                             # Id  of mill for which this editfn is being invoked.
                            to:                         Replyqueue,                                                     # The name makes   foo::pass_something(imp) to {. ... }   syntax read well.
                            widget_to_guiboss:          gt::Widget_To_Guiboss,                                          # 
                            mill_to_millboss:           mt::Mill_To_Millboss,
                            #
                            mainmill_modestate:         mt::Panemode_State,                                             # Any persistent per-mode state (e.g., private state for fundamental-mode.pkg) for main mill is available via this.
                            minimill_modestate:         mt::Panemode_State,                                             # Any persistent per-mode state (e.g., private state for    minimill-mode.pkg) for mini mill is available via this.
                            #
                            mill_extension_state:       Crypt,
                            textpane_to_textmill:       mt::Textpane_To_Textmill,                                       # NB: We're running in textmill's microthread to guarantee atomicity, so invoking blocking textpane_to_textmill.* fns is likely to deadlock.  See Note[1].
                            mode_to_drawpane:           Null_Or( m2d::Mode_To_Drawpane ),                               # This will be non-NULL iff we specified a non-NULL draw_*_fn in our mt::PANEMODE value at bottom of file (which we do not do in this package).
                            valid_completions:          Null_Or( String -> List(String) )                               # If this is non-NULL then user is entering a commandname or filename or millname(=buffername) on the modeline, and given fn returns all valid completions of string-entered-so-far.
                          };

                WORK [ ];                                                                                               # 
            };
        list_mills__editfn
            =
            mt::EDITFN (
              mt::PLAIN_EDITFN
                {
                  name   =>  "list_mills",
                  doc    =>  "List running mills in new pane.",
                  args   =>  [],
                  editfn =>  list_mills
                }
              );                                my _ =
        mt::note_editfn  list_mills__editfn;


        fun kill_mill (arg:                             mt::Editfn_In)
            :                                           mt::Editfn_Out
            =
            {   arg ->    { args:                       List( mt::Prompted_Arg ),                                       # Args read interactively from user per our __editfn.args spec.
                            textlines:                  mt::Textlines,
                            point:                      g2d::Point,                                                     # As in Point_And_Mark.
                            mark:                       Null_Or(g2d::Point),                                            # 
                            lastmark:                   Null_Or(g2d::Point),                                            # 
                            screen_origin:              g2d::Point,                                                     # Origin of pane-visible text relative to textmill contents:  (0,0) means we're showing top of buffer at top of textpane.
                            visible_lines:              Int,                                                            # Number of lines of text visible in pane.
                            readonly:                   Bool,                                                           # TRUE iff contents of textmill are currently marked as read-only.
                            keystring:                  String,                                                         # User keystroke that invoked this editfn.
                            numeric_prefix:             Null_Or( Int ),                                                 # ^U "Universal numeric prefix" value for this editfn if supplied by user, else NULL.
                            edit_history:               mt::Edit_History,                                               # Recent visible states of textmill, to support undo functionality.
                            pane_tag:                   Int,                                                            # Tag of pane for which this editfn is being invoked.  This is a small int for human/GUI use.
                            pane_id:                    Id,                                                             # Id  of pane for which this editfn is being invoked.
                            mill_id:                    Id,                                                             # Id  of mill for which this editfn is being invoked.
                            to:                         Replyqueue,                                                     # The name makes   foo::pass_something(imp) to {. ... }   syntax read well.
                            widget_to_guiboss:          gt::Widget_To_Guiboss,                                          # 
                            mill_to_millboss:           mt::Mill_To_Millboss,
                            #
                            mainmill_modestate:         mt::Panemode_State,                                             # Any persistent per-mode state (e.g., private state for fundamental-mode.pkg) for main mill is available via this.
                            minimill_modestate:         mt::Panemode_State,                                             # Any persistent per-mode state (e.g., private state for    minimill-mode.pkg) for mini mill is available via this.
                            #
                            mill_extension_state:       Crypt,
                            textpane_to_textmill:       mt::Textpane_To_Textmill,                                       # NB: We're running in textmill's microthread to guarantee atomicity, so invoking blocking textpane_to_textmill.* fns is likely to deadlock.  See Note[1].
                            mode_to_drawpane:           Null_Or( m2d::Mode_To_Drawpane ),                               # This will be non-NULL iff we specified a non-NULL draw_*_fn in our mt::PANEMODE value at bottom of file (which we do not do in this package).
                            valid_completions:          Null_Or( String -> List(String) )                               # If this is non-NULL then user is entering a commandname or filename or millname(=buffername) on the modeline, and given fn returns all valid completions of string-entered-so-far.
                          };


                WORK [ ];                                                                                               # 
            };
        kill_mill__editfn
            =
            mt::EDITFN (
              mt::PLAIN_EDITFN
                {
                  name   =>  "kill_mill",
                  doc    =>  "Kill mill underlying current pane.",
                  args   =>  [],
                  editfn =>  kill_mill
                }
              );                                my _ =
        mt::note_editfn  kill_mill__editfn;


        fun save_some_mills (arg:                       mt::Editfn_In)
            :                                           mt::Editfn_Out
            =
            {   arg ->    { args:                       List( mt::Prompted_Arg ),                                       # Args read interactively from user per our __editfn.args spec.
                            textlines:                  mt::Textlines,
                            point:                      g2d::Point,                                                     # As in Point_And_Mark.
                            mark:                       Null_Or(g2d::Point),                                            # 
                            lastmark:                   Null_Or(g2d::Point),                                            # 
                            screen_origin:              g2d::Point,                                                     # Origin of pane-visible text relative to textmill contents:  (0,0) means we're showing top of buffer at top of textpane.
                            visible_lines:              Int,                                                            # Number of lines of text visible in pane.
                            readonly:                   Bool,                                                           # TRUE iff contents of textmill are currently marked as read-only.
                            keystring:                  String,                                                         # User keystroke that invoked this editfn.
                            numeric_prefix:             Null_Or( Int ),                                                 # ^U "Universal numeric prefix" value for this editfn if supplied by user, else NULL.
                            edit_history:               mt::Edit_History,                                               # Recent visible states of textmill, to support undo functionality.
                            pane_tag:                   Int,                                                            # Tag of pane for which this editfn is being invoked.  This is a small int for human/GUI use.
                            pane_id:                    Id,                                                             # Id  of pane for which this editfn is being invoked.
                            mill_id:                    Id,                                                             # Id  of mill for which this editfn is being invoked.
                            to:                         Replyqueue,                                                     # The name makes   foo::pass_something(imp) to {. ... }   syntax read well.
                            widget_to_guiboss:          gt::Widget_To_Guiboss,                                          # 
                            mill_to_millboss:           mt::Mill_To_Millboss,
                            #
                            mainmill_modestate:         mt::Panemode_State,                                             # Any persistent per-mode state (e.g., private state for fundamental-mode.pkg) for main mill is available via this.
                            minimill_modestate:         mt::Panemode_State,                                             # Any persistent per-mode state (e.g., private state for    minimill-mode.pkg) for mini mill is available via this.
                            #
                            mill_extension_state:       Crypt,
                            textpane_to_textmill:       mt::Textpane_To_Textmill,                                       # NB: We're running in textmill's microthread to guarantee atomicity, so invoking blocking textpane_to_textmill.* fns is likely to deadlock.  See Note[1].
                            mode_to_drawpane:           Null_Or( m2d::Mode_To_Drawpane ),                               # This will be non-NULL iff we specified a non-NULL draw_*_fn in our mt::PANEMODE value at bottom of file (which we do not do in this package).
                            valid_completions:          Null_Or( String -> List(String) )                               # If this is non-NULL then user is entering a commandname or filename or millname(=buffername) on the modeline, and given fn returns all valid completions of string-entered-so-far.
                          };


                WORK [ ];                                                                                               # 
            };
        save_some_mills__editfn
            =
            mt::EDITFN (
              mt::PLAIN_EDITFN
                {
                  name   =>  "save_some_mills",
                  doc    =>  "Save state of any dirty mills with savefiles.",
                  args   =>  [],
                  editfn =>  save_some_mills
                }
              );                                my _ =
        mt::note_editfn  save_some_mills__editfn;


        fun save_mills_kill_mythryl (arg:               mt::Editfn_In)
            :                                           mt::Editfn_Out
            =
            {   arg ->    { args:                       List( mt::Prompted_Arg ),                                       # Args read interactively from user per our __editfn.args spec.
                            textlines:                  mt::Textlines,
                            point:                      g2d::Point,                                                     # As in Point_And_Mark.
                            mark:                       Null_Or(g2d::Point),                                            # 
                            lastmark:                   Null_Or(g2d::Point),                                            # 
                            screen_origin:              g2d::Point,                                                     # Origin of pane-visible text relative to textmill contents:  (0,0) means we're showing top of buffer at top of textpane.
                            visible_lines:              Int,                                                            # Number of lines of text visible in pane.
                            readonly:                   Bool,                                                           # TRUE iff contents of textmill are currently marked as read-only.
                            keystring:                  String,                                                         # User keystroke that invoked this editfn.
                            numeric_prefix:             Null_Or( Int ),                                                 # ^U "Universal numeric prefix" value for this editfn if supplied by user, else NULL.
                            edit_history:               mt::Edit_History,                                               # Recent visible states of textmill, to support undo functionality.
                            pane_tag:                   Int,                                                            # Tag of pane for which this editfn is being invoked.  This is a small int for human/GUI use.
                            pane_id:                    Id,                                                             # Id  of pane for which this editfn is being invoked.
                            mill_id:                    Id,                                                             # Id  of mill for which this editfn is being invoked.
                            to:                         Replyqueue,                                                     # The name makes   foo::pass_something(imp) to {. ... }   syntax read well.
                            widget_to_guiboss:          gt::Widget_To_Guiboss,                                          # 
                            mill_to_millboss:           mt::Mill_To_Millboss,
                            #
                            mainmill_modestate:         mt::Panemode_State,                                             # Any persistent per-mode state (e.g., private state for fundamental-mode.pkg) for main mill is available via this.
                            minimill_modestate:         mt::Panemode_State,                                             # Any persistent per-mode state (e.g., private state for    minimill-mode.pkg) for mini mill is available via this.
                            #
                            mill_extension_state:       Crypt,
                            textpane_to_textmill:       mt::Textpane_To_Textmill,                                       # NB: We're running in textmill's microthread to guarantee atomicity, so invoking blocking textpane_to_textmill.* fns is likely to deadlock.  See Note[1].
                            mode_to_drawpane:           Null_Or( m2d::Mode_To_Drawpane ),                               # This will be non-NULL iff we specified a non-NULL draw_*_fn in our mt::PANEMODE value at bottom of file (which we do not do in this package).
                            valid_completions:          Null_Or( String -> List(String) )                               # If this is non-NULL then user is entering a commandname or filename or millname(=buffername) on the modeline, and given fn returns all valid completions of string-entered-so-far.
                          };

                widget_to_guiboss.g.shut_down_guiboss ();

                WORK [ ];                                                                                               # 
            };
        save_mills_kill_mythryl__editfn
            =
            mt::EDITFN (
              mt::PLAIN_EDITFN
                {
                  name   =>  "save_mills_kill_mythryl",
                  doc    =>  "Save state of all running mills then exit.",
                  args   =>  [],
                  editfn =>  save_mills_kill_mythryl
                }
              );                                my _ =
        mt::note_editfn  save_mills_kill_mythryl__editfn;


        fun self_insert_command (arg:           mt::Editfn_In)
            :                                   mt::Editfn_Out
            =
            {   arg ->    { args:                       List( mt::Prompted_Arg ),                                       # Args read interactively from user per our __editfn.args spec.
                            textlines:                  mt::Textlines,
                            point:                      g2d::Point,                                                     # As in Point_And_Mark.
                            mark:                       Null_Or(g2d::Point),                                            # 
                            lastmark:                   Null_Or(g2d::Point),                                            # 
                            screen_origin:              g2d::Point,                                                     # Origin of pane-visible text relative to textmill contents:  (0,0) means we're showing top of buffer at top of textpane.
                            visible_lines:              Int,                                                            # Number of lines of text visible in pane.
                            readonly:                   Bool,                                                           # TRUE iff contents of textmill are currently marked as read-only.
                            keystring:                  String,                                                         # User keystroke that invoked this editfn.
                            numeric_prefix:             Null_Or( Int ),                                                 # ^U "Universal numeric prefix" value for this editfn if supplied by user, else NULL.
                            edit_history:               mt::Edit_History,                                               # Recent visible states of textmill, to support undo functionality.
                            pane_tag:                   Int,                                                            # Tag of pane for which this editfn is being invoked.  This is a small int for human/GUI use.
                            pane_id:                    Id,                                                             # Id  of pane for which this editfn is being invoked.
                            mill_id:                    Id,                                                             # Id  of mill for which this editfn is being invoked.
                            to:                         Replyqueue,                                                     # The name makes   foo::pass_something(imp) to {. ... }   syntax read well.
                            widget_to_guiboss:          gt::Widget_To_Guiboss,                                          # 
                            mill_to_millboss:           mt::Mill_To_Millboss,
                            #
                            mainmill_modestate:         mt::Panemode_State,                                             # Any persistent per-mode state (e.g., private state for fundamental-mode.pkg) for main mill is available via this.
                            minimill_modestate:         mt::Panemode_State,                                             # Any persistent per-mode state (e.g., private state for    minimill-mode.pkg) for mini mill is available via this.
                            #
                            mill_extension_state:       Crypt,
                            textpane_to_textmill:       mt::Textpane_To_Textmill,                                       # NB: We're running in textmill's microthread to guarantee atomicity, so invoking blocking textpane_to_textmill.* fns is likely to deadlock.  See Note[1].
                            mode_to_drawpane:           Null_Or( m2d::Mode_To_Drawpane ),                               # This will be non-NULL iff we specified a non-NULL draw_*_fn in our mt::PANEMODE value at bottom of file (which we do not do in this package).
                            valid_completions:          Null_Or( String -> List(String) )                               # If this is non-NULL then user is entering a commandname or filename or millname(=buffername) on the modeline, and given fn returns all valid completions of string-entered-so-far.
                          };

                if readonly
                    #
                    FAIL "Buffer is read-only";
                else
                    point -> { row, col };

                    line_key = row;                                                                                     # Internally lines are numbered 0->(N-1) (but we display them to user as 1-N).

                    text =  mt::findline (textlines, line_key);

                    chomped_text =  string::chomp  text;

                    (string::expand_tabs_and_control_chars
                      {
                        utf8text        =>  chomped_text,
                        startcol        =>  0,
                        screencol1      =>  col,
                        screencol2      => -1,                                                                          # Don't-care.
                        utf8byte        => -1                                                                           # Don't-care.
                      })
                      ->
                      { screentext_length_in_screencols:        Int,
                        screencol1_byteoffset_in_utf8text:      Int,
                        ...
                      };

                    if (col >= screentext_length_in_screencols)
                        #
# XXX SUCKO FIXME: TBD 
                        WORK [ ];                                                                                       # Cursor is on non-existent char past end of existing line.  Don't fail, but don't do anything either. (emacs deletes the end-of-line newline here, but I prefer to have only kill_line do that.)
                    else
                                                                                                                        # Cursor is on an existing char, possibly a multibyte utf8 char.  Excise it by replacing the line with the concatenation of the substrings preceding and following the char.
                        text_before_point
                            =
                            string::substring
                              (
                                text,                                                                                   # String from which to extract substring.
                                0,                                                                                      # The substring we want starts at offset 0.
                                screencol1_byteoffset_in_utf8text                                                       # The substring we want runs to location of point.  Treating cursor offset as length works (only) because we're starting substring at offset zero.
                              );

                        text_beyond_point
                            =
                            string::extract
                              (
                                text,                                                                                   # String from which to extract substring.
                                screencol1_byteoffset_in_utf8text,                                                      # Substring starts at the byte(s) under the cursor.  (Cursor will mark multiple bytes only if it is on a multibyte utf8 char.)
                                NULL                                                                                    # Substring runs to end of 'text'.
                              );

                        repeat_factor
                            =
                            case numeric_prefix
                                #
                                THE repeat_factor =>  max (1, repeat_factor);
                                NULL              =>  1;
                            esac;

                        updated_text    =  string::cat [ text_before_point,
                                                         string::repeat (keystring, repeat_factor),
                                                         text_beyond_point
                                                       ];

                        updated_text    =  mt::MONOLINE   { string =>  updated_text,
                                                            prefix =>  NULL
                                                          };

                        updated_textlines                                                                               # First remove existing line -- nl::set does NOT remove any previous line at that key.
                            =
                            (nl::remove (textlines, line_key))
                            except _ = textlines;                                                                       # This will happen if there is no line 'line_key' in textlines.

                        updated_textlines                                                                               # Now insert updated line.
                            =
                            nl::set (updated_textlines, line_key, updated_text);

                        point = { row, col => col + repeat_factor };                                                    # XXX BUGGO FIXME This will not leave cursor on right screen column if 'keystring' contained TAB, say.

                        WORK  [ mt::TEXTLINES updated_textlines,
                                mt::POINT point
                              ];
                    fi;         
                fi;
            };
        self_insert_command__editfn
            =
            mt::EDITFN (
              mt::PLAIN_EDITFN
                {
                  name   =>  "self_insert_command",
                  doc    =>  "Insert keystroke at point (cursor).",
                  args   =>  [],
                  editfn =>  self_insert_command
                }
              );                                my _ =
        mt::note_editfn  self_insert_command__editfn;


        fun quoted_insert (arg:         mt::Editfn_In)
            :                           mt::Editfn_Out
            =
            {   arg ->    { args:                       List( mt::Prompted_Arg ),                                       # Args read interactively from user per our __editfn.args spec.
                            textlines:                  mt::Textlines,
                            point:                      g2d::Point,                                                     # As in Point_And_Mark.
                            mark:                       Null_Or(g2d::Point),                                            # 
                            lastmark:                   Null_Or(g2d::Point),                                            # 
                            screen_origin:              g2d::Point,                                                     # Origin of pane-visible text relative to textmill contents:  (0,0) means we're showing top of buffer at top of textpane.
                            visible_lines:              Int,                                                            # Number of lines of text visible in pane.
                            readonly:                   Bool,                                                           # TRUE iff contents of textmill are currently marked as read-only.
                            keystring:                  String,                                                         # User keystroke that invoked this editfn.
                            numeric_prefix:             Null_Or( Int ),                                                 # ^U "Universal numeric prefix" value for this editfn if supplied by user, else NULL.
                            edit_history:               mt::Edit_History,                                               # Recent visible states of textmill, to support undo functionality.
                            pane_tag:                   Int,                                                            # Tag of pane for which this editfn is being invoked.  This is a small int for human/GUI use.
                            pane_id:                    Id,                                                             # Id  of pane for which this editfn is being invoked.
                            mill_id:                    Id,                                                             # Id  of mill for which this editfn is being invoked.
                            to:                         Replyqueue,                                                     # The name makes   foo::pass_something(imp) to {. ... }   syntax read well.
                            widget_to_guiboss:          gt::Widget_To_Guiboss,                                          # 
                            mill_to_millboss:           mt::Mill_To_Millboss,
                            #
                            mainmill_modestate:         mt::Panemode_State,                                             # Any persistent per-mode state (e.g., private state for fundamental-mode.pkg) for main mill is available via this.
                            minimill_modestate:         mt::Panemode_State,                                             # Any persistent per-mode state (e.g., private state for    minimill-mode.pkg) for mini mill is available via this.
                            #
                            mill_extension_state:       Crypt,
                            textpane_to_textmill:       mt::Textpane_To_Textmill,                                       # NB: We're running in textmill's microthread to guarantee atomicity, so invoking blocking textpane_to_textmill.* fns is likely to deadlock.  See Note[1].
                            mode_to_drawpane:           Null_Or( m2d::Mode_To_Drawpane ),                               # This will be non-NULL iff we specified a non-NULL draw_*_fn in our mt::PANEMODE value at bottom of file (which we do not do in this package).
                            valid_completions:          Null_Or( String -> List(String) )                               # If this is non-NULL then user is entering a commandname or filename or millname(=buffername) on the modeline, and given fn returns all valid completions of string-entered-so-far.
                          };

                if readonly
                    #
                    FAIL "Buffer is read-only";
                else
                    WORK  [ mt::QUOTE_NEXT self_insert_command__editfn
                          ];
                fi;
            };
        quoted_insert__editfn
            =
            mt::EDITFN (
              mt::PLAIN_EDITFN
                {
                  name   =>  "quoted_insert",
                  doc    =>  "Insert next keystroke literally at cursor, no matter what it is.",
                  args   =>  [],
                  editfn =>  quoted_insert
                }
              );                                my _ =
        mt::note_editfn  quoted_insert__editfn;


        fun point_to_register' (arg:    mt::Editfn_In)
            :                           mt::Editfn_Out
            =
            {   arg ->    { args:                       List( mt::Prompted_Arg ),                                       # Args read interactively from user per our __editfn.args spec.
                            textlines:                  mt::Textlines,
                            point:                      g2d::Point,                                                     # As in Point_And_Mark.
                            mark:                       Null_Or(g2d::Point),                                            # 
                            lastmark:                   Null_Or(g2d::Point),                                            # 
                            screen_origin:              g2d::Point,                                                     # Origin of pane-visible text relative to textmill contents:  (0,0) means we're showing top of buffer at top of textpane.
                            visible_lines:              Int,                                                            # Number of lines of text visible in pane.
                            readonly:                   Bool,                                                           # TRUE iff contents of textmill are currently marked as read-only.
                            keystring:                  String,                                                         # User keystroke that invoked this editfn.
                            numeric_prefix:             Null_Or( Int ),                                                 # ^U "Universal numeric prefix" value for this editfn if supplied by user, else NULL.
                            edit_history:               mt::Edit_History,                                               # Recent visible states of textmill, to support undo functionality.
                            pane_tag:                   Int,                                                            # Tag of pane for which this editfn is being invoked.  This is a small int for human/GUI use.
                            pane_id:                    Id,                                                             # Id  of pane for which this editfn is being invoked.
                            mill_id:                    Id,                                                             # Id  of mill for which this editfn is being invoked.
                            to:                         Replyqueue,                                                     # The name makes   foo::pass_something(imp) to {. ... }   syntax read well.
                            widget_to_guiboss:          gt::Widget_To_Guiboss,                                          # 
                            mill_to_millboss:           mt::Mill_To_Millboss,
                            #
                            mainmill_modestate:         mt::Panemode_State,                                             # Any persistent per-mode state (e.g., private state for fundamental-mode.pkg) for main mill is available via this.
                            minimill_modestate:         mt::Panemode_State,                                             # Any persistent per-mode state (e.g., private state for    minimill-mode.pkg) for mini mill is available via this.
                            #
                            mill_extension_state:       Crypt,
                            textpane_to_textmill:       mt::Textpane_To_Textmill,                                       # NB: We're running in textmill's microthread to guarantee atomicity, so invoking blocking textpane_to_textmill.* fns is likely to deadlock.  See Note[1].
                            mode_to_drawpane:           Null_Or( m2d::Mode_To_Drawpane ),                               # This will be non-NULL iff we specified a non-NULL draw_*_fn in our mt::PANEMODE value at bottom of file (which we do not do in this package).
                            valid_completions:          Null_Or( String -> List(String) )                               # If this is non-NULL then user is entering a commandname or filename or millname(=buffername) on the modeline, and given fn returns all valid completions of string-entered-so-far.
                          };

nb {. sprintf "point_to_register'/AAA --fundamental-mode.pkg"; }; 
                if readonly
                    #
                    FAIL "Buffer is read-only";
                else
                    WORK  [ mt::MODELINE_MESSAGE "point_to_register unimplemented"
                          ];
                fi;
            };
        point_to_register'__editfn
            =
            mt::EDITFN (
              mt::PLAIN_EDITFN
                {
                  name   =>  "point_to_register'",
                  doc    =>  "Save point (cursor) in register.",
                  args   =>  [],
                  editfn =>  point_to_register'
                }
              );
        # NB: We deliberately do NOT register point_to_register'__editfn -- it is purely internal.
        fun point_to_register (arg:     mt::Editfn_In)
            :                           mt::Editfn_Out
            =
            {   arg ->    { args:                       List( mt::Prompted_Arg ),                                       # Args read interactively from user per our __editfn.args spec.
                            textlines:                  mt::Textlines,
                            point:                      g2d::Point,                                                     # As in Point_And_Mark.
                            mark:                       Null_Or(g2d::Point),                                            # 
                            lastmark:                   Null_Or(g2d::Point),                                            # 
                            screen_origin:              g2d::Point,                                                     # Origin of pane-visible text relative to textmill contents:  (0,0) means we're showing top of buffer at top of textpane.
                            visible_lines:              Int,                                                            # Number of lines of text visible in pane.
                            readonly:                   Bool,                                                           # TRUE iff contents of textmill are currently marked as read-only.
                            keystring:                  String,                                                         # User keystroke that invoked this editfn.
                            numeric_prefix:             Null_Or( Int ),                                                 # ^U "Universal numeric prefix" value for this editfn if supplied by user, else NULL.
                            edit_history:               mt::Edit_History,                                               # Recent visible states of textmill, to support undo functionality.
                            pane_tag:                   Int,                                                            # Tag of pane for which this editfn is being invoked.  This is a small int for human/GUI use.
                            pane_id:                    Id,                                                             # Id  of pane for which this editfn is being invoked.
                            mill_id:                    Id,                                                             # Id  of mill for which this editfn is being invoked.
                            to:                         Replyqueue,                                                     # The name makes   foo::pass_something(imp) to {. ... }   syntax read well.
                            widget_to_guiboss:          gt::Widget_To_Guiboss,                                          # 
                            mill_to_millboss:           mt::Mill_To_Millboss,
                            #
                            mainmill_modestate:         mt::Panemode_State,                                             # Any persistent per-mode state (e.g., private state for fundamental-mode.pkg) for main mill is available via this.
                            minimill_modestate:         mt::Panemode_State,                                             # Any persistent per-mode state (e.g., private state for    minimill-mode.pkg) for mini mill is available via this.
                            #
                            mill_extension_state:       Crypt,
                            textpane_to_textmill:       mt::Textpane_To_Textmill,                                       # NB: We're running in textmill's microthread to guarantee atomicity, so invoking blocking textpane_to_textmill.* fns is likely to deadlock.  See Note[1].
                            mode_to_drawpane:           Null_Or( m2d::Mode_To_Drawpane ),                               # This will be non-NULL iff we specified a non-NULL draw_*_fn in our mt::PANEMODE value at bottom of file (which we do not do in this package).
                            valid_completions:          Null_Or( String -> List(String) )                               # If this is non-NULL then user is entering a commandname or filename or millname(=buffername) on the modeline, and given fn returns all valid completions of string-entered-so-far.
                          };

nb {. sprintf "point_to_register/AAA --fundamental-mode.pkg"; }; 
                if readonly
                    #
                    FAIL "Buffer is read-only";
                else
                    WORK  [ mt::QUOTE_NEXT point_to_register'__editfn                                                   # This will result in  point_to_register'  being called with 'keystring' set to next char typed by user.
                          ];
                fi;
            };
        point_to_register__editfn
            =
            mt::EDITFN (
              mt::PLAIN_EDITFN
                {
                  name   =>  "point_to_register",
                  doc    =>  "Save point (cursor) in register.",
                  args   =>  [],
                  editfn =>  point_to_register
                }
              );                                my _ =
        mt::note_editfn  point_to_register__editfn;

        fun insert_register' (arg:      mt::Editfn_In)
            :                           mt::Editfn_Out
            =
            {   arg ->    { args:                       List( mt::Prompted_Arg ),                                       # Args read interactively from user per our __editfn.args spec.
                            textlines:                  mt::Textlines,
                            point:                      g2d::Point,                                                     # As in Point_And_Mark.
                            mark:                       Null_Or(g2d::Point),                                            # 
                            lastmark:                   Null_Or(g2d::Point),                                            # 
                            screen_origin:              g2d::Point,                                                     # Origin of pane-visible text relative to textmill contents:  (0,0) means we're showing top of buffer at top of textpane.
                            visible_lines:              Int,                                                            # Number of lines of text visible in pane.
                            readonly:                   Bool,                                                           # TRUE iff contents of textmill are currently marked as read-only.
                            keystring:                  String,                                                         # User keystroke that invoked this editfn.
                            numeric_prefix:             Null_Or( Int ),                                                 # ^U "Universal numeric prefix" value for this editfn if supplied by user, else NULL.
                            edit_history:               mt::Edit_History,                                               # Recent visible states of textmill, to support undo functionality.
                            pane_tag:                   Int,                                                            # Tag of pane for which this editfn is being invoked.  This is a small int for human/GUI use.
                            pane_id:                    Id,                                                             # Id  of pane for which this editfn is being invoked.
                            mill_id:                    Id,                                                             # Id  of mill for which this editfn is being invoked.
                            to:                         Replyqueue,                                                     # The name makes   foo::pass_something(imp) to {. ... }   syntax read well.
                            widget_to_guiboss:          gt::Widget_To_Guiboss,                                          # 
                            mill_to_millboss:           mt::Mill_To_Millboss,
                            #
                            mainmill_modestate:         mt::Panemode_State,                                             # Any persistent per-mode state (e.g., private state for fundamental-mode.pkg) for main mill is available via this.
                            minimill_modestate:         mt::Panemode_State,                                             # Any persistent per-mode state (e.g., private state for    minimill-mode.pkg) for mini mill is available via this.
                            #
                            mill_extension_state:       Crypt,
                            textpane_to_textmill:       mt::Textpane_To_Textmill,                                       # NB: We're running in textmill's microthread to guarantee atomicity, so invoking blocking textpane_to_textmill.* fns is likely to deadlock.  See Note[1].
                            mode_to_drawpane:           Null_Or( m2d::Mode_To_Drawpane ),                               # This will be non-NULL iff we specified a non-NULL draw_*_fn in our mt::PANEMODE value at bottom of file (which we do not do in this package).
                            valid_completions:          Null_Or( String -> List(String) )                               # If this is non-NULL then user is entering a commandname or filename or millname(=buffername) on the modeline, and given fn returns all valid completions of string-entered-so-far.
                          };

nb {. sprintf "insert_register'/AAA --fundamental-mode.pkg"; }; 
                if readonly
                    #
                    FAIL "Buffer is read-only";
                else
                    WORK  [ mt::MODELINE_MESSAGE "point_to_register unimplemented"
                          ];
                fi;
            };
        insert_register'__editfn
            =
            mt::EDITFN (
              mt::PLAIN_EDITFN
                {
                  name   =>  "insert_register'",
                  doc    =>  "Save point (cursor) in register.",
                  args   =>  [],
                  editfn =>  insert_register'
                }
              );
        # NB: We deliberately do NOT register insert_register'__editfn -- it is purely internal.
        fun insert_register (arg:       mt::Editfn_In)
            :                           mt::Editfn_Out
            =
            {   arg ->    { args:                       List( mt::Prompted_Arg ),                                       # Args read interactively from user per our __editfn.args spec.
                            textlines:                  mt::Textlines,
                            point:                      g2d::Point,                                                     # As in Point_And_Mark.
                            mark:                       Null_Or(g2d::Point),                                            # 
                            lastmark:                   Null_Or(g2d::Point),                                            # 
                            screen_origin:              g2d::Point,                                                     # Origin of pane-visible text relative to textmill contents:  (0,0) means we're showing top of buffer at top of textpane.
                            visible_lines:              Int,                                                            # Number of lines of text visible in pane.
                            readonly:                   Bool,                                                           # TRUE iff contents of textmill are currently marked as read-only.
                            keystring:                  String,                                                         # User keystroke that invoked this editfn.
                            numeric_prefix:             Null_Or( Int ),                                                 # ^U "Universal numeric prefix" value for this editfn if supplied by user, else NULL.
                            edit_history:               mt::Edit_History,                                               # Recent visible states of textmill, to support undo functionality.
                            pane_tag:                   Int,                                                            # Tag of pane for which this editfn is being invoked.  This is a small int for human/GUI use.
                            pane_id:                    Id,                                                             # Id  of pane for which this editfn is being invoked.
                            mill_id:                    Id,                                                             # Id  of mill for which this editfn is being invoked.
                            to:                         Replyqueue,                                                     # The name makes   foo::pass_something(imp) to {. ... }   syntax read well.
                            widget_to_guiboss:          gt::Widget_To_Guiboss,                                          # 
                            mill_to_millboss:           mt::Mill_To_Millboss,
                            #
                            mainmill_modestate:         mt::Panemode_State,                                             # Any persistent per-mode state (e.g., private state for fundamental-mode.pkg) for main mill is available via this.
                            minimill_modestate:         mt::Panemode_State,                                             # Any persistent per-mode state (e.g., private state for    minimill-mode.pkg) for mini mill is available via this.
                            #
                            mill_extension_state:       Crypt,
                            textpane_to_textmill:       mt::Textpane_To_Textmill,                                       # NB: We're running in textmill's microthread to guarantee atomicity, so invoking blocking textpane_to_textmill.* fns is likely to deadlock.  See Note[1].
                            mode_to_drawpane:           Null_Or( m2d::Mode_To_Drawpane ),                               # This will be non-NULL iff we specified a non-NULL draw_*_fn in our mt::PANEMODE value at bottom of file (which we do not do in this package).
                            valid_completions:          Null_Or( String -> List(String) )                               # If this is non-NULL then user is entering a commandname or filename or millname(=buffername) on the modeline, and given fn returns all valid completions of string-entered-so-far.
                          };

nb {. sprintf "insert_register/AAA --fundamental-mode.pkg"; }; 
                if readonly
                    #
                    FAIL "Buffer is read-only";
                else
                    WORK  [ mt::QUOTE_NEXT insert_register'__editfn                                                     # This will result in  insert_register'  being called with 'keystring' set to next char typed by user.
                          ];
                fi;
            };
        insert_register__editfn
            =
            mt::EDITFN (
              mt::PLAIN_EDITFN
                {
                  name   =>  "insert_register",
                  doc    =>  "Save point (cursor) in register.",
                  args   =>  [],
                  editfn =>  insert_register
                }
              );                                my _ =
        mt::note_editfn  insert_register__editfn;

        fun jump_to_register' (arg:     mt::Editfn_In)
            :                           mt::Editfn_Out
            =
            {   arg ->    { args:                       List( mt::Prompted_Arg ),                                       # Args read interactively from user per our __editfn.args spec.
                            textlines:                  mt::Textlines,
                            point:                      g2d::Point,                                                     # As in Point_And_Mark.
                            mark:                       Null_Or(g2d::Point),                                            # 
                            lastmark:                   Null_Or(g2d::Point),                                            # 
                            screen_origin:              g2d::Point,                                                     # Origin of pane-visible text relative to textmill contents:  (0,0) means we're showing top of buffer at top of textpane.
                            visible_lines:              Int,                                                            # Number of lines of text visible in pane.
                            readonly:                   Bool,                                                           # TRUE iff contents of textmill are currently marked as read-only.
                            keystring:                  String,                                                         # User keystroke that invoked this editfn.
                            numeric_prefix:             Null_Or( Int ),                                                 # ^U "Universal numeric prefix" value for this editfn if supplied by user, else NULL.
                            edit_history:               mt::Edit_History,                                               # Recent visible states of textmill, to support undo functionality.
                            pane_tag:                   Int,                                                            # Tag of pane for which this editfn is being invoked.  This is a small int for human/GUI use.
                            pane_id:                    Id,                                                             # Id  of pane for which this editfn is being invoked.
                            mill_id:                    Id,                                                             # Id  of mill for which this editfn is being invoked.
                            to:                         Replyqueue,                                                     # The name makes   foo::pass_something(imp) to {. ... }   syntax read well.
                            widget_to_guiboss:          gt::Widget_To_Guiboss,                                          # 
                            mill_to_millboss:           mt::Mill_To_Millboss,
                            #
                            mainmill_modestate:         mt::Panemode_State,                                             # Any persistent per-mode state (e.g., private state for fundamental-mode.pkg) for main mill is available via this.
                            minimill_modestate:         mt::Panemode_State,                                             # Any persistent per-mode state (e.g., private state for    minimill-mode.pkg) for mini mill is available via this.
                            #
                            mill_extension_state:       Crypt,
                            textpane_to_textmill:       mt::Textpane_To_Textmill,                                       # NB: We're running in textmill's microthread to guarantee atomicity, so invoking blocking textpane_to_textmill.* fns is likely to deadlock.  See Note[1].
                            mode_to_drawpane:           Null_Or( m2d::Mode_To_Drawpane ),                               # This will be non-NULL iff we specified a non-NULL draw_*_fn in our mt::PANEMODE value at bottom of file (which we do not do in this package).
                            valid_completions:          Null_Or( String -> List(String) )                               # If this is non-NULL then user is entering a commandname or filename or millname(=buffername) on the modeline, and given fn returns all valid completions of string-entered-so-far.
                          };

nb {. sprintf "jump_to_register'/AAA --fundamental-mode.pkg"; }; 
                if readonly
                    #
                    FAIL "Buffer is read-only";
                else
                    WORK  [ mt::MODELINE_MESSAGE "point_to_register unimplemented"
                          ];
                fi;
            };
        jump_to_register'__editfn
            =
            mt::EDITFN (
              mt::PLAIN_EDITFN
                {
                  name   =>  "jump_to_register'",
                  doc    =>  "Save point (cursor) in register.",
                  args   =>  [],
                  editfn =>  jump_to_register'
                }
              );
        # NB: We deliberately do NOT register jump_to_register'__editfn -- it is purely internal.
        fun jump_to_register (arg:      mt::Editfn_In)
            :                           mt::Editfn_Out
            =
            {   arg ->    { args:                       List( mt::Prompted_Arg ),                                       # Args read interactively from user per our __editfn.args spec.
                            textlines:                  mt::Textlines,
                            point:                      g2d::Point,                                                     # As in Point_And_Mark.
                            mark:                       Null_Or(g2d::Point),                                            # 
                            lastmark:                   Null_Or(g2d::Point),                                            # 
                            screen_origin:              g2d::Point,                                                     # Origin of pane-visible text relative to textmill contents:  (0,0) means we're showing top of buffer at top of textpane.
                            visible_lines:              Int,                                                            # Number of lines of text visible in pane.
                            readonly:                   Bool,                                                           # TRUE iff contents of textmill are currently marked as read-only.
                            keystring:                  String,                                                         # User keystroke that invoked this editfn.
                            numeric_prefix:             Null_Or( Int ),                                                 # ^U "Universal numeric prefix" value for this editfn if supplied by user, else NULL.
                            edit_history:               mt::Edit_History,                                               # Recent visible states of textmill, to support undo functionality.
                            pane_tag:                   Int,                                                            # Tag of pane for which this editfn is being invoked.  This is a small int for human/GUI use.
                            pane_id:                    Id,                                                             # Id  of pane for which this editfn is being invoked.
                            mill_id:                    Id,                                                             # Id  of mill for which this editfn is being invoked.
                            to:                         Replyqueue,                                                     # The name makes   foo::pass_something(imp) to {. ... }   syntax read well.
                            widget_to_guiboss:          gt::Widget_To_Guiboss,                                          # 
                            mill_to_millboss:           mt::Mill_To_Millboss,
                            #
                            mainmill_modestate:         mt::Panemode_State,                                             # Any persistent per-mode state (e.g., private state for fundamental-mode.pkg) for main mill is available via this.
                            minimill_modestate:         mt::Panemode_State,                                             # Any persistent per-mode state (e.g., private state for    minimill-mode.pkg) for mini mill is available via this.
                            #
                            mill_extension_state:       Crypt,
                            textpane_to_textmill:       mt::Textpane_To_Textmill,                                       # NB: We're running in textmill's microthread to guarantee atomicity, so invoking blocking textpane_to_textmill.* fns is likely to deadlock.  See Note[1].
                            mode_to_drawpane:           Null_Or( m2d::Mode_To_Drawpane ),                               # This will be non-NULL iff we specified a non-NULL draw_*_fn in our mt::PANEMODE value at bottom of file (which we do not do in this package).
                            valid_completions:          Null_Or( String -> List(String) )                               # If this is non-NULL then user is entering a commandname or filename or millname(=buffername) on the modeline, and given fn returns all valid completions of string-entered-so-far.
                          };

nb {. sprintf "jump_to_register/AAA --fundamental-mode.pkg"; }; 
                if readonly
                    #
                    FAIL "Buffer is read-only";
                else
                    WORK  [ mt::QUOTE_NEXT jump_to_register'__editfn                                                    # This will result in  jump_to_register'  being called with 'keystring' set to next char typed by user.
                          ];
                fi;
            };
        jump_to_register__editfn
            =
            mt::EDITFN (
              mt::PLAIN_EDITFN
                {
                  name   =>  "jump_to_register",
                  doc    =>  "Save point (cursor) in register.",
                  args   =>  [],
                  editfn =>  jump_to_register
                }
              );                                my _ =
        mt::note_editfn  jump_to_register__editfn;

        fun kill_rectangle (arg:        mt::Editfn_In)
            :                           mt::Editfn_Out
            =
            {   arg ->    { args:                       List( mt::Prompted_Arg ),                                       # Args read interactively from user per our __editfn.args spec.
                            textlines:                  mt::Textlines,
                            point:                      g2d::Point,                                                     # As in Point_And_Mark.
                            mark:                       Null_Or(g2d::Point),                                            # 
                            lastmark:                   Null_Or(g2d::Point),                                            # 
                            screen_origin:              g2d::Point,                                                     # Origin of pane-visible text relative to textmill contents:  (0,0) means we're showing top of buffer at top of textpane.
                            visible_lines:              Int,                                                            # Number of lines of text visible in pane.
                            readonly:                   Bool,                                                           # TRUE iff contents of textmill are currently marked as read-only.
                            keystring:                  String,                                                         # User keystroke that invoked this editfn.
                            numeric_prefix:             Null_Or( Int ),                                                 # ^U "Universal numeric prefix" value for this editfn if supplied by user, else NULL.
                            edit_history:               mt::Edit_History,                                               # Recent visible states of textmill, to support undo functionality.
                            pane_tag:                   Int,                                                            # Tag of pane for which this editfn is being invoked.  This is a small int for human/GUI use.
                            pane_id:                    Id,                                                             # Id  of pane for which this editfn is being invoked.
                            mill_id:                    Id,                                                             # Id  of mill for which this editfn is being invoked.
                            to:                         Replyqueue,                                                     # The name makes   foo::pass_something(imp) to {. ... }   syntax read well.
                            widget_to_guiboss:          gt::Widget_To_Guiboss,                                          # 
                            mill_to_millboss:           mt::Mill_To_Millboss,
                            #
                            mainmill_modestate:         mt::Panemode_State,                                             # Any persistent per-mode state (e.g., private state for fundamental-mode.pkg) for main mill is available via this.
                            minimill_modestate:         mt::Panemode_State,                                             # Any persistent per-mode state (e.g., private state for    minimill-mode.pkg) for mini mill is available via this.
                            #
                            mill_extension_state:       Crypt,
                            textpane_to_textmill:       mt::Textpane_To_Textmill,                                       # NB: We're running in textmill's microthread to guarantee atomicity, so invoking blocking textpane_to_textmill.* fns is likely to deadlock.  See Note[1].
                            mode_to_drawpane:           Null_Or( m2d::Mode_To_Drawpane ),                               # This will be non-NULL iff we specified a non-NULL draw_*_fn in our mt::PANEMODE value at bottom of file (which we do not do in this package).
                            valid_completions:          Null_Or( String -> List(String) )                               # If this is non-NULL then user is entering a commandname or filename or millname(=buffername) on the modeline, and given fn returns all valid completions of string-entered-so-far.
                          };

nb {. sprintf "kill_rectangle/AAA --fundamental-mode.pkg"; }; 
                if readonly
                    #
                    FAIL "Buffer is read-only";
                else
                    WORK  [  mt::MODELINE_MESSAGE "kill_rectangle unimplemented"
                          ];
                fi;
            };
        kill_rectangle__editfn
            =
            mt::EDITFN (
              mt::PLAIN_EDITFN
                {
                  name   =>  "kill_rectangle",
                  doc    =>  "Save point (cursor) in register.",
                  args   =>  [],
                  editfn =>  kill_rectangle
                }
              );                                my _ =
        mt::note_editfn  kill_rectangle__editfn;

        fun copy_to_register' (arg:     mt::Editfn_In)
            :                           mt::Editfn_Out
            =
            {   arg ->    { args:                       List( mt::Prompted_Arg ),                                       # Args read interactively from user per our __editfn.args spec.
                            textlines:                  mt::Textlines,
                            point:                      g2d::Point,                                                     # As in Point_And_Mark.
                            mark:                       Null_Or(g2d::Point),                                            # 
                            lastmark:                   Null_Or(g2d::Point),                                            # 
                            screen_origin:              g2d::Point,                                                     # Origin of pane-visible text relative to textmill contents:  (0,0) means we're showing top of buffer at top of textpane.
                            visible_lines:              Int,                                                            # Number of lines of text visible in pane.
                            readonly:                   Bool,                                                           # TRUE iff contents of textmill are currently marked as read-only.
                            keystring:                  String,                                                         # User keystroke that invoked this editfn.
                            numeric_prefix:             Null_Or( Int ),                                                 # ^U "Universal numeric prefix" value for this editfn if supplied by user, else NULL.
                            edit_history:               mt::Edit_History,                                               # Recent visible states of textmill, to support undo functionality.
                            pane_tag:                   Int,                                                            # Tag of pane for which this editfn is being invoked.  This is a small int for human/GUI use.
                            pane_id:                    Id,                                                             # Id  of pane for which this editfn is being invoked.
                            mill_id:                    Id,                                                             # Id  of mill for which this editfn is being invoked.
                            to:                         Replyqueue,                                                     # The name makes   foo::pass_something(imp) to {. ... }   syntax read well.
                            widget_to_guiboss:          gt::Widget_To_Guiboss,                                          # 
                            mill_to_millboss:           mt::Mill_To_Millboss,
                            #
                            mainmill_modestate:         mt::Panemode_State,                                             # Any persistent per-mode state (e.g., private state for fundamental-mode.pkg) for main mill is available via this.
                            minimill_modestate:         mt::Panemode_State,                                             # Any persistent per-mode state (e.g., private state for    minimill-mode.pkg) for mini mill is available via this.
                            #
                            mill_extension_state:       Crypt,
                            textpane_to_textmill:       mt::Textpane_To_Textmill,                                       # NB: We're running in textmill's microthread to guarantee atomicity, so invoking blocking textpane_to_textmill.* fns is likely to deadlock.  See Note[1].
                            mode_to_drawpane:           Null_Or( m2d::Mode_To_Drawpane ),                               # This will be non-NULL iff we specified a non-NULL draw_*_fn in our mt::PANEMODE value at bottom of file (which we do not do in this package).
                            valid_completions:          Null_Or( String -> List(String) )                               # If this is non-NULL then user is entering a commandname or filename or millname(=buffername) on the modeline, and given fn returns all valid completions of string-entered-so-far.
                          };

nb {. sprintf "copy_to_register'/AAA --fundamental-mode.pkg"; }; 
                if readonly
                    #
                    FAIL "Buffer is read-only";
                else
                    WORK  [ mt::MODELINE_MESSAGE "point_to_register unimplemented"
                          ];
                fi;
            };
        copy_to_register'__editfn
            =
            mt::EDITFN (
              mt::PLAIN_EDITFN
                {
                  name   =>  "copy_to_register'",
                  doc    =>  "Save point (cursor) in register.",
                  args   =>  [],
                  editfn =>  copy_to_register'
                }
              );
        # NB: We deliberately do NOT register copy_to_register'__editfn -- it is purely internal.
        fun copy_to_register (arg:      mt::Editfn_In)
            :                           mt::Editfn_Out
            =
            {   arg ->    { args:                       List( mt::Prompted_Arg ),                                       # Args read interactively from user per our __editfn.args spec.
                            textlines:                  mt::Textlines,
                            point:                      g2d::Point,                                                     # As in Point_And_Mark.
                            mark:                       Null_Or(g2d::Point),                                            # 
                            lastmark:                   Null_Or(g2d::Point),                                            # 
                            screen_origin:              g2d::Point,                                                     # Origin of pane-visible text relative to textmill contents:  (0,0) means we're showing top of buffer at top of textpane.
                            visible_lines:              Int,                                                            # Number of lines of text visible in pane.
                            readonly:                   Bool,                                                           # TRUE iff contents of textmill are currently marked as read-only.
                            keystring:                  String,                                                         # User keystroke that invoked this editfn.
                            numeric_prefix:             Null_Or( Int ),                                                 # ^U "Universal numeric prefix" value for this editfn if supplied by user, else NULL.
                            edit_history:               mt::Edit_History,                                               # Recent visible states of textmill, to support undo functionality.
                            pane_tag:                   Int,                                                            # Tag of pane for which this editfn is being invoked.  This is a small int for human/GUI use.
                            pane_id:                    Id,                                                             # Id  of pane for which this editfn is being invoked.
                            mill_id:                    Id,                                                             # Id  of mill for which this editfn is being invoked.
                            to:                         Replyqueue,                                                     # The name makes   foo::pass_something(imp) to {. ... }   syntax read well.
                            widget_to_guiboss:          gt::Widget_To_Guiboss,                                          # 
                            mill_to_millboss:           mt::Mill_To_Millboss,
                            #
                            mainmill_modestate:         mt::Panemode_State,                                             # Any persistent per-mode state (e.g., private state for fundamental-mode.pkg) for main mill is available via this.
                            minimill_modestate:         mt::Panemode_State,                                             # Any persistent per-mode state (e.g., private state for    minimill-mode.pkg) for mini mill is available via this.
                            #
                            mill_extension_state:       Crypt,
                            textpane_to_textmill:       mt::Textpane_To_Textmill,                                       # NB: We're running in textmill's microthread to guarantee atomicity, so invoking blocking textpane_to_textmill.* fns is likely to deadlock.  See Note[1].
                            mode_to_drawpane:           Null_Or( m2d::Mode_To_Drawpane ),                               # This will be non-NULL iff we specified a non-NULL draw_*_fn in our mt::PANEMODE value at bottom of file (which we do not do in this package).
                            valid_completions:          Null_Or( String -> List(String) )                               # If this is non-NULL then user is entering a commandname or filename or millname(=buffername) on the modeline, and given fn returns all valid completions of string-entered-so-far.
                          };

nb {. sprintf "copy_to_register/AAA --fundamental-mode.pkg"; }; 
                if readonly
                    #
                    FAIL "Buffer is read-only";
                else
                    WORK  [ mt::QUOTE_NEXT copy_to_register'__editfn                                                    # This will result in  copy_to_register'  being called with 'keystring' set to next char typed by user.
                          ];
                fi;
            };
        copy_to_register__editfn
            =
            mt::EDITFN (
              mt::PLAIN_EDITFN
                {
                  name   =>  "copy_to_register",
                  doc    =>  "Save point (cursor) in register.",
                  args   =>  [],
                  editfn =>  copy_to_register
                }
              );                                my _ =
        mt::note_editfn  copy_to_register__editfn;

        fun newline             (arg:           mt::Editfn_In)                                                          # Split line at cursor, leave cursor at start of new line.
            :                                   mt::Editfn_Out
            =
            {   arg ->    { args:                       List( mt::Prompted_Arg ),                                       # Args read interactively from user per our __editfn.args spec.
                            textlines:                  mt::Textlines,
                            point:                      g2d::Point,                                                     # As in Point_And_Mark.
                            mark:                       Null_Or(g2d::Point),                                            # 
                            lastmark:                   Null_Or(g2d::Point),                                            # 
                            screen_origin:              g2d::Point,                                                     # Origin of pane-visible text relative to textmill contents:  (0,0) means we're showing top of buffer at top of textpane.
                            visible_lines:              Int,                                                            # Number of lines of text visible in pane.
                            readonly:                   Bool,                                                           # TRUE iff contents of textmill are currently marked as read-only.
                            keystring:                  String,                                                         # User keystroke that invoked this editfn.
                            numeric_prefix:             Null_Or( Int ),                                                 # ^U "Universal numeric prefix" value for this editfn if supplied by user, else NULL.
                            edit_history:               mt::Edit_History,                                               # Recent visible states of textmill, to support undo functionality.
                            pane_tag:                   Int,                                                            # Tag of pane for which this editfn is being invoked.  This is a small int for human/GUI use.
                            pane_id:                    Id,                                                             # Id  of pane for which this editfn is being invoked.
                            mill_id:                    Id,                                                             # Id  of mill for which this editfn is being invoked.
                            to:                         Replyqueue,                                                     # The name makes   foo::pass_something(imp) to {. ... }   syntax read well.
                            widget_to_guiboss:          gt::Widget_To_Guiboss,                                          # 
                            mill_to_millboss:           mt::Mill_To_Millboss,
                            #
                            mainmill_modestate:         mt::Panemode_State,                                             # Any persistent per-mode state (e.g., private state for fundamental-mode.pkg) for main mill is available via this.
                            minimill_modestate:         mt::Panemode_State,                                             # Any persistent per-mode state (e.g., private state for    minimill-mode.pkg) for mini mill is available via this.
                            #
                            mill_extension_state:       Crypt,
                            textpane_to_textmill:       mt::Textpane_To_Textmill,                                       # NB: We're running in textmill's microthread to guarantee atomicity, so invoking blocking textpane_to_textmill.* fns is likely to deadlock.  See Note[1].
                            mode_to_drawpane:           Null_Or( m2d::Mode_To_Drawpane ),                               # This will be non-NULL iff we specified a non-NULL draw_*_fn in our mt::PANEMODE value at bottom of file (which we do not do in this package).
                            valid_completions:          Null_Or( String -> List(String) )                               # If this is non-NULL then user is entering a commandname or filename or millname(=buffername) on the modeline, and given fn returns all valid completions of string-entered-so-far.
                          };
                if readonly
                    #
                    FAIL "Buffer is read-only";
                else
                    point -> { row, col };

                    line_key = row;                                                                                     # Internally lines are numbered 0->(N-1) (but we display them to user as 1-N).

                    text =  mt::findline (textlines, line_key);

                    chomped_text =  string::chomp  text;

                    (string::expand_tabs_and_control_chars
                      {
                        utf8text        =>  chomped_text,
                        startcol        =>  0,
                        screencol1      =>  col,
                        screencol2      => -1,                                                                          # Don't-care.
                        utf8byte        => -1                                                                           # Don't-care.
                      })
                      ->
                      { screentext_length_in_screencols:        Int,
                        screencol1_byteoffset_in_utf8text:      Int,
                        ...
                      };

                    if (col >= screentext_length_in_screencols)
                        #
# XXX SUCKO FIXME: TBD 
                        WORK [ ];                                                                                       # Cursor is on non-existent char past end of existing line.  Don't fail, but don't do anything either. (emacs deletes the end-of-line newline here, but I prefer to have only kill_line do that.)
                    else
                                                                                                                        # Cursor is on an existing char, possibly a multibyte utf8 char.  Excise it by replacing the line with the concatenation of the substrings preceding and following the char.
                        text_before_point
                            =
                            string::substring
                              (
                                text,                                                                                   # String from which to extract substring.
                                0,                                                                                      # The substring we want starts at offset 0.
                                screencol1_byteoffset_in_utf8text                                                       # The substring we want runs to location of cursor.  Treating cursor offset as length works (only) because we're starting substring at offset zero.
                              );

                        text_beyond_point
                            =
                            string::extract
                              (
                                text,                                                                                   # String from which to extract substring.
                                screencol1_byteoffset_in_utf8text,                                                      # Substring starts at the byte(s) under the cursor.  (Cursor will mark multiple bytes only if it is on a multibyte utf8 char.)
                                NULL                                                                                    # Substring runs to end of 'text'.
                              );

                        # We're splitting the current line into two.
                        # Synthesize those two lines:
                        #
                        line1 =  string::cat [ text_before_point, "\n" ];
                        line2 =                text_beyond_point;

                        line1 = mt::MONOLINE  { string => line1,  prefix =>  NULL };
                        line2 = mt::MONOLINE  { string => line2,  prefix =>  NULL };

                        updated_textlines                                                                               # First remove existing line -- nl::set does NOT remove any previous line at that key.
                            =
                            (nl::remove (textlines, line_key))
                            except _ = textlines;                                                                       # This will happen if there is no line 'line_key' in textlines.

                        updated_textlines = nl::set (updated_textlines, line_key, line2);                               # Now insert the two new lines.
                        updated_textlines = nl::set (updated_textlines, line_key, line1);                               # 

                        WORK  [ mt::TEXTLINES updated_textlines,
                                mt::POINT { row => row + 1, col => 0 }                                                  # Leave cursor at start of second line.
                              ];
                    fi;         
                fi;
            };
        newline__editfn
            =
            mt::EDITFN (
              mt::PLAIN_EDITFN
                {
                  name   =>  "newline",
                  doc    =>  "Split line at point (cursor), leave point at start of new line.",
                  args   =>  [],
                  editfn =>  newline
                }
              );                                my _ =
        mt::note_editfn  newline__editfn;


        fun kill_whole_line     (arg:           mt::Editfn_In)                                                          # Remove complete line under cursor, leave cursor at same column on next line.
            :                                   mt::Editfn_Out
            =
            {   arg ->    { args:                       List( mt::Prompted_Arg ),                                       # Args read interactively from user per our __editfn.args spec.
                            textlines:                  mt::Textlines,
                            point:                      g2d::Point,                                                     # As in Point_And_Mark.
                            mark:                       Null_Or(g2d::Point),                                            # 
                            lastmark:                   Null_Or(g2d::Point),                                            # 
                            screen_origin:              g2d::Point,                                                     # Origin of pane-visible text relative to textmill contents:  (0,0) means we're showing top of buffer at top of textpane.
                            visible_lines:              Int,                                                            # Number of lines of text visible in pane.
                            readonly:                   Bool,                                                           # TRUE iff contents of textmill are currently marked as read-only.
                            keystring:                  String,                                                         # User keystroke that invoked this editfn.
                            numeric_prefix:             Null_Or( Int ),                                                 # ^U "Universal numeric prefix" value for this editfn if supplied by user, else NULL.
                            edit_history:               mt::Edit_History,                                               # Recent visible states of textmill, to support undo functionality.
                            pane_tag:                   Int,                                                            # Tag of pane for which this editfn is being invoked.  This is a small int for human/GUI use.
                            pane_id:                    Id,                                                             # Id  of pane for which this editfn is being invoked.
                            mill_id:                    Id,                                                             # Id  of mill for which this editfn is being invoked.
                            to:                         Replyqueue,                                                     # The name makes   foo::pass_something(imp) to {. ... }   syntax read well.
                            widget_to_guiboss:          gt::Widget_To_Guiboss,                                          # 
                            mill_to_millboss:           mt::Mill_To_Millboss,
                            #
                            mainmill_modestate:         mt::Panemode_State,                                             # Any persistent per-mode state (e.g., private state for fundamental-mode.pkg) for main mill is available via this.
                            minimill_modestate:         mt::Panemode_State,                                             # Any persistent per-mode state (e.g., private state for    minimill-mode.pkg) for mini mill is available via this.
                            #
                            mill_extension_state:       Crypt,
                            textpane_to_textmill:       mt::Textpane_To_Textmill,                                       # NB: We're running in textmill's microthread to guarantee atomicity, so invoking blocking textpane_to_textmill.* fns is likely to deadlock.  See Note[1].
                            mode_to_drawpane:           Null_Or( m2d::Mode_To_Drawpane ),                               # This will be non-NULL iff we specified a non-NULL draw_*_fn in our mt::PANEMODE value at bottom of file (which we do not do in this package).
                            valid_completions:          Null_Or( String -> List(String) )                               # If this is non-NULL then user is entering a commandname or filename or millname(=buffername) on the modeline, and given fn returns all valid completions of string-entered-so-far.
                          };
                if readonly
                    #
                    FAIL "Buffer is read-only";
                else
                    point -> { row, col };

                    line_key = row;                                                                                     # Internally lines are numbered 0->(N-1) (but we display them to user as 1-N).

                    oldline = mt::findline (textlines, line_key);

                    updated_textlines                                                                                   # Remove line.
                        =
                        (nl::remove (textlines, line_key))
                        except _ = textlines;                                                                           # This will happen if there is no line 'line_key' in textlines.

                    mill_to_millboss
                        ->
                        mt::MILL_TO_MILLBOSS  eb;

                    eb.set_cutbuffer_contents (ct::WHOLELINE oldline);

                    WORK  [ mt::TEXTLINES updated_textlines ];
                fi;
            };
        kill_whole_line__editfn
            =
            mt::EDITFN (
              mt::PLAIN_EDITFN
                {
                  name   =>  "kill_whole_line",
                  doc    =>  "Remove complete line under point (cursor), leave point at same column on next line.",
                  args   =>  [],
                  editfn =>  kill_whole_line
                }
              );                                my _ =
        mt::note_editfn  kill_whole_line__editfn;



        fun yank                (arg:           mt::Editfn_In)                                                          # Insert contents of cutbuffer at cursor. Insertion style depends on cutbuffer contents type.
            :                                   mt::Editfn_Out
            =
            {   arg ->    { args:                       List( mt::Prompted_Arg ),                                       # Args read interactively from user per our __editfn.args spec.
                            textlines:                  mt::Textlines,
                            point:                      g2d::Point,                                                     # As in Point_And_Mark.
                            mark:                       Null_Or(g2d::Point),                                            # 
                            lastmark:                   Null_Or(g2d::Point),                                            # 
                            screen_origin:              g2d::Point,                                                     # Origin of pane-visible text relative to textmill contents:  (0,0) means we're showing top of buffer at top of textpane.
                            visible_lines:              Int,                                                            # Number of lines of text visible in pane.
                            readonly:                   Bool,                                                           # TRUE iff contents of textmill are currently marked as read-only.
                            keystring:                  String,                                                         # User keystroke that invoked this editfn.
                            numeric_prefix:             Null_Or( Int ),                                                 # ^U "Universal numeric prefix" value for this editfn if supplied by user, else NULL.
                            edit_history:               mt::Edit_History,                                               # Recent visible states of textmill, to support undo functionality.
                            pane_tag:                   Int,                                                            # Tag of pane for which this editfn is being invoked.  This is a small int for human/GUI use.
                            pane_id:                    Id,                                                             # Id  of pane for which this editfn is being invoked.
                            mill_id:                    Id,                                                             # Id  of mill for which this editfn is being invoked.
                            to:                         Replyqueue,                                                     # The name makes   foo::pass_something(imp) to {. ... }   syntax read well.
                            widget_to_guiboss:          gt::Widget_To_Guiboss,                                          # 
                            mill_to_millboss:           mt::Mill_To_Millboss,
                            #
                            mainmill_modestate:         mt::Panemode_State,                                             # Any persistent per-mode state (e.g., private state for fundamental-mode.pkg) for main mill is available via this.
                            minimill_modestate:         mt::Panemode_State,                                             # Any persistent per-mode state (e.g., private state for    minimill-mode.pkg) for mini mill is available via this.
                            #
                            mill_extension_state:       Crypt,
                            textpane_to_textmill:       mt::Textpane_To_Textmill,                                       # NB: We're running in textmill's microthread to guarantee atomicity, so invoking blocking textpane_to_textmill.* fns is likely to deadlock.  See Note[1].
                            mode_to_drawpane:           Null_Or( m2d::Mode_To_Drawpane ),                               # This will be non-NULL iff we specified a non-NULL draw_*_fn in our mt::PANEMODE value at bottom of file (which we do not do in this package).
                            valid_completions:          Null_Or( String -> List(String) )                               # If this is non-NULL then user is entering a commandname or filename or millname(=buffername) on the modeline, and given fn returns all valid completions of string-entered-so-far.
                          };

                if readonly
                    #
                    FAIL "Buffer is read-only";
                else
                    point -> { row, col };

                    line_key = row;                                                                                     # Internally lines are numbered 0->(N-1) (but we display them to user as 1-N).

                    mill_to_millboss
                        ->
                        mt::MILL_TO_MILLBOSS  eb;

                    case (eb.get_cutbuffer_contents())
                        #
                        ct::PARTLINE  text_to_insert                                                                    # Used for vanilla cut operations confined to a single line.
                            =>
                            {   (tlj::insert_string { text_to_insert, point, textlines })
                                  ->
                                  { updated_textlines,  point_after_inserted_text };

                                WORK  [ mt::TEXTLINES updated_textlines,
                                        mt::POINT     point_after_inserted_text,
                                        mt::MARK      NULL,
                                        mt::LASTMARK (THE point)
                                      ];
                            };

                        ct::WHOLELINE (line: String)                                                                    # Used for special cut operations which cut complete lines even if point (cursor) is in middle of line.
                            =>
                            {   line   = mt::MONOLINE { string => line,
                                                        prefix =>  NULL
                                                      };
                                #
                                updated_textlines
                                    =
                                    nl::set (textlines, line_key, line);

                                WORK  [ mt::TEXTLINES updated_textlines ];
                            };

                        ct::MULTILINE lines_to_insert                                                                   # Used for vanilla cut operations which happen to span more than one line.
                            =>
                            {   (tlj::insert_lines { lines_to_insert, point, textlines })
                                  ->
                                  { updated_textlines, point_after_inserted_text };

                                WORK  [ mt::TEXTLINES updated_textlines,
                                        mt::MARK      NULL,
                                        mt::LASTMARK (THE point),
                                        mt::POINT     point_after_inserted_text
                                      ];
                            };
                    esac;
                fi;
            };
        yank__editfn
            =
            mt::EDITFN (
              mt::PLAIN_EDITFN
                {
                  name   =>  "yank",
                  doc    =>  "Insert contents of cutbuffer at point (cursor). Insertion style depends on cutbuffer contents type.",
                  args   =>  [],
                  editfn =>  yank
                }
              );                                my _ =
        mt::note_editfn  yank__editfn;


        fun set_mark_command    (arg:           mt::Editfn_In)                                                  # Insert contents of cutbuffer at cursor. Insertion style depends on cutbuffer contents type.
            :                                   mt::Editfn_Out
            =
            {   arg ->    { args:                       List( mt::Prompted_Arg ),                               # Args read interactively from user per our __editfn.args spec.
                            textlines:                  mt::Textlines,
                            point:                      g2d::Point,                                             # As in Point_And_Mark.
                            mark:                       Null_Or(g2d::Point),                                    # 
                            lastmark:                   Null_Or(g2d::Point),                                    # 
                            screen_origin:              g2d::Point,                                             # Origin of pane-visible text relative to textmill contents:  (0,0) means we're showing top of buffer at top of textpane.
                            visible_lines:              Int,                                                    # Number of lines of text visible in pane.
                            readonly:                   Bool,                                                   # TRUE iff contents of textmill are currently marked as read-only.
                            keystring:                  String,                                                 # User keystroke that invoked this editfn.
                            numeric_prefix:             Null_Or( Int ),                                         # ^U "Universal numeric prefix" value for this editfn if supplied by user, else NULL.
                            edit_history:               mt::Edit_History,                                       # Recent visible states of textmill, to support undo functionality.
                            pane_tag:                   Int,                                                    # Tag of pane for which this editfn is being invoked.  This is a small int for human/GUI use.
                            pane_id:                    Id,                                                     # Id  of pane for which this editfn is being invoked.
                            mill_id:                    Id,                                                     # Id  of mill for which this editfn is being invoked.
                            to:                         Replyqueue,                                             # The name makes   foo::pass_something(imp) to {. ... }   syntax read well.
                            widget_to_guiboss:          gt::Widget_To_Guiboss,                                  # 
                            mill_to_millboss:           mt::Mill_To_Millboss,
                            #
                            mainmill_modestate:         mt::Panemode_State,                                     # Any persistent per-mode state (e.g., private state for fundamental-mode.pkg) for main mill is available via this.
                            minimill_modestate:         mt::Panemode_State,                                     # Any persistent per-mode state (e.g., private state for    minimill-mode.pkg) for mini mill is available via this.
                            #
                            mill_extension_state:       Crypt,
                            textpane_to_textmill:       mt::Textpane_To_Textmill,                               # NB: We're running in textmill's microthread to guarantee atomicity, so invoking blocking textpane_to_textmill.* fns is likely to deadlock.  See Note[1].
                            mode_to_drawpane:           Null_Or( m2d::Mode_To_Drawpane ),                       # This will be non-NULL iff we specified a non-NULL draw_*_fn in our mt::PANEMODE value at bottom of file (which we do not do in this package).
                            valid_completions:          Null_Or( String -> List(String) )                       # If this is non-NULL then user is entering a commandname or filename or millname(=buffername) on the modeline, and given fn returns all valid completions of string-entered-so-far.
                          };

                WORK  [ mt::MARK (THE point) ];
            };
        set_mark_command__editfn
            =
            mt::EDITFN (
              mt::PLAIN_EDITFN
                {
                  name   =>  "set_mark_command",
                  doc    =>  "Set 'mark' to location of point (cursor).",
                  args   =>  [],
                  editfn =>  set_mark_command
                }
              );                                my _ =
        mt::note_editfn  set_mark_command__editfn;


        fun keyboard_quit       (arg:           mt::Editfn_In)                                                  # This is emacs' stop-everything command.  For the moment it just clears the mark.
            :                                   mt::Editfn_Out
            =
            {   arg ->    { args:                       List( mt::Prompted_Arg ),                               # Args read interactively from user per our __editfn.args spec.
                            textlines:                  mt::Textlines,
                            point:                      g2d::Point,                                             # As in Point_And_Mark.
                            mark:                       Null_Or(g2d::Point),                                    # 
                            lastmark:                   Null_Or(g2d::Point),                                    # 
                            screen_origin:              g2d::Point,                                             # Origin of pane-visible text relative to textmill contents:  (0,0) means we're showing top of buffer at top of textpane.
                            visible_lines:              Int,                                                    # Number of lines of text visible in pane.
                            readonly:                   Bool,                                                   # TRUE iff contents of textmill are currently marked as read-only.
                            keystring:                  String,                                                 # User keystroke that invoked this editfn.
                            numeric_prefix:             Null_Or( Int ),                                         # ^U "Universal numeric prefix" value for this editfn if supplied by user, else NULL.
                            edit_history:               mt::Edit_History,                                       # Recent visible states of textmill, to support undo functionality.
                            pane_tag:                   Int,                                                    # Tag of pane for which this editfn is being invoked.  This is a small int for human/GUI use.
                            pane_id:                    Id,                                                     # Id  of pane for which this editfn is being invoked.
                            mill_id:                    Id,                                                     # Id  of mill for which this editfn is being invoked.
                            to:                         Replyqueue,                                             # The name makes   foo::pass_something(imp) to {. ... }   syntax read well.
                            widget_to_guiboss:          gt::Widget_To_Guiboss,                                  # 
                            mill_to_millboss:           mt::Mill_To_Millboss,
                            #
                            mainmill_modestate:         mt::Panemode_State,                                     # Any persistent per-mode state (e.g., private state for fundamental-mode.pkg) for main mill is available via this.
                            minimill_modestate:         mt::Panemode_State,                                     # Any persistent per-mode state (e.g., private state for    minimill-mode.pkg) for mini mill is available via this.
                            #
                            mill_extension_state:       Crypt,
                            textpane_to_textmill:       mt::Textpane_To_Textmill,                               # NB: We're running in textmill's microthread to guarantee atomicity, so invoking blocking textpane_to_textmill.* fns is likely to deadlock.  See Note[1].
                            mode_to_drawpane:           Null_Or( m2d::Mode_To_Drawpane ),                       # This will be non-NULL iff we specified a non-NULL draw_*_fn in our mt::PANEMODE value at bottom of file (which we do not do in this package).
                            valid_completions:          Null_Or( String -> List(String) )                       # If this is non-NULL then user is entering a commandname or filename or millname(=buffername) on the modeline, and given fn returns all valid completions of string-entered-so-far.
                          };

                WORK  [ mt::MARK NULL,
                        mt::QUIT                                                                                # Special hack just for keyboard_quit which instructs textpane.pkg to reset all ephemeral state etc.
                      ];
            };
        keyboard_quit__editfn
            =
            mt::EDITFN (
              mt::PLAIN_EDITFN
                {
                  name   =>  "keyboard_quit",
                  doc    =>  "Stop everything, clear mark, reset to stable quiescient state.",
                  args   =>  [],
                  editfn =>  keyboard_quit
                }
              );                                my _ =
        mt::note_editfn  keyboard_quit__editfn;


        fun kill_line   (arg:                   mt::Editfn_In)                                                  # 
            :                                   mt::Editfn_Out
            =
            {   arg ->    { args:                       List( mt::Prompted_Arg ),                               # Args read interactively from user per our __editfn.args spec.
                            textlines:                  mt::Textlines,
                            point:                      g2d::Point,                                             # As in Point_And_Mark.
                            mark:                       Null_Or(g2d::Point),                                    # 
                            lastmark:                   Null_Or(g2d::Point),                                    # 
                            screen_origin:              g2d::Point,                                             # Origin of pane-visible text relative to textmill contents:  (0,0) means we're showing top of buffer at top of textpane.
                            visible_lines:              Int,                                                    # Number of lines of text visible in pane.
                            readonly:                   Bool,                                                   # TRUE iff contents of textmill are currently marked as read-only.
                            keystring:                  String,                                                 # User keystroke that invoked this editfn.
                            numeric_prefix:             Null_Or( Int ),                                         # ^U "Universal numeric prefix" value for this editfn if supplied by user, else NULL.
                            edit_history:               mt::Edit_History,                                       # Recent visible states of textmill, to support undo functionality.
                            pane_tag:                   Int,                                                    # Tag of pane for which this editfn is being invoked.  This is a small int for human/GUI use.
                            pane_id:                    Id,                                                     # Id  of pane for which this editfn is being invoked.
                            mill_id:                    Id,                                                     # Id  of mill for which this editfn is being invoked.
                            to:                         Replyqueue,                                             # The name makes   foo::pass_something(imp) to {. ... }   syntax read well.
                            widget_to_guiboss:          gt::Widget_To_Guiboss,                                  # 
                            mill_to_millboss:           mt::Mill_To_Millboss,
                            #
                            mainmill_modestate:         mt::Panemode_State,                                     # Any persistent per-mode state (e.g., private state for fundamental-mode.pkg) for main mill is available via this.
                            minimill_modestate:         mt::Panemode_State,                                     # Any persistent per-mode state (e.g., private state for    minimill-mode.pkg) for mini mill is available via this.
                            #
                            mill_extension_state:       Crypt,
                            textpane_to_textmill:       mt::Textpane_To_Textmill,                               # NB: We're running in textmill's microthread to guarantee atomicity, so invoking blocking textpane_to_textmill.* fns is likely to deadlock.  See Note[1].
                            mode_to_drawpane:           Null_Or( m2d::Mode_To_Drawpane ),                       # This will be non-NULL iff we specified a non-NULL draw_*_fn in our mt::PANEMODE value at bottom of file (which we do not do in this package).
                            valid_completions:          Null_Or( String -> List(String) )                       # If this is non-NULL then user is entering a commandname or filename or millname(=buffername) on the modeline, and given fn returns all valid completions of string-entered-so-far.
                          };
                if readonly
                    #
                    FAIL "Buffer is read-only";
                else
                    mill_to_millboss
                        ->
                        mt::MILL_TO_MILLBOSS  eb;

                    #
                    point' = tlj::normalize_point (point, textlines);                                           # The column for 'point' may be somewhere odd like in the middle of a tabs, so start by deriving normalized version.

                    line_key = point'.row;                                                                      # Internally lines are numbered 0->(N-1) (but we display them to user as 1-N).

                    text =  mt::findline (textlines, line_key);

                    chomped_text =  string::chomp  text;

                    (string::expand_tabs_and_control_chars                                                      # Map screencols col1,col2 to byteoffsets in chomped_text.
                      {
                        utf8text        =>  chomped_text,
                        startcol        =>  0,
                        screencol1      =>  point'.col,
                        screencol2      => -1,                                                                  # Don't-care. 
                        utf8byte        => -1                                                                   # Don't-care. 
                      })
                      ->
                      { screencol1_byteoffset_in_utf8text => region_start,
                        ...
                      };


                    utf8_len_in_bytes = string::length_in_bytes  chomped_text;                                  # 

                    text_before_region =  string::substring (chomped_text, 0, region_start      );
                    text_within_region =  string::extract   (chomped_text,    region_start, NULL);

                    if (string::length_in_bytes text_within_region  >  0)                                       # Delete (and move to cutbuffer) ending part of line starting at point.
                        #
                        eb.set_cutbuffer_contents (ct::PARTLINE text_within_region);

                        updated_line =  text_before_region
                                     +  (chomped_text==text ?? "" :: "\n");                                     # Add back terminal newline, if original line had one.

                        updated_line =  mt::MONOLINE  { string =>  updated_line,
                                                        prefix =>  NULL
                                                      };

                        textlines =  nl::remove (textlines, line_key);
                        textlines =  nl::set    (textlines, line_key, updated_line);

                        WORK  [ mt::TEXTLINES textlines
                              ];
                    else                                                                                        # Cursor is at end of line: Join current line to next line.
                        max_key =   case (nl::max_key  textlines)
                                        #
                                        THE max_key => max_key;
                                        NULL        => 0;                                                       # We don't expect this.
                                    esac;

                        if (max_key > point'.row)                                                               # If we're not on the last line...
                            #
                            text2 = mt::findline (textlines, line_key + 1);

                            updated_line = chomped_text + text2;

                            updated_line =  mt::MONOLINE  { string =>  updated_line,
                                                            prefix =>  NULL
                                                          };

                            textlines =  nl::remove (textlines, line_key);
                            textlines =  nl::remove (textlines, line_key);
                            textlines =  nl::set    (textlines, line_key, updated_line);

                            eb.set_cutbuffer_contents (ct::MULTILINE [ "", "" ]);                               # Empirically, this works to effectively put a single newline in the cutbuffer.  I should read and document the code to figure out why. :-)

                            WORK  [ mt::TEXTLINES textlines
                                  ];

                        elif (chomped_text != text)                                                             # ... else if we're on the last line and chopping off its terminal newline...
                            #
                            updated_line = chomped_text;

                            updated_line =  mt::MONOLINE  { string =>  updated_line,
                                                            prefix =>  NULL
                                                          };

                            textlines =  nl::remove (textlines, line_key);
                            textlines =  nl::set    (textlines, line_key, updated_line);

                            eb.set_cutbuffer_contents (ct::MULTILINE [ "", "" ]);

                            WORK  [ mt::TEXTLINES textlines
                                  ];
                        else                                                                                    # ... else we're at the end of the last line in the buffer which already lacks a newline, so nothing to do.
                            WORK  [ 
                                  ];
                        fi;
                    fi;
                fi;
            };
        kill_line__editfn
            =
            mt::EDITFN (
              mt::PLAIN_EDITFN
                {
                  name   =>  "kill_line",
                  doc    =>  "Kill to end of line. If at end of line, delete end of line. TBD: With numeric prefix, kill multiple lines starting at point.",
                  args   =>  [],
                  editfn =>  kill_line
                }
              );                                my _ =
        mt::note_editfn  kill_line__editfn;


        fun transpose_chars (arg:                       mt::Editfn_In)                                          # Interchange char under cursor with preceding char on line.  We treat the end-of-line cases differently than emacs because I don't like the emacs handling.  -- 2015-07-17 CrT
            :                                           mt::Editfn_Out
            =
            {   arg ->    { args:                       List( mt::Prompted_Arg ),                               # Args read interactively from user per our __editfn.args spec.
                            textlines:                  mt::Textlines,
                            point:                      g2d::Point,                                             # As in Point_And_Mark.
                            mark:                       Null_Or(g2d::Point),                                    # 
                            lastmark:                   Null_Or(g2d::Point),                                    # 
                            screen_origin:              g2d::Point,                                             # Origin of pane-visible text relative to textmill contents:  (0,0) means we're showing top of buffer at top of textpane.
                            visible_lines:              Int,                                                    # Number of lines of text visible in pane.
                            readonly:                   Bool,                                                   # TRUE iff contents of textmill are currently marked as read-only.
                            keystring:                  String,                                                 # User keystroke that invoked this editfn.
                            numeric_prefix:             Null_Or( Int ),                                         # ^U "Universal numeric prefix" value for this editfn if supplied by user, else NULL.
                            edit_history:               mt::Edit_History,                                       # Recent visible states of textmill, to support undo functionality.
                            pane_tag:                   Int,                                                    # Tag of pane for which this editfn is being invoked.  This is a small int for human/GUI use.
                            pane_id:                    Id,                                                     # Id  of pane for which this editfn is being invoked.
                            mill_id:                    Id,                                                     # Id  of mill for which this editfn is being invoked.
                            to:                         Replyqueue,                                             # The name makes   foo::pass_something(imp) to {. ... }   syntax read well.
                            widget_to_guiboss:          gt::Widget_To_Guiboss,                                  # 
                            mill_to_millboss:           mt::Mill_To_Millboss,
                            #
                            mainmill_modestate:         mt::Panemode_State,                                     # Any persistent per-mode state (e.g., private state for fundamental-mode.pkg) for main mill is available via this.
                            minimill_modestate:         mt::Panemode_State,                                     # Any persistent per-mode state (e.g., private state for    minimill-mode.pkg) for mini mill is available via this.
                            #
                            mill_extension_state:       Crypt,
                            textpane_to_textmill:       mt::Textpane_To_Textmill,                               # NB: We're running in textmill's microthread to guarantee atomicity, so invoking blocking textpane_to_textmill.* fns is likely to deadlock.  See Note[1].
                            mode_to_drawpane:           Null_Or( m2d::Mode_To_Drawpane ),                       # This will be non-NULL iff we specified a non-NULL draw_*_fn in our mt::PANEMODE value at bottom of file (which we do not do in this package).
                            valid_completions:          Null_Or( String -> List(String) )                       # If this is non-NULL then user is entering a commandname or filename or millname(=buffername) on the modeline, and given fn returns all valid completions of string-entered-so-far.
                          };

                if readonly
                    #
                    FAIL "Buffer is read-only";
                else
                    point =  tlj::normalize_point (point, textlines);                                                   # Normalize point because it might be in the middle of a multicolumn tab or other control char.

                    point -> { row, col };
                    #
                    line_key = row;                                                                                     # Internally lines are numbered 0->(N-1) (but we display them to user as 1-N).

                    case (nl::find (textlines, line_key))
                        #
                        THE textline
                            =>
                            {   text         =  mt::visible_line  textline;
                                chomped_text =  string::chomp     text;

                                my (col1, col2)
                                    =
                                    if (col > 0)
                                        #
                                        point' = { row => point.row,                                                    # Because 'point' is normalized, point.col-1 is guaranteed to be somewhere on the preceding char.
                                                   col => point.col - 1
                                                 };
                                        point' = tlj::normalize_point (point', textlines);                              # Now point'.col is guaranteed to be at the start of the preceding char.

                                        (point'.col, col);                                                              # Return (first_column_of_preceding_char, first_column_of_cursor_char)
                                    else
                                        (string::expand_tabs_and_control_chars                                          # Figure length-in-screen-cols of char under cursor.
                                          {
                                            utf8text    =>  chomped_text,
                                            startcol    =>  0,
                                            screencol1  =>  point.col,
                                            screencol2  => -1,                                                          # Don't-care.
                                            utf8byte    => -1                                                           # Don't-care.
                                          })
                                          ->
                                          { screencol1_colcount_on_screen:      Int,
                                            ...
                                          };
                                        (col, col + screencol1_colcount_on_screen);                                     # Return (first_column_of_cursor_char, first_column_of_next_char), since there is no preceding char.  A reasonable alternative would be to do nothing in this case.
                                    fi;

                                (string::expand_tabs_and_control_chars                                                  # We call  expand_tabs_and_control_chars  twice, first time is to get actual length-in-screen-cols of line.
                                  {                                                                                     # We can't combine that call with next because expand_tabs_and_control_chars() will blank-pad output as needed to make screencol1/screencol2 valid offsets.
                                    utf8text    =>  chomped_text,
                                    startcol    =>  0,
                                    screencol1  => -1,                                                                  # Don't-care.
                                    screencol2  => -1,                                                                  # Don't-care.
                                    utf8byte    => -1                                                                   # Don't-care.
                                  })
                                  ->
                                  { screentext_length_in_screencols:    Int,
                                    ...
                                  };

                                (string::expand_tabs_and_control_chars                                                  # Now find out chomped_text byte offsets of our two chars to be transposed, along with length-in-bytes for each.
                                  {
                                    utf8text    =>  chomped_text,
                                    startcol    =>  0,
                                    screencol1  =>  col1,
                                    screencol2  =>  col2,
                                    utf8byte    => -1                                                                   # Don't-care.
                                  })
                                  ->
                                  { screencol1_byteoffset_in_utf8text:  Int,
                                    screencol1_bytescount_in_utf8text:  Int,
                                    #
                                    screencol2_byteoffset_in_utf8text:  Int,
                                    screencol2_bytescount_in_utf8text:  Int,
                                    ...
                                  };

                                if (col2 >= screentext_length_in_screencols)
                                    #
                                    WORK [ ];                                                                           # Cursor is on non-existent char past end of existing line.  Don't fail, but don't do anything either.
                                else
                                                                                                                        # Cursor is on an existing char, possibly a multibyte utf8 char.  Excise it by replacing the line with the concatenation of the substrings preceding and following the char.
                                    text_before_charpair
                                        =
                                        string::substring
                                          (
                                            text,                                                                       # String from which to extract substring.
                                            0,                                                                          # The substring we want starts at offset 0.
                                            screencol1_byteoffset_in_utf8text                                           # The substring we want runs to location of cursor.  Treating cursor offset as length works (only) because we're starting substring at offset zero.
                                          );

                                    text_for_char1
                                        =
                                        string::substring
                                          (
                                            text,                                                                       # 
                                            screencol1_byteoffset_in_utf8text,                                          # 
                                            screencol1_bytescount_in_utf8text                                           # 
                                          );

                                    text_for_char2
                                        =
                                        string::substring
                                          (
                                            text,                                                                       # 
                                            screencol2_byteoffset_in_utf8text,                                          # 
                                            screencol2_bytescount_in_utf8text                                           # 
                                          );

                                    text_beyond_charpair
                                        =
                                        string::extract
                                          (
                                            text,                                                                       # String from which to extract substring.
                                            screencol2_byteoffset_in_utf8text + screencol2_bytescount_in_utf8text,      # Substring starts immediately after the byte(s) under the cursor.  (Cursor will mark multiple bytes only if it is on a multibyte utf8 char.)
                                            NULL                                                                        # Substring runs to end of 'text'.
                                          );

                                    updated_text        =  string::cat  [ text_before_charpair,
                                                                          text_for_char2,
                                                                          text_for_char1,
                                                                          text_beyond_charpair
                                                                        ];

                                    updated_text        = mt::MONOLINE  { string =>  updated_text,
                                                                          prefix =>  NULL
                                                                        };

                                    updated_textlines                                                                   # First remove existing line -- nl::set does NOT remove any previous line at that key.
                                        =
                                        (nl::remove (textlines, line_key))
                                        except _ = textlines;                                                           # This will happen if there is no line 'line_key' in textlines.

                                    updated_textlines                                                                   # Now insert updated line.
                                        =
                                        nl::set (updated_textlines, line_key, updated_text);


                                    col' =  {                                                                           # We want to leave cursor one char to the right of the interchanged chars.  This is nontrivial if one of them was a tab (say).
                                                text_before_updated_cursor
                                                    =
                                                    string::cat [ text_before_charpair,
                                                                  text_for_char2,
                                                                  text_for_char1
                                                                ];

                                                (string::expand_tabs_and_control_chars                                  # Now find out chomped_text byte offsets of our two chars to be transposed, along with length-in-bytes for each.
                                                  {
                                                    utf8text    =>  text_before_updated_cursor,
                                                    startcol    =>  0,
                                                    screencol1  => -1,                                                  # Don't-care.
                                                    screencol2  => -1,                                                  # Don't-care.
                                                    utf8byte    => -1                                                   # Don't-care.
                                                  })
                                                  ->
                                                  { screentext_length_in_screencols:    Int,
                                                    ...
                                                  };

                                                screentext_length_in_screencols;
                                            };

                                    WORK  [ mt::TEXTLINES updated_textlines,
                                            mt::POINT { row, col => col' }                                              # Move the cursor one char to right.
                                          ];
                                fi;     
                            };

                        NULL     => WORK [ ];                                                                                   # Cursor is on non-existent line.  Don't fail, but don't do anything either.
                    esac;
                fi;
            };
        transpose_chars__editfn
            =
            mt::EDITFN (
              mt::PLAIN_EDITFN
                {
                  name   =>  "transpose_chars",
                  doc    =>  "Interchange current and previous char.",
                  args   =>  [],
                  editfn =>  transpose_chars
                }
              );                                my _ =
        mt::note_editfn  transpose_chars__editfn;


        fun exchange_point_and_mark (arg:               mt::Editfn_In)                                          # 
            :                                           mt::Editfn_Out
            =
            {   arg ->    { args:                       List( mt::Prompted_Arg ),                               # Args read interactively from user per our __editfn.args spec.
                            textlines:                  mt::Textlines,
                            point:                      g2d::Point,                                             # As in Point_And_Mark.
                            mark:                       Null_Or(g2d::Point),                                    # 
                            lastmark:                   Null_Or(g2d::Point),                                    # 
                            screen_origin:              g2d::Point,                                             # Origin of pane-visible text relative to textmill contents:  (0,0) means we're showing top of buffer at top of textpane.
                            visible_lines:              Int,                                                    # Number of lines of text visible in pane.
                            readonly:                   Bool,                                                   # TRUE iff contents of textmill are currently marked as read-only.
                            keystring:                  String,                                                 # User keystroke that invoked this editfn.
                            numeric_prefix:             Null_Or( Int ),                                         # ^U "Universal numeric prefix" value for this editfn if supplied by user, else NULL.
                            edit_history:               mt::Edit_History,                                       # Recent visible states of textmill, to support undo functionality.
                            pane_tag:                   Int,                                                    # Tag of pane for which this editfn is being invoked.  This is a small int for human/GUI use.
                            pane_id:                    Id,                                                     # Id  of pane for which this editfn is being invoked.
                            mill_id:                    Id,                                                     # Id  of mill for which this editfn is being invoked.
                            to:                         Replyqueue,                                             # The name makes   foo::pass_something(imp) to {. ... }   syntax read well.
                            widget_to_guiboss:          gt::Widget_To_Guiboss,                                  # 
                            mill_to_millboss:           mt::Mill_To_Millboss,
                            #
                            mainmill_modestate:         mt::Panemode_State,                                     # Any persistent per-mode state (e.g., private state for fundamental-mode.pkg) for main mill is available via this.
                            minimill_modestate:         mt::Panemode_State,                                     # Any persistent per-mode state (e.g., private state for    minimill-mode.pkg) for mini mill is available via this.
                            #
                            mill_extension_state:       Crypt,
                            textpane_to_textmill:       mt::Textpane_To_Textmill,                               # NB: We're running in textmill's microthread to guarantee atomicity, so invoking blocking textpane_to_textmill.* fns is likely to deadlock.  See Note[1].
                            mode_to_drawpane:           Null_Or( m2d::Mode_To_Drawpane ),                       # This will be non-NULL iff we specified a non-NULL draw_*_fn in our mt::PANEMODE value at bottom of file (which we do not do in this package).
                            valid_completions:          Null_Or( String -> List(String) )                       # If this is non-NULL then user is entering a commandname or filename or millname(=buffername) on the modeline, and given fn returns all valid completions of string-entered-so-far.
                          };

                mark   =    case (mark, lastmark)
                                #
                                (THE _, _) =>     mark;                                                         # Use 'mark' if it is set.
                                _          => lastmark;                                                         # Use 'lastmark' otherwise.  (In this case lastmark will always be set unless no mark has ever been set in this buffer.)
                            esac;

                result =    case mark
                                #
                                NULL => FAIL "Mark is not set";                                                 # Can't exchange point and mark when mark isn't set!

                                THE mark
                                    =>
                                    if (mark.row <  point.row
                                    or (mark.row == point.row  and  mark.col < point.col))
                                        #
                                        WORK  [ mt::MARK  (THE { row => point.row, col => point.col - 1 }),
                                                mt::POINT mark
                                              ];
                                    else                                                                        # mark > point
                                        WORK  [ mt::MARK   (THE point),
                                                mt::POINT  { row => mark.row, col => mark.col + 1 }
                                              ];
                                    fi;
                            esac;

                result;
            };
        exchange_point_and_mark__editfn
            =
            mt::EDITFN (
              mt::PLAIN_EDITFN
                {
                  name   =>  "exchange_point_and_mark",
                  doc    =>  "Exchange mark and point (cursor) if mark is set.  Fail if mark is not set.",
                  args   =>  [],
                  editfn =>  exchange_point_and_mark
                }
              );                                my _ =
        mt::note_editfn  exchange_point_and_mark__editfn;


        fun beginning_of_buffer (arg:           mt::Editfn_In)                                                  # 
            :                                           mt::Editfn_Out
            =
            {   arg ->    { args:                       List( mt::Prompted_Arg ),                               # Args read interactively from user per our __editfn.args spec.
                            textlines:                  mt::Textlines,
                            point:                      g2d::Point,                                             # As in Point_And_Mark.
                            mark:                       Null_Or(g2d::Point),                                    # 
                            lastmark:                   Null_Or(g2d::Point),                                    # 
                            screen_origin:              g2d::Point,                                             # Origin of pane-visible text relative to textmill contents:  (0,0) means we're showing top of buffer at top of textpane.
                            visible_lines:              Int,                                                    # Number of lines of text visible in pane.
                            readonly:                   Bool,                                                   # TRUE iff contents of textmill are currently marked as read-only.
                            keystring:                  String,                                                 # User keystroke that invoked this editfn.
                            numeric_prefix:             Null_Or( Int ),                                         # ^U "Universal numeric prefix" value for this editfn if supplied by user, else NULL.
                            edit_history:               mt::Edit_History,                                       # Recent visible states of textmill, to support undo functionality.
                            pane_tag:                   Int,                                                    # Tag of pane for which this editfn is being invoked.  This is a small int for human/GUI use.
                            pane_id:                    Id,                                                     # Id  of pane for which this editfn is being invoked.
                            mill_id:                    Id,                                                     # Id  of mill for which this editfn is being invoked.
                            to:                         Replyqueue,                                             # The name makes   foo::pass_something(imp) to {. ... }   syntax read well.
                            widget_to_guiboss:          gt::Widget_To_Guiboss,                                  # 
                            mill_to_millboss:           mt::Mill_To_Millboss,
                            #
                            mainmill_modestate:         mt::Panemode_State,                                     # Any persistent per-mode state (e.g., private state for fundamental-mode.pkg) for main mill is available via this.
                            minimill_modestate:         mt::Panemode_State,                                     # Any persistent per-mode state (e.g., private state for    minimill-mode.pkg) for mini mill is available via this.
                            #
                            mill_extension_state:       Crypt,
                            textpane_to_textmill:       mt::Textpane_To_Textmill,                               # NB: We're running in textmill's microthread to guarantee atomicity, so invoking blocking textpane_to_textmill.* fns is likely to deadlock.  See Note[1].
                            mode_to_drawpane:           Null_Or( m2d::Mode_To_Drawpane ),                       # This will be non-NULL iff we specified a non-NULL draw_*_fn in our mt::PANEMODE value at bottom of file (which we do not do in this package).
                            valid_completions:          Null_Or( String -> List(String) )                       # If this is non-NULL then user is entering a commandname or filename or millname(=buffername) on the modeline, and given fn returns all valid completions of string-entered-so-far.
                          };

                result =    WORK  [ mt::POINT { row => 0, col => 0 }
                                  ];

                result;
            };
        beginning_of_buffer__editfn
            =
            mt::EDITFN (
              mt::PLAIN_EDITFN
                {
                  name   =>  "beginning_of_buffer",
                  doc    =>  "Move point to first char of first line of buffer.",
                  args   =>  [],
                  editfn =>  beginning_of_buffer
                }
              );                                my _ =
        mt::note_editfn  beginning_of_buffer__editfn;


        fun end_of_buffer (arg:                         mt::Editfn_In)                                          # Move 'point' to end of buffer.  That means last line in buffer, just past last char (other than newline).
            :                                           mt::Editfn_Out
            =
            {   arg ->    { args:                       List( mt::Prompted_Arg ),                               # Args read interactively from user per our __editfn.args spec.
                            textlines:                  mt::Textlines,
                            point:                      g2d::Point,                                             # As in Point_And_Mark.
                            mark:                       Null_Or(g2d::Point),                                    # 
                            lastmark:                   Null_Or(g2d::Point),                                    # 
                            screen_origin:              g2d::Point,                                             # Origin of pane-visible text relative to textmill contents:  (0,0) means we're showing top of buffer at top of textpane.
                            visible_lines:              Int,                                                    # Number of lines of text visible in pane.
                            readonly:                   Bool,                                                   # TRUE iff contents of textmill are currently marked as read-only.
                            keystring:                  String,                                                 # User keystroke that invoked this editfn.
                            numeric_prefix:             Null_Or( Int ),                                         # ^U "Universal numeric prefix" value for this editfn if supplied by user, else NULL.
                            edit_history:               mt::Edit_History,                                       # Recent visible states of textmill, to support undo functionality.
                            pane_tag:                   Int,                                                    # Tag of pane for which this editfn is being invoked.  This is a small int for human/GUI use.
                            pane_id:                    Id,                                                     # Id  of pane for which this editfn is being invoked.
                            mill_id:                    Id,                                                     # Id  of mill for which this editfn is being invoked.
                            to:                         Replyqueue,                                             # The name makes   foo::pass_something(imp) to {. ... }   syntax read well.
                            widget_to_guiboss:          gt::Widget_To_Guiboss,                                  # 
                            mill_to_millboss:           mt::Mill_To_Millboss,
                            #
                            mainmill_modestate:         mt::Panemode_State,                                     # Any persistent per-mode state (e.g., private state for fundamental-mode.pkg) for main mill is available via this.
                            minimill_modestate:         mt::Panemode_State,                                     # Any persistent per-mode state (e.g., private state for    minimill-mode.pkg) for mini mill is available via this.
                            #
                            mill_extension_state:       Crypt,
                            textpane_to_textmill:       mt::Textpane_To_Textmill,                               # NB: We're running in textmill's microthread to guarantee atomicity, so invoking blocking textpane_to_textmill.* fns is likely to deadlock.  See Note[1].
                            mode_to_drawpane:           Null_Or( m2d::Mode_To_Drawpane ),                       # This will be non-NULL iff we specified a non-NULL draw_*_fn in our mt::PANEMODE value at bottom of file (which we do not do in this package).
                            valid_completions:          Null_Or( String -> List(String) )                       # If this is non-NULL then user is entering a commandname or filename or millname(=buffername) on the modeline, and given fn returns all valid completions of string-entered-so-far.
                          };

                row =   case (nl::max_key textlines)                                                            # Finding number of last row is fairly easy.
                            #
                            THE row => row;
                            NULL    => 0;                                                                       # Shouldn't happen.
                        esac;
                                                                                                                # Now we find screencol of last char in line. That's harder.  Following code is duplicated from move_end_of_line(), probably we should move it into a shared fn.


                line =  mt::findline (textlines, row);                                                          # Get last line.

                chomped_line =  string::chomp  line;                                                            # Drop terminal newline if any.

                (string::expand_tabs_and_control_chars                                                          # Count number of screencols in last line.
                  {
                    utf8text    =>  chomped_line,
                    startcol    =>  0,
                    screencol1  => -1,                                                                          # Don't care.
                    screencol2  => -1,                                                                          # Don't care.
                    utf8byte    => -1                                                                           # Don't care.
                  })
                  ->
                  { screentext_length_in_screencols,
                    ...
                  };

                col    =  screentext_length_in_screencols;

                col    =  max (0, col - 1);                                                                     # Is this right?  XXX QUERO FIXME

                result =  WORK  [ mt::POINT { row, col }
                                ];
                result;
            };
        end_of_buffer__editfn
            =
            mt::EDITFN (
              mt::PLAIN_EDITFN
                {
                  name   =>  "end_of_buffer",
                  doc    =>  "Move point to last char of last line of buffer.",
                  args   =>  [],
                  editfn =>  end_of_buffer
                }
              );                                my _ =
        mt::note_editfn  end_of_buffer__editfn;

        stipulate
            fun split_pane_vertically_or_horizontally
                  (
                    arg:                                mt::Editfn_In,
                    xirow_or_xicol                                                                              # gt::XI_ROW or gt::XI_COL, depending whether we're splitting horizontally or vertically.
                  )
                  :                                     mt::Editfn_Out
                =
                {   arg ->    { args:                   List( mt::Prompted_Arg ),                               # Args read interactively from user per our __editfn.args spec.
                                textlines:              mt::Textlines,
                                point:                  g2d::Point,                                             # As in Point_And_Mark.
                                mark:                   Null_Or(g2d::Point),                                    # 
                                lastmark:               Null_Or(g2d::Point),                                    # 
                                screen_origin:          g2d::Point,                                             # Origin of pane-visible text relative to textmill contents:  (0,0) means we're showing top of buffer at top of textpane.
                                visible_lines:          Int,                                                    # Number of lines of text visible in pane.
                                readonly:               Bool,                                                   # TRUE iff contents of textmill are currently marked as read-only.
                                keystring:              String,                                                 # User keystroke that invoked this editfn.
                                numeric_prefix:         Null_Or( Int ),                                         # ^U "Universal numeric prefix" value for this editfn if supplied by user, else NULL.
                                edit_history:           mt::Edit_History,                                       # Recent visible states of textmill, to support undo functionality.
                                pane_tag:               Int,                                                    # Tag of pane for which this editfn is being invoked.  This is a small int for human/GUI use.
                                pane_id:                Id,                                                     # Id  of pane for which this editfn is being invoked.
                                mill_id:                Id,                                                     # Id  of mill for which this editfn is being invoked.
                                to:                     Replyqueue,                                             # The name makes   foo::pass_something(imp) to {. ... }   syntax read well.
                                widget_to_guiboss:      gt::Widget_To_Guiboss,                                  # 
                                mill_to_millboss:       mt::Mill_To_Millboss,
                                #
                                mainmill_modestate:     mt::Panemode_State,                                     # Any persistent per-mode state (e.g., private state for fundamental-mode.pkg) for main mill is available via this.
                                minimill_modestate:     mt::Panemode_State,                                     # Any persistent per-mode state (e.g., private state for    minimill-mode.pkg) for mini mill is available via this.
                                #
                                mill_extension_state:   Crypt,
                                textpane_to_textmill:   mt::Textpane_To_Textmill,                               # NB: We're running in textmill's microthread to guarantee atomicity, so invoking blocking textpane_to_textmill.* fns is likely to deadlock.  See Note[1].
                                mode_to_drawpane:       Null_Or( m2d::Mode_To_Drawpane ),                       # This will be non-NULL iff we specified a non-NULL draw_*_fn in our mt::PANEMODE value at bottom of file (which we do not do in this package).
                                valid_completions:      Null_Or( String -> List(String) )                       # If this is non-NULL then user is entering a commandname or filename or millname(=buffername) on the modeline, and given fn returns all valid completions of string-entered-so-far.
                              };

                    textpane_to_textmill
                        ->
                        mt::TEXTPANE_TO_TEXTMILL  t2t;

                    t2t.app_to_mill
                        ->
                        mt::APP_TO_MILL  a2m;

                    a2m.pass_pane_guiplan to {.                                                                 # pass_pane_guiplan() synthesizes a guiplan for a pane which will display the state of textmill 'a2m'.
                        #                                                                                       # Ultimately this invokes  make_pane_guiplan'  in  src/lib/x-kit/widget/edit/make-textpane.pkg
                        pane_guiplan = #guiplan;

                        do_while_not {.                                                                         # Repeat guipith edit until it takes.  This is needed because other concurrent microthreads may be
                            #                                                                                   # attempting overlapping guipith edits with us.  This avoids deadlock at a (tiny) risk of livelock.
                            get_guipiths             =  widget_to_guiboss.g.get_guipiths;
                            install_updated_guipiths =  widget_to_guiboss.g.install_updated_guipiths;

                            (get_guipiths ())
                                ->
                                (gui_version, guipiths)
                                     #
                                     :  (Int, idm::Map( gt::Xi_Hostwindow_Info ))
                                     ;

                            guipiths =  gtj::guipith_map (guipiths, options)
                                            where
                                                fun do_widget  (w: gt::Xi_Widget_Type):  gt::Xi_Widget_Type
                                                    =
                                                    case w
                                                        #
                                                        gt::XI_FRAME
                                                          { id:                 Id,
                                                            frame_widget:               gt::Xi_Widget_Type,                             # Widget which will draw the frame surround.
                                                            widget:                     gt::Xi_Widget_Type                              # Widget-tree to draw surrounded by frame.
                                                          }
                                                            =>
                                                            case frame_widget
                                                                #
                                                                gt::XI_WIDGET
                                                                  {
                                                                    widget_id:          Id,
                                                                    widget_layout_hint: gt::Widget_Layout_Hint,
                                                                    doc:                        String                                  # Debugging support: Allow XI_WIDGETs to be distinguishable for debug-display purposes.
                                                                  }
                                                                    =>
                                                                    if (not (same_id (widget_id, pane_id)))
                                                                        #
                                                                        w;
                                                                    else
                                                                        xirow_or_xicol                                          # gt::XI_ROW or gt::XI_COL, depending whether we're splitting horizontally or vertically.
                                                                          {
                                                                            id        =>  issue_unique_id (),
                                                                            #
                                                                            first_cut =>  NULL,
                                                                            widgets   =>  [ w,
                                                                                            gt::XI_GUIPLAN  pane_guiplan
                                                                                          ]
                                                                          };
                                                                    fi;


                                                                _ => w;
                                                            esac;

                                                        _ => w;
                                                    esac;

                                                options = [  gtj::XI_WIDGET_TYPE_MAP_FN  do_widget  ]
                                                        #
                                                        : List( gtj::Guipith_Map_Option )
                                                        ;
                                            end;

                            install_updated_guipiths                                                                    # If this returns FALSE we'll loop and retry.
                                #
                                (gui_version, guipiths);
                        };                                                                                                      # do_while_not
                    };

                    result =  WORK  [ 
                                    ];
                    result;
                };
                                                                        
        herein

            fun split_pane_vertically (arg:                     mt::Editfn_In)                                          # Replace the current textpane by two textpanes half as high.
                :                                               mt::Editfn_Out                                          # NB: emacs called panes "windows", which was a mistake because when X came along emacs had to call windows "frames".  We call panes "panes" and windows "windows". :-)
                =
                split_pane_vertically_or_horizontally (arg, gt::XI_COL);
            #
            split_pane_vertically__editfn
                =
                mt::EDITFN (
                  mt::PLAIN_EDITFN
                    {
                      name       =>  "split_pane_vertically",
                      doc        =>  "Replace current textpane by two textpanes half as high.",
                      args       =>  [],
                      editfn =>  split_pane_vertically
                    }
                  );                            my _ =
            mt::note_editfn  split_pane_vertically__editfn;

            fun split_pane_horizontally (arg:           mt::Editfn_In)                                          # Replace the current textpane by two textpanes half as high
                :                                       mt::Editfn_Out
                =
                split_pane_vertically_or_horizontally (arg, gt::XI_ROW);
            #
            split_pane_horizontally__editfn
                =
                mt::EDITFN (
                  mt::PLAIN_EDITFN
                    {
                      name       =>  "split_pane_horizontally",
                      doc        =>  "Replace current textpane by two textpanes half as wide.",
                      args       =>  [],
                      editfn =>  split_pane_horizontally
                    }
                  );                            my _ =
            mt::note_editfn  split_pane_horizontally__editfn;
        end;


        fun rotate_panepair (arg:                       mt::Editfn_In)                                          # Do a 90-degree clockwise rotation of the current panepair: If active window was on top, it winds up at right. If at right, on bottom. If on bottom, at left. If at left, on top.
            :                                           mt::Editfn_Out
            =
            {   arg ->    { args:                       List( mt::Prompted_Arg ),                               # Args read interactively from user per our __editfn.args spec.
                            textlines:                  mt::Textlines,
                            point:                      g2d::Point,                                             # As in Point_And_Mark.
                            mark:                       Null_Or(g2d::Point),                                    # 
                            lastmark:                   Null_Or(g2d::Point),                                    # 
                            screen_origin:              g2d::Point,                                             # Origin of pane-visible text relative to textmill contents:  (0,0) means we're showing top of buffer at top of textpane.
                            visible_lines:              Int,                                                    # Number of lines of text visible in pane.
                            readonly:                   Bool,                                                   # TRUE iff contents of textmill are currently marked as read-only.
                            keystring:                  String,                                                 # User keystroke that invoked this editfn.
                            numeric_prefix:             Null_Or( Int ),                                         # ^U "Universal numeric prefix" value for this editfn if supplied by user, else NULL.
                            edit_history:               mt::Edit_History,                                       # Recent visible states of textmill, to support undo functionality.
                            pane_tag:                   Int,                                                    # Tag of pane for which this editfn is being invoked.  This is a small int for human/GUI use.
                            pane_id:                    Id,                                                     # Id  of pane for which this editfn is being invoked.
                            mill_id:                    Id,                                                     # Id  of mill for which this editfn is being invoked.
                            to:                         Replyqueue,                                             # The name makes   foo::pass_something(imp) to {. ... }   syntax read well.
                            widget_to_guiboss:          gt::Widget_To_Guiboss,                                  # 
                            mill_to_millboss:           mt::Mill_To_Millboss,
                            #
                            mainmill_modestate:         mt::Panemode_State,                                     # Any persistent per-mode state (e.g., private state for fundamental-mode.pkg) for main mill is available via this.
                            minimill_modestate:         mt::Panemode_State,                                     # Any persistent per-mode state (e.g., private state for    minimill-mode.pkg) for mini mill is available via this.
                            #
                            mill_extension_state:       Crypt,
                            textpane_to_textmill:       mt::Textpane_To_Textmill,                               # NB: We're running in textmill's microthread to guarantee atomicity, so invoking blocking textpane_to_textmill.* fns is likely to deadlock.  See Note[1].
                            mode_to_drawpane:           Null_Or( m2d::Mode_To_Drawpane ),                       # This will be non-NULL iff we specified a non-NULL draw_*_fn in our mt::PANEMODE value at bottom of file (which we do not do in this package).
                            valid_completions:          Null_Or( String -> List(String) )                       # If this is non-NULL then user is entering a commandname or filename or millname(=buffername) on the modeline, and given fn returns all valid completions of string-entered-so-far.

                          };

                done = REF FALSE;

                do_while_not {.                                                                                 # Repeat guipith edit until it takes.  This is needed because other concurrent microthreads may be
                    #                                                                                           # attempting overlapping guipith edits with us.  This avoids deadlock at a (tiny) risk of livelock.
                    get_guipiths             =  widget_to_guiboss.g.get_guipiths;
                    install_updated_guipiths =  widget_to_guiboss.g.install_updated_guipiths;

                    (get_guipiths ())
                        ->
                        (gui_version, guipiths)
                             #
                             :  (Int, idm::Map( gt::Xi_Hostwindow_Info ))
                             ;

                    guipiths =  gtj::guipith_map (guipiths, options)
                                    where
                                        fun is_us (widget: gt::Xi_Widget_Type):  Bool                                   # 
                                            =                                                                           #
                                            case widget                                                                 #
                                                #                                                                       #
                                                gt::XI_FRAME { frame_widget => gt::XI_WIDGET { widget_id, ... }, ... }  #
                                                    =>                                                                  #
                                                    if   *done                                          FALSE;          # Do only one substitution.  Without this check, we'll substitute recursively all the way up the tree, leaving only one pane.  (Which is the emacs semantics.)
                                                    elif (same_id (widget_id, pane_id))  done:= TRUE;   TRUE;           # 
                                                    else                                                FALSE;          #
                                                    fi;
                                                                                                                        #
                                                _   =>                                                  FALSE;          #
                                            esac;                                                                       #


                                        fun invert (first_cut: Null_Or(Float))                                          # Replace f by (1.0-f).
                                            =
                                            case first_cut
                                                #
                                                THE f =>  THE (1.0 - f);
                                                NULL  =>  NULL;
                                            esac;

                                        fun do_widget  (widget:  gt::Xi_Widget_Type):  gt::Xi_Widget_Type               #
                                            =                                                                           #
                                            case widget                                                                 #
                                                #                                                                       #
                                                gt::XI_ROW                                                              # If we've  found a ROW...      (Currently we can't just write (gt::XI_ROW | gt::XI_COL) here, we have to duplicate the pattern.)
                                                  {
                                                    id:         Id,
                                                    first_cut:  Null_Or(Float),
                                                    widgets =>  [ topwidget:    gt::Xi_Widget_Type,                     # As above, we handle only ROW and COLs with two widgets.
                                                                  botwidget:    gt::Xi_Widget_Type
                                                                ]
                                                  }
                                                    =>
                                                    if   (is_us topwidget)      gt::XI_COL { id => issue_unique_id(),  widgets => [ topwidget, botwidget ],  first_cut                   };
                                                    elif (is_us botwidget)      gt::XI_COL { id => issue_unique_id(),  widgets => [ topwidget, botwidget ],  first_cut                   };
                                                    else                        widget;                                 # Neither widget in this ROW/COL is us, so leave it unchanged.
                                                    fi;

                                                gt::XI_COL                                                              # ... or if we've found a COL.  
                                                  {
                                                    id:         Id,
                                                    first_cut:  Null_Or(Float),
                                                    widgets =>  [ topwidget:    gt::Xi_Widget_Type,                     # As above, we handle only ROW and COLs with two widgets.
                                                                  botwidget:    gt::Xi_Widget_Type
                                                                ]
                                                  }
                                                    =>
                                                    if   (is_us topwidget)      gt::XI_ROW { id => issue_unique_id(), widgets => [ botwidget, topwidget ], first_cut => invert first_cut };
                                                    elif (is_us botwidget)      gt::XI_ROW { id => issue_unique_id(), widgets => [ botwidget, topwidget ], first_cut => invert first_cut };
                                                    else                        widget;                                 # Neither widget in this ROW/COL is us, so leave it unchanged.
                                                    fi;

                                                _   =>  widget;                                                         # 'widget' is not a ROW/COL, so leave it unchnaged.
                                            esac;

                                        options = [  gtj::XI_WIDGET_TYPE_MAP_FN  do_widget  ]
                                                #
                                                : List( gtj::Guipith_Map_Option )
                                                ;
                                    end;

                        install_updated_guipiths                                                                        # If this returns FALSE we'll loop and retry.
                            #
                            (gui_version, guipiths);
                    };                                                                                                  # do_while_not


                result =  WORK  [ 
                                ];
                result;
            };
        rotate_panepair__editfn
            =
            mt::EDITFN (
              mt::PLAIN_EDITFN
                {
                  name   =>  "rotate_panepair",
                  doc    =>  "Rotate current panepair by ninety degrees.",
                  args   =>  [],
                  editfn =>  rotate_panepair
                }
              );                                my _ =
        mt::note_editfn  rotate_panepair__editfn;



        fun delete_other_pane (arg:                     mt::Editfn_In)                                          # Opposite of split_pane_vertically_or_horizontally:  Replace ROW/COL containing widget with just widget.  We assume a binary tree -- each ROW or COL has two children. (That's what split_pane_horizontally_or_vertically will create.)
            :                                           mt::Editfn_Out
            =
            {   arg ->    { args:                       List( mt::Prompted_Arg ),                               # Args read interactively from user per our __editfn.args spec.
                            textlines:                  mt::Textlines,
                            point:                      g2d::Point,                                             # As in Point_And_Mark.
                            mark:                       Null_Or(g2d::Point),                                    # 
                            lastmark:                   Null_Or(g2d::Point),                                    # 
                            screen_origin:              g2d::Point,                                             # Origin of pane-visible text relative to textmill contents:  (0,0) means we're showing top of buffer at top of textpane.
                            visible_lines:              Int,                                                    # Number of lines of text visible in pane.
                            readonly:                   Bool,                                                   # TRUE iff contents of textmill are currently marked as read-only.
                            keystring:                  String,                                                 # User keystroke that invoked this editfn.
                            numeric_prefix:             Null_Or( Int ),                                         # ^U "Universal numeric prefix" value for this editfn if supplied by user, else NULL.
                            edit_history:               mt::Edit_History,                                       # Recent visible states of textmill, to support undo functionality.
                            pane_tag:                   Int,                                                    # Tag of pane for which this editfn is being invoked.  This is a small int for human/GUI use.
                            pane_id:                    Id,                                                     # Id  of pane for which this editfn is being invoked.
                            mill_id:                    Id,                                                     # Id  of mill for which this editfn is being invoked.
                            to:                         Replyqueue,                                             # The name makes   foo::pass_something(imp) to {. ... }   syntax read well.
                            widget_to_guiboss:          gt::Widget_To_Guiboss,                                  # 
                            mill_to_millboss:           mt::Mill_To_Millboss,
                            #
                            mainmill_modestate:         mt::Panemode_State,                                     # Any persistent per-mode state (e.g., private state for fundamental-mode.pkg) for main mill is available via this.
                            minimill_modestate:         mt::Panemode_State,                                     # Any persistent per-mode state (e.g., private state for    minimill-mode.pkg) for mini mill is available via this.
                            #
                            mill_extension_state:       Crypt,
                            textpane_to_textmill:       mt::Textpane_To_Textmill,                               # NB: We're running in textmill's microthread to guarantee atomicity, so invoking blocking textpane_to_textmill.* fns is likely to deadlock.  See Note[1].
                            mode_to_drawpane:           Null_Or( m2d::Mode_To_Drawpane ),                       # This will be non-NULL iff we specified a non-NULL draw_*_fn in our mt::PANEMODE value at bottom of file (which we do not do in this package).
                            valid_completions:          Null_Or( String -> List(String) )                       # If this is non-NULL then user is entering a commandname or filename or millname(=buffername) on the modeline, and given fn returns all valid completions of string-entered-so-far.

                          };

                done = REF FALSE;

                do_while_not {.                                                                                 # Repeat guipith edit until it takes.  This is needed because other concurrent microthreads may be
                    #                                                                                           # attempting overlapping guipith edits with us.  This avoids deadlock at a (tiny) risk of livelock.
                    get_guipiths             =  widget_to_guiboss.g.get_guipiths;
                    install_updated_guipiths =  widget_to_guiboss.g.install_updated_guipiths;

                    (get_guipiths ())
                        ->
                        (gui_version, guipiths)
                             #
                             :  (Int, idm::Map( gt::Xi_Hostwindow_In