<> <> <> 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, TV, TVType, UnderClass], 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], Rope USING [Cat, Concat, Equal, Fetch, Find, Flatten, Length, Match, ROPE, Size, Text], TEditDocument USING [LineTable, TEditDocumentData], TEditSelectionOps USING [ShowGivenPosition], TEditTouchup USING [LockAfterRefresh, UnlockAfterRefresh], TextNode USING [Forward, Location, Ref], TiogaMenuOps USING [Open], TiogaOps USING [GetSelection, Location, LocOffset, Ref, Root], VersionMapDefaults USING [FileNameFromVersion], ViewerClasses USING [Viewer], ViewerEvents USING [EventProc, EventRegistration, RegisterEventProc, UnRegisterEventProc], ViewerOps USING [EnumerateViewers, OpenIcon], WorldVM USING [LocalWorld, World]; AMViewerOpsImpl: CEDAR MONITOR IMPORTS AMBridge, AMFiles, AMModel, AMModelBridge, AMTypes, BackStop, BasicTime, FS, IO, Rope, TEditSelectionOps, TEditTouchup, TextNode, TiogaMenuOps, TiogaOps, VersionMapDefaults, ViewerEvents, ViewerOps, WorldVM EXPORTS AMViewerOps, FileViewerOps = BEGIN OPEN AMViewerOps, FileViewerOps; <> Context: TYPE = AMModel.Context; Section: TYPE = AMModel.Section; Source: TYPE = AMModel.Source; ROPE: TYPE = Rope.ROPE; TV: TYPE = AMTypes.TV; VersionStamp: TYPE = BcdDefs.VersionStamp; NullVersion: VersionStamp = BcdDefs.NullVersion; Viewer: TYPE = ViewerClasses.Viewer; World: TYPE = WorldVM.World; <<>> <> ViewerFromSection: PUBLIC PROC [section: Section, report: ReportProc] RETURNS [viewer: Viewer _ NIL] = TRUSTED { errmsg: ROPE _ NIL; inner: PROC = TRUSTED { sourceVersion: 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 = 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 selection"; WITH s: source^ SELECT FROM field => start _ s.firstCharIndex; ENDCASE; viewer _ NameToOpenViewer[name, sourceVersion, report, TRUE, start]; IF viewer = NIL THEN RETURN; 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] = { <<... 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: 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: TRUE]; index _ offset; fileName _ viewer.file; SetHint[viewer]; }; }; SectionFromSelection: PUBLIC PROC [world: World _ NIL, which: SelectionOption _ primary] RETURNS [section: Section _ NIL, contexts: LIST OF Context _ NIL] = { <<... 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 Context _ NIL] = TRUSTED { shortName: ROPE _ StripDir[name]; IF Rope.Length[shortName] # 0 THEN { sourceVersion: VersionStamp _ FileVersion[name].version; source: Source _ NIL; context: 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: VersionStamp _ NullVersion] RETURNS [fullName: ROPE _ NIL, version: VersionStamp _ NullVersion] = { <> gmt: BasicTime.GMT = IF desiredVersion = 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: Viewer _ NIL; report: ReportProc--PROC [msg: ROPE, severity: Severity]-- = { IF feedBack # NIL THEN feedBack.PutRope[msg]; }; inner: PROC = TRUSTED { viewer _ NameToOpenViewer[fileName, NullVersion, report, TRUE, index]; }; 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: 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]; }; NameToOpenViewer: PROC [name: ROPE, version: VersionStamp, report: ReportProc, ignoreOnesBeingChanged: BOOL, index: INT] RETURNS [viewer: Viewer _ NIL] = { IF Rope.Length[name] # 0 THEN { short: ROPE _ NIL; IF version = 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[IO.PutFR["%g|%g", [rope[name]], [integer[index]] ]]; RETURN; }; IF viewer.iconic THEN ViewerOps.OpenIcon[viewer]; TEditSelectionOps.ShowGivenPosition[viewer, index]; }; }; VersionExpectedMessage: PROC [version: VersionStamp] RETURNS [ROPE] = { RETURN[IO.PutFR["source of %g expected", [time[BasicTime.FromNSTime[version.time]]]]]; }; SourceToFullName: PROC [source: Source, report: ReportProc] RETURNS [ROPE] = TRUSTED { <> fileVersion, sourceVersion: VersionStamp _ NullVersion; name: ROPE _ 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) "]; RETURN [VersionMapDefaults.FileNameFromVersion[$Source, sourceVersion].name]; }; 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 _ Rope.Size[name]; start: INT _ 0; DO IF pos = 0 THEN {pos _ size; EXIT}; pos _ pos - 1; SELECT Rope.Fetch[name, pos] FROM '! => EXIT; '. => {pos _ size; EXIT}; ENDCASE; ENDLOOP; start _ pos; WHILE start > 0 DO SELECT Rope.Fetch[name, start _ start - 1] FROM '/, '\\, '[, '], '<, '> => { start _ start + 1; EXIT}; ENDCASE; ENDLOOP; RETURN [Rope.Flatten[name, start, pos-start]]; }; FindViewer: PROC [fileName: Rope.ROPE, version: VersionStamp, ignoreOnesBeingChanged: BOOL] RETURNS [viewer: Viewer _ NIL] = { visit: SAFE PROC [v: 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: 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; <> IF Rope.Length[vname] = 0 THEN RETURN [FALSE]; pos _ Rope.Find[vname, fileName, 0, FALSE]; IF pos >= 0 THEN { <> IF version # NullVersion THEN { fileVersion: VersionStamp = FileVersion[vname, version].version; IF fileVersion # version THEN RETURN [TRUE]; }; viewer _ v; RETURN [FALSE]; }; }; viewer _ GetHint[fileName]; IF viewer # NIL AND NOT visit[viewer] THEN RETURN; viewer _ NIL; ViewerOps.EnumerateViewers[visit]; }; GetHint: ENTRY PROC [name: ROPE] RETURNS [Viewer _ NIL] = { <> SELECT TRUE FROM hintViewer = NIL => {}; hintViewer.destroyed => hintViewer _ NIL; Rope.Equal[name, hintName, FALSE] => RETURN [hintViewer]; ENDCASE; RETURN [NIL]; }; SetHint: ENTRY PROC [viewer: Viewer] = { IF hintRegistration # NIL THEN { ViewerEvents.UnRegisterEventProc[hintRegistration, destroy]; hintRegistration _ NIL; }; IF viewer = NIL THEN hintName _ NIL ELSE hintName _ viewer.file; hintViewer _ viewer; IF viewer # NIL THEN { hintRegistration _ ViewerEvents.RegisterEventProc[FlushHint, destroy, viewer, TRUE]; }; }; FlushHint: ViewerEvents.EventProc = { SetHint[NIL]; }; hintName: ROPE _ NIL; hintViewer: Viewer _ NIL; hintRegistration: ViewerEvents.EventRegistration _ NIL; END.