<> <> <> <> <> <> <<>> <> <<>> <> DIRECTORY BackStop USING [Call], BasicTime USING [GMT, nullGMT], Convert, IO USING [PutRope, PutFR, PutFR1, STREAM], LineNumberExtras, PFS USING [Error, UniqueID, nullUniqueID, PathFromRope, RopeFromPath, FileInfo, GetWDir], PFSNames USING [PATH, Component, Cat, IsAbsolute, EmptyPath, ShortName, ComponentRope], Rope USING [Cat, Concat, Equal, Find, Length, ROPE], SourceFileOps, SourceFileOpsExtras, SystemNames USING [LocalDir], TEditDocument USING [SelectionId], TEditSelectionOps USING [ShowGivenPositionRange], TextNode USING [Ref], TiogaLies, TiogaMenuOps USING [Open], TiogaOps USING [GetProp, GetSelection, Location, LocOffset, Ref, Root, ViewerDoc, WhichSelection], ViewerClasses USING [Column, Viewer], ViewerOps USING [EnumerateViewers, OpenIcon], ViewerPrivate USING [rootViewerTree], ViewerTools USING [GetSelectionContents]; SourceFileOpsViewersImpl: CEDAR MONITOR IMPORTS BackStop, Convert, IO, LineNumberExtras, PFS, PFSNames, Rope, SystemNames, TEditSelectionOps, TiogaLies, TiogaMenuOps, TiogaOps, ViewerOps, ViewerPrivate, ViewerTools EXPORTS SourceFileOps, SourceFileOpsExtras = BEGIN OPEN SourceFileOpsExtras; <> FullPosition: TYPE ~ SourceFileOpsExtras.Position; ShortPosition: TYPE ~ SourceFileOps.Position; PATH: TYPE ~ PFSNames.PATH; STREAM: TYPE = IO.STREAM; Viewer: TYPE = ViewerClasses.Viewer; Severity: TYPE = {success, comment, warning, fatal}; < operation is completed>> < intermediate information>> < something is wrong, but not fatal>> < operation is completed, but did not succeed>> ReportProc: TYPE = PROC [msg: ROPE, severity: Severity]; <<... is the type of user-supplied procedure used to report results in above operations>> <<>> <> mySelectionToTiogaOpsSelection: ARRAY SourceFileOps.WhichSelection OF TiogaOps.WhichSelection = [primary, secondary, feedback]; mySelectionToTEditSelectionOpsSelection: ARRAY SourceFileOps.WhichSelection OF TEditDocument.SelectionId = [primary, secondary, feedback]; Expand: PROC [s: ShortPosition] RETURNS [FullPosition] ~ { RETURN [[s.fileName, nullUniqueID, s.index]]}; Restrict: PROC [f: FullPosition] RETURNS [ShortPosition] ~ { RETURN [[f.fileName, f.index]]}; GetSelection: PUBLIC PROC [selection: SourceFileOps.WhichSelection _ primary] RETURNS [ShortPosition] = {RETURN Restrict[FullGetSelection[selection].pos]}; FullGetSelection: PUBLIC PROC [selection: SourceFileOps.WhichSelection _ primary] RETURNS [pos: FullPosition, contents: ROPE] = { viewer: Viewer; start, end: TiogaOps.Location; [viewer: viewer, start: start, end: end] _ TiogaOps.GetSelection[mySelectionToTiogaOpsSelection[selection]]; contents _ ViewerTools.GetSelectionContents[]; IF viewer # NIL AND NOT viewer.destroyed AND NOT viewer.newFile THEN { root: TiogaOps.Ref _ TiogaOps.Root[start.node]; fileName: FileName _ PFS.PathFromRope[viewer.file]; charStart, charEnd: INT; createUnique: UniqueID; lines: LineNumberExtras.LineRange; lineRange: Range; createVal: REF ANY _ TiogaOps.GetProp[root, $FileCreateDate]; IF createVal#NIL THEN { rgmt: REF BasicTime.GMT; TRUSTED {rgmt _ LOOPHOLE[createVal]}; --YUK! createUnique _ [[rgmt^]]} ELSE createUnique _ nullUniqueID; IF fileName=PFSNames.EmptyPath THEN fileName _ NIL; charStart _ TiogaOps.LocOffset[loc1: [root, 0], loc2: start, skipCommentNodes: TRUE]; charEnd _ TiogaOps.LocOffset[loc1: [root, 0], loc2: end, skipCommentNodes: TRUE]; lines _ LineNumberExtras.PositionsToLines[TiogaLies.OpsRefToTextNodeRef[root], [charStart, charEnd], TRUE]; lineRange _ [MIN[lines.first, Index.LAST], MIN[lines.last, Index.LAST]]; IF start = end THEN {charEnd _ noIndex; lineRange.last _ noIndex}; SetHint[viewer]; RETURN [[fileName, createUnique, [char: [charStart, charEnd], line: lineRange]], contents]}; RETURN [noPosition, contents]}; OpenSource: PUBLIC PROC [desc: ROPE, pos: ShortPosition, feedBack: STREAM _ NIL, selection: WhichSelection _ feedback] = {FullOpenSource[desc, Expand[pos], feedBack, selection]}; FullOpenSource: PUBLIC PROC [desc: ROPE, pos: FullPosition, feedBack: STREAM _ NIL, selection: WhichSelection _ feedback] = { v: Viewer _ NIL; report: ReportProc--PROC [msg: ROPE, severity: Severity]-- = { IF feedBack # NIL THEN {feedBack.PutRope[msg]; feedBack.PutRope["\N"]}; RETURN}; inner: PROC = TRUSTED { ok _ NameToOpenViewer[pos, report, TRUE, selection]; RETURN}; ok: BOOL _ TRUE; err: ROPE _ NIL; err _ BackStop.Call[inner]; IF err#NIL THEN Report[report, fatal, " Can't open: ", err] ELSE IF ok THEN Report[report, success, " Source opened", FullFmtIdxs[pos], "."]; }; FormatPosition: PUBLIC PROC [pos: ShortPosition] RETURNS [Rope.ROPE] ~ {RETURN FullFormatPosition[Expand[pos]]}; FullFormatPosition: PUBLIC PROC [pos: FullPosition] RETURNS [Rope.ROPE] ~ { name: ROPE _ "a broken file name"; time: ROPE _ "a broken time"; IF pos.fileName#NIL AND pos.fileName#PFSNames.EmptyPath THEN name _ PFS.RopeFromPath[pos.fileName !PFS.Error => CONTINUE] ELSE name _ "an unnamed file"; IF pos.uniqueID.egmt.time=BasicTime.nullGMT THEN time _ "unspecified time" ELSE time _ Convert.RopeFromTime[pos.uniqueID.egmt.time, years, seconds, FALSE, FALSE !Convert.Error => CONTINUE]; IF pos.uniqueID.egmt.usecs#0 THEN time _ IO.PutFR["%g + %gus", [rope[time]], [cardinal[pos.uniqueID.egmt.usecs]] ]; IF pos.uniqueID.host#[0, 0] THEN time _ IO.PutFR["%g host [%xH, %xH]", [rope[time]], [cardinal[pos.uniqueID.host.a]], [cardinal[pos.uniqueID.host.b]] ]; RETURN name.Cat[" created at ", time, " ", FullFmtIdxs[pos]]}; FmtIdxs: PUBLIC PROC [pos: ShortPosition] RETURNS [ROPE] ~ {RETURN FullFmtIdxs[Expand[pos]]}; FullFmtIdxs: PUBLIC PROC [pos: FullPosition] RETURNS [ROPE] ~ { SELECT TRUE FROM pos.index = ALL[noRange] => RETURN [" (no position)"]; pos.index[line] = noRange => RETURN IO.PutFR1[" (%g)", [rope[FmtRange[pos.index[char], "char ", "chars "]]]]; pos.index[char] = noRange => RETURN IO.PutFR1[" (%g)", [rope[FmtRange[pos.index[line], "line ", "line "]]]]; ENDCASE => RETURN IO.PutFR[" (%g = %g)", [rope[FmtRange[pos.index[char], "char ", "chars "]]], [rope[FmtRange[pos.index[line], "line ", "line "]]]]}; FmtRange: PUBLIC PROC [r: Range, introSingular, introPlural: ROPE _ NIL] RETURNS [ROPE] ~ { IF r = noRange THEN RETURN introPlural.Concat["unspecified"]; IF r.last = noIndex THEN RETURN introSingular.Concat[Convert.RopeFromInt[r.first]]; RETURN IO.PutFR["%g%g..%g", [rope[introPlural]], [integer[r.first]], [integer[r.last]] ]}; Report: PROC [report: ReportProc, severity: Severity, r1,r2,r3,r4: ROPE _ NIL] = { IF report # NIL THEN { msg: ROPE _ Rope.Cat[r1, r2, r3, r4]; report[msg, severity]; }; }; ColumnLocked: PROC [column: ViewerClasses.Column] RETURNS [locked: BOOL _ FALSE] ~ { viewer: ViewerClasses.Viewer _ ViewerPrivate.rootViewerTree[column]; IF viewer = NIL THEN RETURN[FALSE]; DO IF viewer.lock.count > 0 THEN RETURN[TRUE]; IF (viewer _ viewer.sibling) = NIL THEN EXIT; ENDLOOP; }; NameToOpenViewer: PROC [pos: FullPosition, report: ReportProc, ignoreOnesBeingChanged: BOOL, selection: WhichSelection] RETURNS [BOOL] = { name: PATH ~ pos.fileName; IF name # NIL AND name # PFSNames.EmptyPath THEN { short: PFSNames.Component _ name.ShortName; nameRope: ROPE _ PFS.RopeFromPath[name]; viewer: Viewer _ NIL; viewer _ FindViewer[ fileName: short.ComponentRope, created: pos.uniqueID.egmt.time, ignoreOnesBeingChanged: ignoreOnesBeingChanged]; IF viewer = NIL OR viewer.destroyed THEN TRUSTED { column: ViewerClasses.Column _ IF ColumnLocked[left] THEN right ELSE left; IF ColumnLocked[column] THEN { viewer _ NIL; Report[report, warning, " Due to a locked column, ", nameRope, " wasn't opened."]; } ELSE { Report[report, comment, " Opening ", nameRope, "... "]; viewer _ TiogaMenuOps.Open[nameRope,, column]; }; }; IF viewer#NIL AND viewer.iconic THEN IF ColumnLocked[viewer.column] THEN viewer _ NIL ELSE ViewerOps.OpenIcon[viewer]; SELECT TRUE FROM viewer=NIL => { Report[report, warning, Rope.Concat[ " couldn't open; would have highlighted ", FullFmtIdxs[pos]]]; RETURN [FALSE]}; pos.index[char]#noRange => TEditSelectionOps.ShowGivenPositionRange[viewer, mySelectionToTEditSelectionOpsSelection[selection], pos.index[char].first, MAX[pos.index[char].first+3, pos.index[char].last], TRUE, FALSE]; pos.index[line]#noRange => LineNumberExtras.ToSelectionRange[viewer, [pos.index[line].first, IF pos.index[line].last=noIndex THEN pos.index[line].first ELSE pos.index[line].last], TRUE]; ENDCASE => NULL; RETURN [TRUE]}; RETURN [FALSE]}; FindViewer: PROC [fileName: Rope.ROPE, created: BasicTime.GMT, 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 v.class.flavor # $Text THEN RETURN [TRUE]; <> 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 created # BasicTime.nullGMT THEN { root: TiogaOps.Ref _ TiogaOps.ViewerDoc[v]; createVal: REF ANY _ TiogaOps.GetProp[root, $FileCreateDate]; IF createVal#NIL THEN { rgmt: REF BasicTime.GMT; TRUSTED {rgmt _ LOOPHOLE[createVal]}; --YUK! BasicTime.GMT is opaque IF rgmt^ # created THEN RETURN [TRUE]; } ELSE RETURN [TRUE]; }; viewer _ v; RETURN [FALSE]; }; }; viewer _ GetHint[fileName]; IF viewer # NIL AND NOT visit[viewer] THEN RETURN; viewer _ NIL; ViewerOps.EnumerateViewers[visit]; RETURN}; GetHint: PROC [name: ROPE] RETURNS [Viewer _ NIL] = { <> viewer: Viewer _ hintViewer; SELECT TRUE FROM viewer = NIL => {}; viewer.destroyed => {}; Rope.Equal[name, viewer.file, FALSE] => RETURN [viewer]; ENDCASE; RETURN [NIL]; }; SetHint: PROC [viewer: Viewer] = { hintViewer _ viewer }; hintViewer: Viewer _ NIL; systemDir: ROPE ~ PFS.RopeFromPath[PFS.GetWDir[]]; commandsDir: ROPE ~ SystemNames.LocalDir["Commands"]; pathPrefixes: LIST OF ROPE _ LIST[NIL, commandsDir, systemDir]; <> <<... then try the Commands subdirectory (stuff from Environment.df)>> <<... then try the default system directory (stuff from BootEssentials.df)>> FullFileName: PROC [shortPath: PATH, uniqueID: PFS.UniqueID _ PFS.nullUniqueID] RETURNS [fullPath: PATH _ NIL] = { <> wPath: PATH; IF shortPath.IsAbsolute THEN RETURN [shortPath]; FOR paths: LIST OF ROPE _ pathPrefixes, paths.rest UNTIL paths = NIL DO ENABLE PFS.Error => IF error.group # bug THEN LOOP; wPath _ PFS.PathFromRope[paths.first]; fullPath _ shortPath.Cat[wPath]; <> RETURN [PFS.FileInfo[name: fullPath, wantedUniqueID: uniqueID ! PFS.Error => IF error.group # bug THEN LOOP ].fullFName]; ENDLOOP; }; <> <> <> <<};>> END.