AMViewerOpsImpl.mesa
Russ Atkinson, October 13, 1983 1:36 pm
Paul Rovner, December 15, 1983 11:44 am
DIRECTORY
AMBridge USING [ContextPC],
AMFiles USING [FullFileName],
AMModel USING
[Context, ContextSection, RootContext, Section, SectionSource, Source, SourceFileName, SourceObj, SourceSection, SourceVersion],
AMModelBridge USING [LoadedSection, LoadedSectionForProc, LoadedSectionForProgPC],
AMViewerOps USING [ReportProc, Severity],
AMTypes USING [GlobalParent, TVType, UnderClass, TV],
BackStop USING [Call],
BasicTime USING [GMT, nullGMT, ToNSTime, FromNSTime],
BcdDefs USING [NullVersion, VersionStamp],
FileViewerOps USING [SelectionOption],
FS USING [Error, FileInfo],
IO USING [PutRope, PutFR, STREAM],
Process USING [Pause, SecondsToTicks],
ProcessExtras USING [CheckForAbort],
Rope USING [Cat, Concat, Fetch, Find, Flatten, Match, ROPE, Size, Text, Length],
TEditDocument USING [LineTable, TEditDocumentData],
TEditScrolling USING [AutoScroll, ScrollToPosition],
TEditTouchup USING [LockAfterRefresh, UnlockAfterRefresh],
TextNode USING [Forward, Location, Ref],
TiogaMenuOps USING [Open],
TiogaOps USING
[GetSelection, Location, LocOffset, LocRelative, Ref, Root, SetSelection, ViewerDoc],
VersionMapDefaults USING [FileNameFromVersion],
ViewerClasses USING [Viewer],
ViewerOps USING [EnumerateViewers, OpenIcon, DestroyViewer, BlinkIcon, CreateViewer],
ViewerTools USING [SelPos, SelPosRec, SetSelection],
WorldVM USING [LocalWorld, World];
AMViewerOpsImpl: CEDAR MONITOR
IMPORTS AMBridge, AMFiles, AMModel, AMModelBridge, AMTypes, BackStop, BasicTime, FS, IO, Process, ProcessExtras, Rope, TEditScrolling, TEditTouchup, TextNode, TiogaMenuOps, TiogaOps, VersionMapDefaults, ViewerOps, ViewerTools, WorldVM
EXPORTS AMViewerOps, FileViewerOps
= BEGIN OPEN AMViewerOps, FileViewerOps;
Useful types
Section: TYPE = AMModel.Section;
Source: TYPE = AMModel.Source;
ROPE: TYPE = Rope.ROPE;
TV: TYPE = AMTypes.TV;
World: TYPE = WorldVM.World;
Global variables
oldSetSel: BOOLFALSE;
AutoScrollOK: BOOLTRUE;
skipComments: BOOLTRUE;
lagLagMsg, lagMsg: ROPENIL;
lagErr: ROPENIL;
EXPORTS to AMViewerOps
ViewerFromSection: PUBLIC PROC
[section: AMModel.Section, report: ReportProc]
RETURNS [viewer: ViewerClasses.Viewer ← NIL] = TRUSTED {
errmsg: ROPENIL;
inner: PROC = TRUSTED {
sourceVersion: BcdDefs.VersionStamp;
source: Source ← AMModel.SectionSource[section];
name: ROPE ← AMModel.SourceFileName[source];
start: INT ← 0;
IF name = NIL THEN {
errmsg ← "can't get source name";
RETURN};
sourceVersion ← AMModel.SourceVersion[source];
IF sourceVersion = BcdDefs.NullVersion THEN {
errmsg ← "can't get source version stamp";
RETURN};
name ← SourceToFullName[source, report];
IF name = NIL THEN {
errmsg ← VersionExpectedMessage[sourceVersion];
RETURN};
errmsg ← "can't set source";
viewer ← NameToOpenViewer[name, sourceVersion, report, TRUE];
errmsg ← "can't set selection";
WITH s: source^ SELECT FROM
field => start ← s.firstCharIndex;
ENDCASE;
SetSel[viewer, start, 2, name];
errmsg ← NIL;
};
msg: ROPE ← BackStop.Call[inner];
IF errmsg = NIL THEN errmsg ← msg;
IF errmsg # NIL THEN report[errmsg, fatal];
};
SourceFromSelection: PUBLIC PROC
[which: SelectionOption ← primary]
RETURNS [fileName: ROPENIL, index: INT ← -1] = TRUSTED {
... returns the selected viewer's name and the source index into the viewer. Returns [NIL, -1] if the selection is not in a valid Tioga viewer.
viewer: ViewerClasses.Viewer ← NIL;
start: TiogaOps.Location;
[viewer: viewer, start: start] ←
TiogaOps.GetSelection[IF which = primary THEN primary ELSE feedback];
IF viewer # NIL AND NOT viewer.destroyed AND NOT viewer.newFile THEN {
root: TiogaOps.Ref ← TiogaOps.Root[start.node];
offset: INT
TiogaOps.LocOffset[loc1: [root, 0], loc2: start, skipCommentNodes: skipComments];
index ← offset;
fileName ← viewer.name;
};
};
SectionFromSelection: PUBLIC PROC
[world: World ← NIL, which: SelectionOption ← primary]
RETURNS [section: Section ← NIL, contexts: LIST OF AMModel.Context ← NIL] = TRUSTED {
... returns a location for the given selection (primary or feedback); returns NIL if can't do it. If world = NIL then world ← LocalWorld. warn = TRUE => the version of the source file for the given location does not correspond to the viewer (although this is no guarantee that the wrong thing happened).
name: ROPENIL;
index: INT ← -1;
[name, index] ← SourceFromSelection[which];
[section, contexts] ← SectionFromSource[world, name, index];
};
SectionFromSource: PUBLIC PROC
[world: World ← NIL, name: ROPENIL, index: INT ← 0]
RETURNS [section: Section ← NIL, contexts: LIST OF AMModel.Context ← NIL] = TRUSTED {
shortName: ROPE ← StripDir[name];
sourceVersion: BcdDefs.VersionStamp ← FileVersion[name].version;
source: Source ← NIL;
context: AMModel.Context ←
AMModel.RootContext[IF world = NIL THEN WorldVM.LocalWorld[] ELSE world];
IF index <= 0
THEN
source ← NEW[AMModel.SourceObj ← [
fileName: shortName,
class: prog,
versionStamp: sourceVersion,
sourceRange: entire[]]]
ELSE
source ← NEW[AMModel.SourceObj ← [
fileName: shortName,
class: statement,
versionStamp: sourceVersion,
sourceRange: field[index, index]]];
[section, contexts] ← AMModel.SourceSection[source, context];
};
FileVersion: PROC
[name: ROPE, desiredVersion: BcdDefs.VersionStamp ← BcdDefs.NullVersion]
RETURNS [fullName: ROPENIL, version: BcdDefs.VersionStamp ← BcdDefs.NullVersion] = {
returns BcdDefs.NullVersion if any errors occur (like missing file)
gmt: BasicTime.GMT
= IF desiredVersion = BcdDefs.NullVersion
THEN BasicTime.nullGMT
ELSE BasicTime.FromNSTime[desiredVersion.time];
fullName ← AMFiles.FullFileName[name, gmt];
IF fullName.Length[] = 0 THEN RETURN;
version.time
← BasicTime.ToNSTime[
FS.FileInfo[name: fullName, wantedCreatedTime: gmt ! FS.Error => CONTINUE].created
];
};
SourceFromTV: PUBLIC PROC
[tv: TV, report: ReportProc] RETURNS [name: ROPENIL, index: INT ← -1] = {
gets the source file name and the source index for the given TV, which must be a local frame or global frame, if not successful, then name = NIL & index < 0
errmsg: ROPENIL;
inner: PROC = TRUSTED {
section: Section ← NIL;
source: Source ← NIL;
SELECT AMTypes.UnderClass[AMTypes.TVType[tv]] FROM
procedure =>
section ← AMModelBridge.LoadedSectionForProc[tv].section;
globalFrame =>
section ← AMModel.ContextSection[tv];
localFrame => {
section ← AMModelBridge.LoadedSectionForProgPC[
prog: AMTypes.GlobalParent[tv],
pc: AMBridge.ContextPC[tv]].section;
};
ENDCASE => {errmsg ← "invalid TV"; RETURN};
source ← AMModel.SectionSource[section];
name ← SourceToFullName[source, report];
IF name = NIL THEN {
errmsg ← VersionExpectedMessage[AMModel.SourceVersion[source]];
RETURN};
WITH s: source^ SELECT FROM
entire => index ← 0;
field => index ← s.firstCharIndex;
ENDCASE => ERROR;
};
err: ROPENIL;
Report[report, comment, " Finding source... "];
err ← BackStop.Call[inner];
IF errmsg = NIL THEN errmsg ← err;
IF errmsg # NIL
THEN Report[report, fatal, " No source: ", errmsg]
ELSE Report[report, success];
};
OpenSource: PUBLIC PROC
[fileName: ROPE, index: INT, chars: INT ← 2, feedBack: IO.STREAMNIL] = {
uses the results of GetSource to open a viewer on the source
if index >= 0, then also sets the selection to the given index (for chars characters)
viewer: ViewerClasses.Viewer ← NIL;
report: ReportProc--PROC [msg: ROPE, severity: Severity]-- = {
IF feedBack # NIL THEN feedBack.PutRope[msg];
};
inner: PROC = TRUSTED {
viewer ← NameToOpenViewer[fileName, BcdDefs.NullVersion, report, TRUE];
IF viewer # NIL AND index > 0 THEN SetSel[viewer, index, chars, fileName];
};
err: ROPENIL;
err ← BackStop.Call[inner];
IF err # NIL
THEN Report[report, fatal, " Can't open: ", err]
ELSE Report[report, success, " Source opened."];
};
Report: PROC [report: ReportProc, severity: Severity, r1,r2,r3,r4: ROPENIL] = {
msg: ROPE ← Rope.Cat[r1, r2, r3, r4];
report[msg, severity];
};
OnScreen: PROC [viewer: ViewerClasses.Viewer, point: TextNode.Location] RETURNS [BOOL] = {
OnScreen determines whether or not the given location is visible for the given viewer.
IF viewer = NIL OR point.node = NIL THEN RETURN [FALSE];
IF viewer.destroyed OR viewer.iconic THEN RETURN [FALSE];
WITH viewer.data SELECT FROM
tdd: TEditDocument.TEditDocumentData => {
Now we know that we have a Tioga document
IF TEditTouchup.LockAfterRefresh[tdd, "OnScreen"] THEN {
At this point tdd is really and truly locked up. We must release it at the end of the block or bad things will happen.
ENABLE {UNWIND => TEditTouchup.UnlockAfterRefresh[tdd]};
lines: TEditDocument.LineTable ← tdd.lineTable;
found: BOOLFALSE;
IF lines # NIL AND lines.lastLine >= 4 THEN {
first: TextNode.Location ← lines[1].pos;
last: TextNode.Location ← lines[lines.lastLine-1].pos;
each: TextNode.Ref ← first.node;
IF point.node = first.node AND point.where < first.where THEN GO TO quickOut;
IF point.node = last.node AND point.where > last.where THEN GO TO quickOut;
WHILE each # NIL DO
IF each = point.node THEN {found ← TRUE; EXIT};
IF each = last.node THEN EXIT;
each ← TextNode.Forward[each].nx;
ENDLOOP;
EXITS quickOut => {};
};
TEditTouchup.UnlockAfterRefresh[tdd];
RETURN [found];
};
};
ENDCASE;
RETURN [FALSE];
};
SetSel: PROC [viewer: ViewerClasses.Viewer, pos,len: INT, openName: ROPENIL] = TRUSTED {
WHILE viewer.iconic AND NOT viewer.destroyed DO
ViewerOps.OpenIcon[viewer];
Process.Pause[Process.SecondsToTicks[1]];
ENDLOOP;
IF viewer.destroyed THEN RETURN;
IF oldSetSel
THEN
ViewerTools.SetSelection[viewer, NEW[ViewerTools.SelPosRec ← [pos, len]]]
ELSE {
x: TiogaOps.Ref ← TiogaOps.ViewerDoc[viewer];
loc1: TiogaOps.Location ←
TiogaOps.LocRelative[location: [x, 0], count: pos, skipCommentNodes: skipComments];
loc2: TiogaOps.Location ←
TiogaOps.LocRelative[location: loc1, count: len, skipCommentNodes: skipComments];
r: REF ← loc1.node;
opaqueLoc: TextNode.Location ← [NARROW[r], loc1.where];
TiogaOps.SetSelection [
viewer: viewer, start: loc1, end: loc2, level: char, caretBefore: TRUE, pendingDelete: FALSE, which: feedback];
IF AutoScrollOK OR OnScreen[viewer, opaqueLoc]
THEN TEditScrolling.AutoScroll[viewer: viewer, tryToGlitch: TRUE, id: feedback]
ELSE TEditScrolling.ScrollToPosition[viewer, opaqueLoc];
};
};
NameToOpenViewer: PROC
[name: ROPE, version: BcdDefs.VersionStamp, report: ReportProc, ignoreOnesBeingChanged: BOOL]
RETURNS [viewer: ViewerClasses.Viewer ← NIL] = {
short: ROPENIL;
IF version = BcdDefs.NullVersion THEN version ← FileVersion[name].version;
short ← StripDir[name];
viewer
← FindViewer[fileName: short,
    version: version,
    ignoreOnesBeingChanged: ignoreOnesBeingChanged];
IF viewer = NIL OR viewer.destroyed THEN TRUSTED {
Report[report, comment, " Opening ", name, "... "];
viewer ← TiogaMenuOps.Open[name];
};
IF viewer.iconic THEN ViewerOps.OpenIcon[viewer];
};
VersionExpectedMessage: PROC [version: BcdDefs.VersionStamp] RETURNS [ROPE] = {
RETURN[IO.PutFR["source of %g expected", [time[BasicTime.FromNSTime[version.time]]]]];
};
SourceToFullName: PROC
[source: Source, report: ReportProc] RETURNS [name: ROPE] = TRUSTED {
returns NIL if the file is not available in the right source
fileVersion, sourceVersion: BcdDefs.VersionStamp ← BcdDefs.NullVersion;
name ← AMModel.SourceFileName[source];
IF NOT Rope.Match["*.mesa*", name, FALSE] THEN name ← name.Concat[".mesa"];
sourceVersion ← AMModel.SourceVersion[source];
[name, fileVersion] ← FileVersion[name, sourceVersion];
IF fileVersion = sourceVersion THEN RETURN [name];
Report[report, comment, " (version map) "];
name ← VersionMapDefaults.FileNameFromVersion[$Source, sourceVersion];
};
StripDir: PROC [name: ROPE] RETURNS [Rope.Text] = {
... turns a long path name into a short path name, stripping off the version information and the directory, but preserving the extension.
size,pos: INT ← name.Size[];
start: INT ← 0;
WHILE (pos ← pos - 1) >= 0 DO
SELECT name.Fetch[pos] FROM
'! => EXIT;
'. => {pos ← size; EXIT};
ENDCASE;
ENDLOOP;
start ← pos;
WHILE start > 0 DO
SELECT name.Fetch[start ← start - 1] FROM
'/, '\\, '[, '], '<, '> => {
start ← start + 1;
EXIT};
ENDCASE;
ENDLOOP;
RETURN [name.Flatten[start, pos-start]];
};
FindViewer: PROC [
 fileName: Rope.ROPE,
 version: BcdDefs.VersionStamp,
 ignoreOnesBeingChanged: BOOL]
RETURNS[viewer: ViewerClasses.Viewer ← NIL] = {
visit: SAFE PROC [v: ViewerClasses.Viewer] RETURNS [BOOLTRUE] = TRUSTED {
-- return TRUE to continue, FALSE to stop
vname: Rope.ROPE ← v.file;
pos: INT;
someSaveInProgress: PROC RETURNS[ans: BOOLFALSE] = CHECKED {
vv: ViewerClasses.Viewer ← v;
IF vv.saveInProgress THEN RETURN[TRUE];
WHILE (vv ← vv.link) # NIL AND (vv # v) DO
IF vv.saveInProgress THEN RETURN[TRUE];
ENDLOOP;
};
IF ignoreOnesBeingChanged AND (v.newVersion OR someSaveInProgress[])
THEN RETURN [TRUE];
don't find viewers with changed contents or being saved
IF vname = NIL THEN vname ← v.name;
just in case the file name is not present
pos ← Rope.Find[vname, fileName, 0, FALSE];
IF pos >= 0 THEN {
Same short name, so check the version stamp
IF version # BcdDefs.NullVersion THEN {
fileVersion: BcdDefs.VersionStamp = FileVersion[vname, version].version;
IF fileVersion # version THEN RETURN [TRUE];
};
viewer ← v;
RETURN [FALSE];
};
};
ViewerOps.EnumerateViewers[visit];
};
ShowLog: PUBLIC PROC [
 fileName: Rope.ROPE,
 destroyIt: BOOLFALSE,
 createIconic: BOOLFALSE,
 blinkIfIconic: BOOLTRUE] = {
logViewer: ViewerClasses.Viewer ← NIL;
FileExists: PROC [fileName: Rope.ROPE] RETURNS[ans: BOOL ← TRUE] = {
[] ← FS.FileInfo[fileName ! FS.Error => {ans ← FALSE; CONTINUE}];
};
CreateLog: PROC [fileName: Rope.ROPE, iconic: BOOL]
RETURNS[viewer: ViewerClasses.Viewer] = {
IF iconic
THEN viewer ← ViewerOps.CreateViewer
[flavor: $Text, info: [name: fileName, file: fileName, iconic: iconic, icon: typescript]]
ELSE {viewer ← TiogaMenuOps.Open[fileName]; viewer.icon ← typescript};
};
logViewer
← FindViewer[fileName: fileName,
    version: BcdDefs.NullVersion,
    ignoreOnesBeingChanged: TRUE];
IF destroyIt
THEN {IF logViewer # NIL THEN {ViewerOps.DestroyViewer[logViewer]; logViewer ← NIL}}
ELSE {
IF logViewer = NIL AND FileExists[fileName]
THEN logViewer ← CreateLog[fileName: fileName, iconic: createIconic];
IF logViewer # NIL
AND logViewer.iconic
AND blinkIfIconic
THEN ViewerOps.BlinkIcon[logViewer];
};
};
WaitUntilSaved: PUBLIC PROC [fileName: Rope.ROPE, feedBack: IO.STREAMNIL] = {
viewer: ViewerClasses.Viewer ← NIL;
viewer ← FindViewer[
 fileName: fileName,
 version: BcdDefs.NullVersion,
 ignoreOnesBeingChanged: FALSE];
IF viewer # NIL AND viewer.newVersion THEN {
SomeSaveInProgress: PROC RETURNS[ans: BOOLFALSE] = CHECKED {
v: ViewerClasses.Viewer ← viewer;
IF viewer.saveInProgress THEN RETURN[TRUE];
WHILE (v ← v.link) # NIL AND (v # viewer) DO
IF v.saveInProgress THEN RETURN[TRUE];
ENDLOOP;
};
IF feedBack # NIL AND NOT SomeSaveInProgress[]
THEN feedBack.PutRope[Rope.Cat["***Please save ", viewer.name, "..."]];
UNTIL SomeSaveInProgress[]
DO
Process.Pause[Process.SecondsToTicks[1]];
ProcessExtras.CheckForAbort[];
ENDLOOP;
IF feedBack # NIL AND SomeSaveInProgress[]
THEN feedBack.PutRope[Rope.Cat["***Waiting for ", viewer.name, " to finish being saved..."]];
WHILE SomeSaveInProgress[] DO
TRUSTED {
Process.Pause[Process.SecondsToTicks[1]];
ProcessExtras.CheckForAbort[];
};
ENDLOOP;
IF feedBack # NIL THEN feedBack.PutRope["ok\n"];
};
};
END.