AMViewerOpsImpl.mesa
Russ Atkinson, April 27, 1983 10:51 pm
DIRECTORY
AMBridge USING [ContextPC],
AMModel USING
[Context, ContextSection, RootContext, Section, SectionSource, SectionVersion, Source, SourceFileName, SourceObj, SourceSection, SourceVersion],
AMModelBridge USING [LoadedSection, LoadedSectionForProc, LoadedSectionForProgPC],
AMViewerOps USING [ReportProc, SelectionOption, Severity],
AMTypes USING [GlobalParent, TVType, UnderClass],
BBContext,
BBSafety USING [Mother],
BcdDefs USING [NullVersion, VersionStamp],
Commander USING [CommandProc, Handle, Register],
Convert USING [ValueToRope],
Directory USING [Error, GetProps, Lookup],
File USING [Capability],
FileLookup USING [LookupFile, Result],
LSD USING [Entry, Lookup],
IO,
List,
Process USING [Pause, SecondsToTicks],
ProcessProps,
PutGet USING [FromRope],
Rope,
RTBasic USING [TV],
System USING [GreenwichMeanTime],
TEditDocument USING [LineTable, TEditDocumentData],
TEditImpl USING [InitTEditDocument],
TEditScrolling USING [AutoScroll, ScrollToPosition],
TEditTouchup USING [LockAfterRefresh, UnlockAfterRefresh],
TextNode USING [Forward, Location, Ref],
TimeStamp USING [Null, Stamp],
TiogaMenuOps USING [DefaultMenus, Open],
TiogaOps USING
[GetSelection, Location, LocOffset, LocRelative, Ref, Root, SetSelection, ViewerDoc],
VersionMap USING [FetchName, FetchStamp, Length, Map, MapList],
VersionMapDefaults USING [FileNameFromVersion, GetMapList],
VersionMapOps USING [SourceFileEntry, SourceFileList],
ViewerClasses USING [Viewer],
ViewerOps USING [EnumerateViewers, EnumProc, OpenIcon],
ViewerTools USING [MakeNewTextViewer, SelPos, SelPosRec, SetSelection],
WorldVM USING [LocalWorld, World];
AMViewerOpsImpl: CEDAR MONITOR
IMPORTS AMBridge, AMModel, AMModelBridge, AMTypes, BBContext, BBSafety, Commander, Convert, Directory, FileLookup, List, LSD, IO, Process, ProcessProps, PutGet, Rope, TEditImpl, TEditScrolling, TEditTouchup, TextNode, TiogaMenuOps, TiogaOps, VersionMap, VersionMapDefaults, ViewerOps, ViewerTools, WorldVM
EXPORTS AMViewerOps, VersionMapOps
SHARES VersionMap
= BEGIN OPEN AMViewerOps, RTBasic;
Useful types
Section: TYPE = AMModel.Section;
Source: TYPE = AMModel.Source;
SourceFileEntry: TYPE = VersionMapOps.SourceFileEntry;
SourceFileList: TYPE = VersionMapOps.SourceFileList;
LoadedSection: TYPE = AMModelBridge.LoadedSection;
ROPE: TYPE = Rope.ROPE;
STREAM: TYPE = IO.STREAM;
TV: TYPE = RTBasic.TV;
World: TYPE = WorldVM.World;
Global variables
ropeCreator: PROC [name: ROPE] RETURNS [rope: ROPE] ← NIL;
this is a crock to avoid using RopeFile.Create, yet allow us to test things
oldSetSel: BOOLFALSE;
AutoScrollOK: BOOLTRUE;
skipComments: BOOLTRUE;
lagLagMsg, lagMsg: ROPENIL;
lagErr: ROPENIL;
EXPORTS to AMViewerOps
SourceError: PUBLIC ERROR [reason: ROPE] = CODE;
ReportError: PUBLIC ERROR [msg: ROPE, severity: Severity] = CODE;
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 ← BBSafety.Mother[inner];
IF errmsg = NIL THEN errmsg ← err;
IF errmsg # NIL
THEN Report[report, fatal, " No source: ", errmsg]
ELSE Report[report, success, NIL];
};
OpenSource: PUBLIC PROC
[name: ROPE, index: INT, chars: INT ← 2, report: ReportProc] = {
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;
inner: PROC = TRUSTED {
viewer ← NameToOpenViewer[name, report];
IF viewer # NIL AND index > 0 THEN SetSel[viewer, index, chars, name];
};
err: ROPENIL;
err ← BBSafety.Mother[inner];
IF err # NIL
THEN Report[report, fatal, " Can't open: ", err]
ELSE Report[report, success, " Source opened."];
};
Viewers/Section translations
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, report];
errmsg ← "can't set selection";
WITH s: source^ SELECT FROM
field => start ← s.firstCharIndex;
ENDCASE;
SetSel[viewer, start, 2, name];
errmsg ← NIL;
};
msg: ROPE ← BBSafety.Mother[inner];
IF errmsg = NIL THEN errmsg ← msg;
IF errmsg # NIL THEN Report[report, fatal, errmsg];
};
SourceFromSelection: PUBLIC PROC
[which: SelectionOption ← primary]
RETURNS [name: 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;
name ← 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 {
msg: ROPENIL;
inner: PROC = TRUSTED {
sourceVersion: BcdDefs.VersionStamp ← FileVersion[name];
source: Source ← NIL;
context: AMModel.Context ←
AMModel.RootContext[IF world = NIL THEN WorldVM.LocalWorld[] ELSE world];
IF sourceVersion = BcdDefs.NullVersion THEN {
msg ← "version not accessible";
RETURN;
};
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];
};
shortName: ROPE ← StripDir[name];
err: ROPENIL;
IF Rope.Match["*.mesa", shortName, FALSE]
THEN err ← BBSafety.Mother[inner]
ELSE err ← Rope.Concat[shortName , " is not a mesa file!"];
IF msg # NIL THEN err ← msg;
IF err # NIL THEN ERROR SourceError[lagErr ← err];
};
NameToOpenViewer: PROC
[name: ROPE, report: ReportProc] RETURNS [viewer: ViewerClasses.Viewer ← NIL] = {
remoteName: BOOL = Rope.SkipTo[name, 0, "/>"] # Rope.Size[name];
visit: ViewerOps.EnumProc = {
IF Rope.Match["*.mesa*", v.name, FALSE] AND EqualNames[v.name, name] THEN {
viewer ← v;
RETURN [FALSE]};
};
ViewerOps.EnumerateViewers[visit];
IF viewer = NIL OR viewer.destroyed THEN TRUSTED {
Report[report, comment, " Opening ", name, "... "];
IF remoteName AND ropeCreator # NIL
THEN {
This is a hack, by the way
rope: ROPE ← ropeCreator[name];
viewer ← ViewerTools.MakeNewTextViewer[
info: [name: name, column: left, iconic: TRUE], paint: FALSE];
viewer.data ← PutGet.FromRope[rope];
viewer.file ← name;
TEditImpl.InitTEditDocument[viewer];
TiogaMenuOps.DefaultMenus[viewer];
}
ELSE viewer ← TiogaMenuOps.Open[name];
};
IF viewer.iconic THEN
ViewerOps.OpenIcon[viewer];
};
GFToVersionStamps: PUBLIC PROC
[gf: TV] RETURNS [source,object: BcdDefs.VersionStamp ← BcdDefs.NullVersion] = TRUSTED {
returns BcdDefs.NullVersion if any errors occur
ENABLE {ABORTED => GO TO abort; ANY => GO TO oops};
section: AMModel.Section ← AMModel.ContextSection[gf];
src: AMModel.Source ← AMModel.SectionSource[section];
source ← AMModel.SourceVersion[src];
object ← AMModel.SectionVersion[section];
EXITS
abort => ERROR ABORTED;
oops => {};
};
FileVersion: PUBLIC PROC
[name: ROPE] RETURNS [version: BcdDefs.VersionStamp ← BcdDefs.NullVersion] = TRUSTED {
returns BcdDefs.NullVersion if any errors occur (like missing file)
local: BOOL = Rope.SkipTo[name, 0, "/>"] = name.Size[];
IF local
THEN {
ENABLE Directory.Error => GO TO none;
flat: ROPE ← name.Flatten[];
fc: File.Capability ← Directory.Lookup[LOOPHOLE[flat]];
garbage: STRING ← [64];
version.time ← LOOPHOLE[Directory.GetProps[fc, garbage].createDate];
}
ELSE {
entry: LSD.Entry ← LSD.Lookup[name];
IF entry = NIL
THEN {
Rats! We have to try to get the create date from the remote host!
host: ROPE ← "Indigo";
nameSansHost: ROPE ← name;
pos: INT ← Rope.SkipTo[name, 0, "[/"];
result: FileLookup.Result;
create: System.GreenwichMeanTime;
IF pos = 0 AND pos # name.Size[] THEN {
pos ← Rope.SkipTo[name, 1, "]/"];
host ← name.Flatten[1, pos-1];
nameSansHost ← name.Flatten[pos+1];
};
[result: result, create: create] ← FileLookup.LookupFile[host, nameSansHost];
IF result = ok THEN version.time ← LOOPHOLE[create];
}
ELSE
version.time ← LOOPHOLE[entry.create]
};
EXITS none => {};
};
SourceToFullName: PUBLIC 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"];
fileVersion ← FileVersion[name];
sourceVersion ← AMModel.SourceVersion[source];
IF fileVersion = sourceVersion THEN RETURN [name];
Report[report, comment, " (version map) "];
name ← VersionMapDefaults.FileNameFromVersion[$Source, sourceVersion];
};
EqualNames: PROC [name1,name2: ROPE] RETURNS [BOOL] = {
... takes care of differences between CIFS and IFS file name formats. The file names are assumed in valid formats, although if they are not, the worst that can happen is a wrong answer.
size1: INT ← name1.Size[];
size2: INT ← name2.Size[];
pos1,pos2: INT ← 0;
WHILE pos1 < size1 AND pos2 < size2 DO
c1: CHAR ← name1.Fetch[pos1];
c2: CHAR ← name2.Fetch[pos2];
pos1 ← pos1 + 1;
pos2 ← pos2 + 1;
IF c1 = c2 THEN LOOP;
SELECT c1 FROM
IN ['A..'Z] => c1 ← c1 + ('a-'A);
'[, '>, '] => c1 ← '/;
'< => {
sigh, have to ignore this char, so backup the other position
pos2 ← pos2 - 1;
LOOP};
ENDCASE;
SELECT c2 FROM
IN ['A..'Z] => c2 ← c2 + ('a-'A);
'[, '>, '] => c2 ← '/;
'< => {
sigh, have to ignore this char, so backup the other position
pos1 ← pos1 - 1;
LOOP};
ENDCASE;
IF c1 # c2 THEN RETURN [FALSE];
ENDLOOP;
RETURN [pos1 = size1 AND pos2 = size2];
};
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]];
};
VersionExpectedMessage: PROC
[version: BcdDefs.VersionStamp] RETURNS [msg: ROPE] = TRUSTED {
msg ← Rope.Cat [
"source of ",
Convert.ValueToRope
[[time[time: LOOPHOLE[version.time], useZone: TRUE]]],
" expected"];
};
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];
};
};
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];
};
Report: PROC [report: ReportProc, severity: Severity, r1,r2,r3,r4: ROPENIL] = {
msg: ROPE ← Rope.Cat[r1,r2,r3,r4];
lagLagMsg ← lagMsg;
lagMsg ← msg;
IF report = NIL THEN {
IF severity # fatal AND severity # warning THEN RETURN;
ERROR ReportError[msg, severity];
};
report[msg, severity];
};
Code to export VersionMapOps
FindSource: PUBLIC PROC
[short: ROPE, mapList: VersionMap.MapList ← NIL, firstOnly: BOOLFALSE]
RETURNS [sfl: SourceFileList ← NIL] = TRUSTED {
size: INT ← short.Size[];
match: INT ← -1;
hasDot: BOOL ← short.Index[0, "."] # size;
IF size = 0 THEN RETURN;
IF short.Fetch[size-1] = '* THEN {
match ← size ← size - 1;
short ← short.Substr[0, match];
};
IF match <= 0 AND NOT hasDot THEN {
short ← short.Concat[".mesa"];
size ← short.Size[]};
IF mapList = NIL THEN mapList ← VersionMapDefaults.GetMapList[$Source];
FOR list: VersionMap.MapList ← mapList, list.rest UNTIL list = NIL DO
map: VersionMap.Map ← list.first;
names: ROPE ← map.names;
lag: INT ← names.Index[0, "\n"];
FOR i: INT IN [0..map.Length[]) DO
anglePos: INT ← lag;
versionPos: INT ← 0;
dotPos: INT ← 0;
next: INT ← names.Index[lag+1, "\n"];
FOR pos: INT DECREASING IN [lag..next) DO
c: CHAR ← names.Fetch[pos];
SELECT c FROM
'>, '/ => {anglePos ← pos; EXIT};
'. => IF dotPos = 0 THEN dotPos ← pos;
'! => versionPos ← pos
ENDCASE;
ENDLOOP;
IF anglePos < dotPos AND dotPos < versionPos THEN {
start: INT ← anglePos + 1;
target: INTIF match > 0 THEN match ELSE versionPos-start;
IF target = size AND target = short.Run[0, names, start, FALSE] THEN {
a match, so add it to the list
full: ROPE ← map.FetchName[i];
sfl ← CONS[[map: map, name: full, stamp: map.FetchStamp[i]], sfl];
IF firstOnly THEN RETURN;
};
};
lag ← next;
ENDLOOP;
ENDLOOP;
};
FindSourceInDir: PUBLIC PROC
[dir, short: ROPE, mapList: VersionMap.MapList ← NIL, firstOnly: BOOLFALSE]
RETURNS [sfl: SourceFileList ← NIL] = TRUSTED {
size: INT ← short.Size[];
dirSize: INT = dir.Size[];
match: INT ← -1;
hasDot: BOOL ← short.Index[0, "."] # size;
IF size = 0 THEN RETURN;
IF short.Fetch[size-1] = '* THEN {
match ← size ← size - 1;
short ← short.Substr[0, match];
};
IF match <= 0 AND NOT hasDot THEN {
short ← short.Concat[".mesa"];
size ← short.Size[]};
IF mapList = NIL THEN mapList ← VersionMapDefaults.GetMapList[$Source];
FOR list: VersionMap.MapList ← mapList, list.rest UNTIL list = NIL DO
map: VersionMap.Map ← list.first;
names: ROPE ← map.names;
lag: INT ← names.Index[0, "\n"];
FOR i: INT IN [0..map.Length[]) DO
anglePos: INT ← lag;
versionPos: INT ← 0;
dotPos: INT ← 0;
dirPos: INT ← -1;
next: INT ← names.Index[lag+1, "\n"];
FOR pos: INT DECREASING IN [lag..next) DO
c: CHAR ← names.Fetch[pos];
SELECT c FROM
'>, '/ => {anglePos ← pos; EXIT};
'. => IF dotPos = 0 THEN dotPos ← pos;
'! => versionPos ← pos
ENDCASE;
ENDLOOP;
IF anglePos < dotPos AND dotPos < versionPos THEN {
start: INT ← anglePos + 1;
target: INTIF match > 0 THEN match ELSE versionPos-start;
IF target = size AND target = short.Run[0, names, start, FALSE] THEN {
a match, so add it to the list
full: ROPE ← map.FetchName[i]; -- get the full name
new: SourceFileList ← NIL;
IF dir # NIL THEN {
test for the directory occuring in the right place
dirPos: INT = Rope.Index[full, 0, dir, FALSE];
c: CHAR ← '/;
IF dirPos = full.Size[] THEN GO TO notHere;
IF dirPos > 0 THEN c ← full.Fetch[dirPos-1];
IF c # '/ AND c # '< THEN GO TO notHere;
c ← full.Fetch[dirPos+dirSize];
IF c # '/ AND c # '> THEN GO TO notHere;
};
sfl ← CONS[[map: map, name: full, stamp: map.FetchStamp[i]], sfl];
IF firstOnly THEN RETURN;
EXITS notHere => {};
};
};
lag ← next;
ENDLOOP;
ENDLOOP;
};
OpenViewerFromEntry: PROC [entry: SourceFileEntry] = TRUSTED {
full: ROPE ← entry.name;
short: ROPE ← ShortName[full];
stamp: TimeStamp.Stamp ← FileVersion[short];
readOnly: BOOLTRUE;
IF stamp # TimeStamp.Null THEN {
file may be on local disk, so let's try it
IF stamp = entry.stamp THEN {
hooray, we can use the local file
full ← short;
readOnly ← FALSE};
};
OpenSource[full, 0, 0, ClarkKent];
};
ShortName: PROC [r: ROPE] RETURNS [ROPE] = {
make a long name into a short one
assumes a valid long name, of course
first: INT ← 0;
last: INT ← r.Size[];
FOR i: INT DECREASING IN [0..last) DO
c: CHAR ← r.Fetch[i];
SELECT c FROM
'>, '/ => {first ← i+1; EXIT};
'! => last ← i
ENDCASE;
ENDLOOP;
RETURN [r.Substr[first, last - first]]
};
OpenCommand: Commander.CommandProc = TRUSTED {
[cmd: Handle]
=> [in, out, err: STREAM, commandLine,command: ROPE, propertyList: List.AList]
st: IO.STREAM ← cmd.out;
dir: ROPENIL;
each: PROC [r: ROPE] = TRUSTED {
inStream: IO.STREAMIO.RIS[r];
DO
sfl: SourceFileList ← NIL;
r ← inStream.GetToken[! IO.EndOfStream => EXIT];
IF r.Size[] = 0 THEN RETURN;
sfl ← RemoveDuplicates[FindSourceInDir[dir, r, NIL, FALSE]];
IF sfl = NIL THEN {
st.PutRope["Sorry, '"];
st.PutRope[r];
st.PutRope["' is not in the current Cedar release.\n"];
RETURN};
IF sfl.rest # NIL THEN {
st.PutRope["Sorry, multiple versions for '"];
st.PutRope[r];
st.PutRope["':"];
WHILE sfl # NIL DO
st.PutRope["\n "];
st.PutRope[sfl.first.name];
st.PutRope[" "];
st.Put[[time[[sfl.first.stamp.time]]]];
sfl ← sfl.rest;
ENDLOOP;
st.PutRope["\n"];
RETURN};
OpenViewerFromEntry[sfl.first];
ENDLOOP;
};
SELECT 5 FROM
Rope.Run[cmd.command, 0, "openC", 0, FALSE] => dir ← "Cedar";
Rope.Run[cmd.command, 0, "openP", 0, FALSE] => dir ← "PreCedar";
ENDCASE;
each[cmd.commandLine ! IO.UserAborted => {CONTINUE}];
};
OpenGlobalCommand: Commander.CommandProc = TRUSTED {
[cmd: Handle]
=> [in, out, err: STREAM, commandLine,command: ROPE, propertyList: List.AList]
st: IO.STREAM ← cmd.out;
dir: ROPENIL;
each: PROC [r: ROPE] = TRUSTED {
inStream: IO.STREAMIO.RIS[r];
gf: TVNIL;
DO
fileName: ROPENIL;
r ← inStream.GetToken[! IO.EndOfStream => EXIT];
IF r.Size[] = 0 THEN RETURN;
gf ← BBContext.GlobalFrameSearch[context: NIL, frameName: r, case: FALSE].gf;
IF gf = NIL THEN {
st.PutRope[r];
st.PutRope[" can not be found.\n"];
LOOP;
};
fileName ← SourceFromTV[gf, ClarkKent].name;
IF fileName # NIL THEN
OpenSource[fileName, 0, 0, ClarkKent];
ENDLOOP;
};
each[cmd.commandLine ! IO.UserAborted => {CONTINUE}];
};
ClarkKent: AMViewerOps.ReportProc = TRUSTED {
[msg: ROPE, severity: Severity]
=> [in, out, err: STREAM, commandLine,command: ROPE, propertyList: List.AList]
WITH List.Assoc[$CommanderHandle, ProcessProps.GetPropList[]] SELECT FROM
cmd: Commander.Handle => {
st: IO.STREAM ← cmd.out;
st.PutRope[msg];
};
ENDCASE;
};
RemoveDuplicates: PROC [sfl: SourceFileList] RETURNS [new: SourceFileList ← NIL] = {
IF sfl # NIL THEN {
entry: SourceFileEntry ← sfl.first;
thisStamp: TimeStamp.Stamp ← entry.stamp;
each: SourceFileList ← sfl.rest ← RemoveDuplicates[sfl.rest];
lag: SourceFileList ← new ← sfl;
WHILE each # NIL DO
next: SourceFileList ← each.rest;
IF each.first.stamp = thisStamp
THEN lag.rest ← next
ELSE lag ← each;
each ← next;
ENDLOOP;
};
};
FindCommand: Commander.CommandProc = TRUSTED {
[cmd: Handle]
=> [in, out, err: STREAM, commandLine,command: ROPE, propertyList: List.AList]
st: IO.STREAM ← cmd.out;
each: PROC [r: ROPE] = TRUSTED {
inStream: IO.STREAMIO.RIS[r];
DO
sfl: SourceFileList ← NIL;
r ← inStream.GetToken[! IO.EndOfStream => EXIT];
IF r.Size[] = 0 THEN RETURN;
sfl ← RemoveDuplicates[FindSource[r, NIL, FALSE]];
IF sfl = NIL THEN {
st.PutRope["Sorry, '"];
st.PutRope[r];
st.PutRope["' is not in the current Cedar release.\n"];
RETURN};
st.PutRope[r];
st.PutRope[" =>\n"];
sfl ← FindSource[r, NIL, FALSE];
IF sfl = NIL THEN {
st.PutRope["Sorry, '"];
st.PutRope[r];
st.PutRope["' is not in the current Cedar release.\n"];
RETURN};
WHILE sfl # NIL DO
st.PutRope[" "];
st.PutRope[sfl.first.name];
st.PutRope[" "];
st.Put[[time[[sfl.first.stamp.time]]]];
st.PutRope["\n"];
sfl ← sfl.rest;
ENDLOOP;
ENDLOOP;
};
each[cmd.commandLine ! IO.UserAborted => {CONTINUE}];
};
Init: PROC = TRUSTED {
Commander.Register [
"openr", OpenCommand,
"Opens viewers on Cedar release source files given the short names (.mesa extension is the default). If a short name has multiple long names associated with it, the alternatives are listed, and no viewer is opened for that name. No spelling correction is performed."];
Commander.Register [
"openC", OpenCommand,
"Like openr, but forces directory to be Cedar."];
Commander.Register [
"openG", OpenGlobalCommand,
"Opens source for named global frame."];
Commander.Register [
"openP", OpenCommand,
"Like openr, but forces directory to be PreCedar."];
Commander.Register [
"findr", FindCommand,
"Finds Cedar release source file names given the short names (.mesa extension is the default). No spelling correction is performed."];
};
Init[];
END.