-- TextLooks.mesa
-- written by Bill Paxton, February 1981
-- last edit by Bill Paxton, March 24, 1981 3:46 PM

-- This package provides Looks for text in Tioga
-- it allows a client to associate looks with characters in a rope
-- looks are represented as a vector of 32 bits

-- The implementation expects that long sequences will have the same looks
-- and thus uses a run encoding to save space.

-- The runs of looks are immutable just like the text in a rope.
-- i.e., you don’t change runs;
-- instead you build new ones out of parts of old ones

-- Also like ropes, we use piece tables in the implementation
-- thus making the cost of setting looks independent of the size of the rope.


TextLooks: DEFINITIONS =
BEGIN

Runs: TYPE = REF RunsBody;
RunsBody: TYPE = PRIVATE RECORD [SELECT kind:OfRuns FROM
substr => [base:Runs, start,len:Card],
concat => [base,rest:Runs, size,pos:Card],
replace => [base,replace:Runs, start,oldPos,newPos,size:Card],
change => [base:Runs, remove,add:Looks, start,len,size:Card],
base => [runs:SEQUENCE length:NAT OF Run],
ENDCASE];
OfRuns: PRIVATE TYPE = {substr, concat, replace, change, base};
Run: TYPE = PRIVATE RECORD [after: Card ← 0, looks: Looks ← noLooks];
BaseRuns: PRIVATE TYPE = REF base RunsBody;

FlatMax: Card = 10; -- flatten if length of run <= FlatMax

Looks: TYPE = PACKED ARRAY Look OF Bit;
Look: TYPE = CHARACTER [’a..’a+31]; -- 32 bits; indexed from "a"
Bit: TYPE = BOOLEAN ← FALSE;
noLooks: Looks = ALL[FALSE];
allLooks: Looks = ALL[TRUE];

LooksBytes: TYPE = MACHINE DEPENDENT RECORD [
byte0(0:0..7), byte1(0:8..15), byte2(1:0..7): [0..255] ← 0,
extra(1:8..9): [0..3] ← 0, spare(1:10..15): [0..63] ← 0];
LooksExtra: PROC [looks: Looks] RETURNS [[0..3]] =
INLINE { RETURN[LOOPHOLE[looks, LooksBytes].extra] };

Card: TYPE = LONG CARDINAL;
MaxCard: Card = LAST[Card];

OutOfBounds: ERROR;

-- **** General operations ****

FetchLooks: PROC [runs: Runs, index: Card] RETURNS [Looks];
-- returns the looks for the character at the given location

CreateRun: PROC [len: Card, looks: Looks ← noLooks] RETURNS [Runs];


-- **** Operations on runs ****

CountRuns: PROC [runs: Runs, start, len: Card, limit: Card ← MaxCard,
merge: BOOLEAN ← FALSE, firstLooks: Looks ← noLooks]
RETURNS [count: Card, nonempty: BOOLEAN, lastLooks: Looks];
-- counts the number of runs in the given range
-- stops counting if reaches limit
-- if merge is true, then doesn’t count first run if its looks=firstLooks

ChangeLooks: PROC [
runs: Runs, size: Card, remove, add: Looks,
start: Card ← 0, len: Card ← MaxCard]
RETURNS [Runs];
-- first remove then add in the given range

AddLooks: PROC [
runs: Runs, size: Card, add: Looks,
start: Card ← 0, len: Card ← MaxCard]
RETURNS [Runs] = INLINE {
RETURN [ ChangeLooks[runs, size, noLooks, add, start, len] ] };

RemoveLooks: PROC [
runs: Runs, size: Card, remove: Looks ← allLooks,
start: Card ← 0, len: Card ← MaxCard]
RETURNS [Runs] = INLINE {
RETURN [ ChangeLooks[runs, size, remove, noLooks, start, len] ] };

SetLooks: PROC [
runs: Runs, size: Card, new: Looks,
start: Card ← 0, len: Card ← MaxCard]
RETURNS [Runs] = INLINE {
RETURN [ ChangeLooks[runs, size, allLooks, new, start, len] ] };

ClearLooks: PROC [
runs: Runs, size: Card, start: Card ← 0, len: Card ← MaxCard]
RETURNS [Runs] = INLINE {
RETURN [ ChangeLooks[runs, size, allLooks, noLooks, start, len] ] };

-- there is no MapLookRuns. use TiogaRunReader’s instead


-- **** Editing Operations ****

Substr: PROC [base: Runs, start: Card ← 0, len: Card ← MaxCard]
RETURNS [Runs];
-- returns a substring of the runs

Concat: PROC [base, rest: Runs, baseLen, restLen: Card] RETURNS [Runs];
-- returns the concatenation of two Runs

Replace: PROC [base, replace: Runs, start, len, baseSize, repSize: Card]
RETURNS [Runs];
-- returns new Runs with the given range replaced

Delete: PROC [base: Runs, start, len, baseSize: Card] RETURNS [Runs] = INLINE
{ RETURN [ Replace[base, NIL, start, len, baseSize, 0] ] };

Insert: PROC [base, insert: Runs, loc, baseSize, insertSize: Card]
RETURNS [Runs] = INLINE { RETURN [
Replace[base, insert, loc, 0, baseSize, insertSize] ] };

Copy: PROC [dest,source: Runs, destSize,destLoc,start,len: Card]
RETURNS [Runs] = INLINE { RETURN [
Replace[dest, Substr[source,start,len], destLoc, 0, destSize, len]] };

Move: PROC [base: Runs, baseSize, dest, start, len: Card] RETURNS [Runs];
-- dest must not be in [start..start+len)

Transpose: PROC [base: Runs, baseSize, astart, alen, bstart, blen: Card]
RETURNS [Runs];
-- [astart..astart+alen) must not intersect [bstart..bstart+blen)

BadMove, BadTranspose: ERROR;

AddRun: PROC [
base: Runs, baseSize, loc, len: Card,
inherit: BOOLEAN ← TRUE, looks: Looks ← noLooks]
RETURNS [Runs];

END.