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: BOOL ← FALSE;
AutoScrollOK: BOOL ← TRUE;
skipComments: BOOL ← TRUE;
lagLagMsg, lagMsg: ROPE ← NIL;
lagErr: ROPE ← NIL;
SourceError: PUBLIC ERROR [reason: ROPE] = CODE;
ReportError: PUBLIC ERROR [msg: ROPE, severity: Severity] = CODE;
SourceFromTV:
PUBLIC
PROC
[tv: TV, report: ReportProc] RETURNS [name: ROPE ← NIL, 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: ROPE ← NIL;
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: ROPE ← NIL;
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: ROPE ← NIL;
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: ROPE ← NIL;
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: ROPE ← NIL, 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: ROPE ← NIL;
index: INT ← -1;
[name, index] ← SourceFromSelection[which];
[section, contexts] ← SectionFromSource[world, name, index];
};
SectionFromSource:
PUBLIC
PROC
[world: World ← NIL, name: ROPE ← NIL, index: INT ← 0]
RETURNS [section: Section ← NIL, contexts: LIST OF AMModel.Context ← NIL] = TRUSTED {
msg: ROPE ← NIL;
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: ROPE ← NIL;
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:
ROPE ←
NIL] =
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: BOOL ← FALSE;
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:
ROPE ←
NIL] = {
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: BOOL ← FALSE]
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: INT ← IF 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: BOOL ← FALSE]
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: INT ← IF 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: BOOL ← TRUE;
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: ROPE ← NIL;
each:
PROC [r:
ROPE] =
TRUSTED {
inStream: IO.STREAM ← IO.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: ROPE ← NIL;
each:
PROC [r:
ROPE] =
TRUSTED {
inStream: IO.STREAM ← IO.RIS[r];
gf: TV ← NIL;
DO
fileName: ROPE ← NIL;
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.STREAM ← IO.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[];