<> <> <> 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; <> Section: TYPE = AMModel.Section; Source: TYPE = AMModel.Source; ROPE: TYPE = Rope.ROPE; TV: TYPE = AMTypes.TV; World: TYPE = WorldVM.World; <> oldSetSel: BOOL _ FALSE; AutoScrollOK: BOOL _ TRUE; skipComments: BOOL _ TRUE; lagLagMsg, lagMsg: ROPE _ NIL; lagErr: ROPE _ NIL; <<>> <> 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, 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: 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; 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: 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 { 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: ROPE _ NIL, version: BcdDefs.VersionStamp _ BcdDefs.NullVersion] = { <> 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: ROPE _ NIL, index: INT _ -1] = { <> 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 _ 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.STREAM _ NIL] = { <> <= 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: ROPE _ NIL; 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: ROPE _ NIL] = { msg: ROPE _ Rope.Cat[r1, r2, r3, r4]; report[msg, severity]; }; OnScreen: PROC [viewer: ViewerClasses.Viewer, point: TextNode.Location] RETURNS [BOOL] = { <> 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 => { <> IF TEditTouchup.LockAfterRefresh[tdd, "OnScreen"] THEN { <> 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]; }; 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]; }; }; NameToOpenViewer: PROC [name: ROPE, version: BcdDefs.VersionStamp, report: ReportProc, ignoreOnesBeingChanged: BOOL] RETURNS [viewer: ViewerClasses.Viewer _ NIL] = { short: ROPE _ NIL; 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 { <> 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 [BOOL _ TRUE] = TRUSTED { <<-- return TRUE to continue, FALSE to stop>> vname: Rope.ROPE _ v.file; pos: INT; someSaveInProgress: PROC RETURNS[ans: BOOL _ FALSE] = 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]; <> IF vname = NIL THEN vname _ v.name; <> pos _ Rope.Find[vname, fileName, 0, FALSE]; IF pos >= 0 THEN { <> 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: BOOL _ FALSE, createIconic: BOOL _ FALSE, blinkIfIconic: BOOL _ TRUE] = { 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.STREAM _ NIL] = { viewer: ViewerClasses.Viewer _ NIL; viewer _ FindViewer[ fileName: fileName, version: BcdDefs.NullVersion, ignoreOnesBeingChanged: FALSE]; IF viewer # NIL AND viewer.newVersion THEN { SomeSaveInProgress: PROC RETURNS[ans: BOOL _ FALSE] = 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.