## 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.sublibstipulate
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.pkgherein
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