-- file: EditorFind.Mesa -- edited by Brotz, September 24, 1982 11:53 AM -- edited by Barth, December 15, 1980 4:18 PM -- edited by Schroeder, March 15, 1981 3:17 PM DIRECTORY Ascii USING [ControlA, ControlB], dsD: FROM "DisplayDefs" USING [CharProperty, GetCharBreakProp], Editor USING [CancelSourceSelection, CancelTargetSelection, DoTargetSelection, MakeCharIndexVisible, RefreshToPlaceCharOnLine, ResetBuffers, TurnOnDeliver], exD: FROM "ExceptionDefs" USING [ClearExceptionsRegion, DisplayException, DisplayExceptionLine, DisplayExceptionString, DisplayExceptionStringOnLine, findHintOne, findHintTwo, malformedSearchString, noBlankFound, noSubMade, oneSubMade, SysBug, textNotFound], inD: FROM "InteractorDefs" USING [CharIndex, CommandProcedure, ConfirmBrackets, IndicateCommandBusy, IndicateCommandFinished, maxBracketStringLength, MessageTextNbrPtr, SetCaretBlinking, StopBlinkingCaret, TextSelection, TextSelectionPtr], Inline USING [BITAND, BITOR], intCommon USING [actionPoint, cmTextNbr, commandType, composedMessageEdited, currentCommand, editorType, findBracketsHouse, findHouse, pendingDeleteSetByControl, substituteHouse, substituteNewBracketsHouse, substituteOldBracketsHouse, target], lmD: FROM "LaurelMenuDefs" USING [ChangeEditorMenu], ovD: FROM "OverviewDefs" USING [CharMask, LineBreakValue], prD: FROM "ProtectionDefs" USING [FindUnprotectedSubrange], Storage USING [Free, Node], String USING [AppendDecimal, AppendString, LowerCase, UpperCase], vmD: FROM "VirtualMgrDefs" USING [CharCache, CharIndex, ComposedMessage, ComposedMessagePtr, DeleteRangeInMessage, GetMessageChar, GetMessageSize, InsertRangeInMessage, InsertSubstringInMessage, MessageRange, PutMessageChar, StartMessageInsertion, StopMessageInsertion, VirtualMessagePtr]; EditorFind: PROGRAM IMPORTS dsD, Editor, exD, inD, intC: intCommon, Inline, lmD, prD, Storage, String, vmD EXPORTS Editor, inD = BEGIN OPEN Editor, inD; FCommand: PUBLIC PROCEDURE = BEGIN lmD.ChangeEditorMenu[find]; IndicateCommandBusy[intC.currentCommand _ intC.findHouse]; FindCommand[intC.findHouse, FALSE]; intC.currentCommand _ NIL; IndicateCommandFinished[intC.findHouse]; END; -- of FCommand -- FindCommand: PUBLIC CommandProcedure = -- Starting at the current selection, searches for text in following brackets. If -- found, selects the found text and normalizes it in the composed message -- region. BEGIN mnp: MessageTextNbrPtr = intC.cmTextNbr; target: TextSelectionPtr = @intC.target; found: BOOLEAN; at, atEnd: CharIndex; targetRange: vmD.MessageRange; IF ~mnp.haveMessage THEN RETURN; IF ~confirmed THEN BEGIN DisplayFindSyntaxHint[]; IF ~ConfirmBrackets[intC.findBracketsHouse, TRUE] THEN RETURN; END; [found, at, atEnd] _ FindOperation[intC.findBracketsHouse.text, target.end, vmD.GetMessageSize[mnp.message], mnp.message]; targetRange _ [at, atEnd, mnp.message]; IF found THEN BEGIN IF mnp.protectedFieldPtr # NIL THEN BEGIN prD.FindUnprotectedSubrange[pfp: mnp.protectedFieldPtr, rangePtr: @targetRange, fixStart: TRUE]; IF targetRange.end # atEnd THEN exD.DisplayExceptionString["Selection shortened to lie within unprotected region!"L]; END; CancelTargetSelection[]; CancelSourceSelection[mnp]; StopBlinkingCaret[]; target^ _ TextSelection [mnp, targetRange.start, targetRange.end, targetRange.start, 0, char, intC.editorType = modeless AND targetRange.start < targetRange.end]; intC.pendingDeleteSetByControl _ FALSE; DoTargetSelection[]; [] _ MakeCharIndexVisible[at, mnp]; IF intC.editorType = modeless THEN SetCaretBlinking[target.point, mnp]; END ELSE exD.DisplayException[exD.textNotFound]; END; -- of FindCommand -- DisplayFindSyntaxHint: PROCEDURE = BEGIN exD.DisplayExceptionLine[exD.findHintOne, 1]; exD.DisplayExceptionLine[exD.findHintTwo, 2]; END; -- of DisplayFindSyntaxHint -- SCommand: PUBLIC PROCEDURE = BEGIN lmD.ChangeEditorMenu[substitute]; IndicateCommandBusy[intC.currentCommand _ intC.substituteHouse]; SubstituteCommand[intC.substituteHouse, FALSE]; intC.currentCommand _ NIL; IndicateCommandFinished[intC.substituteHouse]; END; -- of SCommand -- SubstituteCommand: PUBLIC CommandProcedure = -- Within the bounds of the current selection, substitutes the text in the -- following brackets for each occurence of the text in the preceding brackets. BEGIN target: TextSelectionPtr = @intC.target; originalStart, start, end, at, atEnd, patternEnd: CharIndex; mnp: MessageTextNbrPtr _ target.mnp; composedMessage: vmD.ComposedMessagePtr _ vmD.ComposedMessage[mnp.message]; old: STRING _ intC.substituteOldBracketsHouse.text; new: STRING _ intC.substituteNewBracketsHouse.text; normalNew: STRING _ [maxBracketStringLength]; found: BOOLEAN; matchedLength, putLength: CARDINAL; count: CARDINAL _ 0; IF ~mnp.haveMessage THEN RETURN; IF ~confirmed THEN BEGIN IF ~ConfirmBrackets[intC.substituteNewBracketsHouse, TRUE] THEN RETURN; DisplayFindSyntaxHint[]; IF ~ConfirmBrackets[intC.substituteOldBracketsHouse, TRUE] THEN {exD.ClearExceptionsRegion[]; RETURN}; END; putLength _ MIN[new.length, old.length]; originalStart _ start _ target.start; end _ target.end; exD.ClearExceptionsRegion[]; FOR i: CARDINAL IN [0 .. new.length) DO normalNew[i] _ Inline.BITAND[new[i], ovD.CharMask]; ENDLOOP; DO [found, at, atEnd, patternEnd] _ FindOperation[old, start, end, composedMessage]; IF ~found THEN EXIT; IF count = 0 THEN BEGIN -- save selected range for undo. ResetBuffers[mnp]; vmD.InsertRangeInMessage[0, mnp.deletionBuffer, [start, end, composedMessage]]; intC.commandType _ replace; intC.actionPoint _ originalStart; END; count _ count + 1; matchedLength _ atEnd - at; end _ end + new.length - matchedLength; putLength _ MIN[new.length, matchedLength]; FOR i: CARDINAL IN [0 .. putLength) DO vmD.PutMessageChar[composedMessage, at + i, normalNew[i]]; ENDLOOP; SELECT new.length FROM < matchedLength => vmD.DeleteRangeInMessage [from: [start: at + putLength, end: atEnd, message: composedMessage]]; > matchedLength => BEGIN vmD.StartMessageInsertion[composedMessage, at + putLength]; [] _ vmD.InsertSubstringInMessage[composedMessage, normalNew, putLength, new.length - putLength]; vmD.StopMessageInsertion[composedMessage]; END; ENDCASE; start _ at + new.length + (patternEnd - atEnd); ENDLOOP; IF count > 0 THEN BEGIN vmD.InsertRangeInMessage[0, mnp.insertionBuffer, [target.start, end, composedMessage]]; IF intC.editorType = modeless THEN StopBlinkingCaret[]; IF count = 1 THEN exD.DisplayExceptionLine[exD.oneSubMade, 1] ELSE BEGIN countMessage: STRING _ [30]; String.AppendDecimal[countMessage, count]; String.AppendString[countMessage, " substitutions made."L]; exD.DisplayExceptionStringOnLine[countMessage, 1]; END; target.pendingDelete _ FALSE; target.end _ end; target.mode _ char; target.point _ end; composedMessage.formatStart _ composedMessage.formatEnd _ 0; RefreshToPlaceCharOnLine[originalStart, mnp.lines, mnp, TRUE]; IF intC.editorType = modeless THEN SetCaretBlinking[end, mnp]; intC.composedMessageEdited _ TRUE; TurnOnDeliver[]; END ELSE exD.DisplayException[exD.noSubMade]; END; -- of SubstituteCommand -- FindOperation: PUBLIC PROCEDURE [s: STRING, start, end: CharIndex, message: vmD.VirtualMessagePtr] RETURNS [found: BOOLEAN, at, atEnd, patternEnd: CharIndex] = -- Searches for a match to s within [start .. end) of mnp.message. If found, returns TRUE, -- with [at .. atEnd) the matched range, BEGIN anyStringPattern: CHARACTER = ovD.LineBreakValue; oneCharPattern: CHARACTER = ovD.LineBreakValue + 1; oneAlphaPattern: CHARACTER = ovD.LineBreakValue + 2; oneNonAlphaPattern: CHARACTER = ovD.LineBreakValue + 3; anyAlphaPattern: CHARACTER = ovD.LineBreakValue + 4; anyNonAlphaPattern: CHARACTER = ovD.LineBreakValue + 5; leftBracketPattern: CHARACTER = ovD.LineBreakValue + 6; rightBracketPattern: CHARACTER = ovD.LineBreakValue + 7; StackArray: TYPE = ARRAY [1 .. maxBracketStringLength] OF CARDINAL; textPosStack, patternPosStack: POINTER TO StackArray; patternString: STRING _ [maxBracketStringLength]; char, patternChar, firstPatChar1, firstPatChar2: CHARACTER _ 377C; charType: dsD.CharProperty; psIndex: CARDINAL _ 0; beginPos, endPos, patternPos, textPos, patternAnchor, textAnchor: CARDINAL; stackPtr: [0..maxBracketStringLength] _ 0; get: POINTER TO vmD.CharCache _ @message.get; leftBracketSeen, rightBracketSeen: BOOLEAN _ FALSE; firstPatternCharIsNormal: BOOLEAN _ FALSE; found _ FALSE; -- start of main line code for FindOperation -- -- first, normalize search string, processing special characters. IF s.length > maxBracketStringLength THEN exD.SysBug[]; BEGIN -- for EXITS. i: CARDINAL; FOR i _ 0, i + 1 UNTIL i >= s.length DO SELECT (char _ Inline.BITAND[s[i], ovD.CharMask]) FROM '' => BEGIN IF i + 1 < s.length THEN i _ i + 1 ELSE GO TO MalformedInput; patternString[psIndex] _ s[i]; IF psIndex = 0 THEN {firstPatternCharIsNormal _ TRUE; firstPatChar1 _ s[i]}; END; IN ['A .. 'Z], IN ['a .. 'z] => BEGIN patternString[psIndex] _ Inline.BITOR[char, ovD.LineBreakValue]; IF psIndex = 0 THEN BEGIN firstPatternCharIsNormal _ TRUE; firstPatChar1 _ String.UpperCase[char]; firstPatChar2 _ String.LowerCase[char]; END; END; '# => patternString[psIndex] _ oneCharPattern; '* => patternString[psIndex] _ anyStringPattern; '@ => patternString[psIndex] _ oneAlphaPattern; '! => patternString[psIndex] _ oneNonAlphaPattern; '& => patternString[psIndex] _ anyAlphaPattern; '~ => patternString[psIndex] _ anyNonAlphaPattern; '{ => patternString[psIndex] _ leftBracketPattern; '} => patternString[psIndex] _ rightBracketPattern; ENDCASE => BEGIN patternString[psIndex] _ char; IF psIndex = 0 THEN {firstPatternCharIsNormal _ TRUE; firstPatChar1 _ char}; END; IF char = '{ THEN {IF leftBracketSeen THEN GO TO MalformedInput ELSE leftBracketSeen _ TRUE}; IF char = '} THEN {IF rightBracketSeen THEN GO TO MalformedInput ELSE rightBracketSeen _ TRUE}; psIndex _ psIndex + 1; ENDLOOP; FOR psIndex _ psIndex, psIndex - 1 UNTIL psIndex = 0 DO SELECT patternString[psIndex - 1] FROM anyStringPattern, anyAlphaPattern, anyNonAlphaPattern, leftBracketPattern, rightBracketPattern => NULL; ENDCASE => EXIT; ENDLOOP; IF (patternString.length _ psIndex) = 0 THEN GO TO MalformedInput; EXITS MalformedInput => {exD.DisplayException[exD.malformedSearchString]; RETURN}; END; -- for EXITS -- Now search. BEGIN -- for EXITS textPosStack _ Storage.Node[SIZE[StackArray]]; patternPosStack _ Storage.Node[SIZE[StackArray]]; FOR at _ start, at + 1 UNTIL at = end DO IF firstPatternCharIsNormal THEN BEGIN char _ Inline.BITAND[ovD.CharMask, IF at IN [get.first .. get.free) THEN message.buffer.chars[at + get.floor - get.first] ELSE vmD.GetMessageChar[message, at]]; IF char # firstPatChar1 AND char # firstPatChar2 THEN LOOP; patternPos _ 1; textPos _ at + 1; END ELSE {patternPos _ 0; textPos _ at}; -- save where name began matching non-* seg of pattern patternAnchor _ 0; beginPos _ textAnchor _ at; DO IF patternPos >= patternString.length THEN BEGIN found _ TRUE; at _ beginPos; atEnd _ IF rightBracketSeen THEN endPos ELSE textPos; patternEnd _ textPos; GO TO Return; END; SELECT (patternChar _ patternString[patternPos]) FROM anyStringPattern => {patternAnchor _ patternPos _ patternPos + 1; textAnchor _ textPos; stackPtr _ 0}; leftBracketPattern => {beginPos _ textPos; patternPos _ patternPos +1}; rightBracketPattern => {endPos _ textPos; patternPos _ patternPos +1}; anyNonAlphaPattern, anyAlphaPattern => BEGIN stackPtr _ stackPtr + 1; textPosStack[stackPtr] _ textPos; patternPosStack[stackPtr] _ patternPos; patternPos _ patternPos + 1; END; ENDCASE => BEGIN IF textPos >= end THEN GO TO Return; IF patternChar = oneCharPattern THEN BEGIN IF patternPos # 0 AND patternPos = patternAnchor THEN -- first char(s) of * segment {patternAnchor _ patternAnchor + 1; textAnchor _ textPos + 1}; patternPos _ patternPos + 1; textPos _ textPos + 1; END ELSE BEGIN char _ Inline.BITAND [ovD.CharMask, IF textPos IN [get.first .. get.free) THEN message.buffer.chars[textPos + get.floor - get.first] ELSE vmD.GetMessageChar[message, textPos]]; charType _ dsD.GetCharBreakProp[char]; IF (patternChar = oneNonAlphaPattern AND charType # alphaNumeric) OR (patternChar = oneAlphaPattern AND charType = alphaNumeric) OR (patternChar = char) OR (patternChar > ovD.LineBreakValue AND ((patternChar_Inline.BITAND[ovD.CharMask, patternChar]) IN ['A..'Z] OR patternChar IN ['a .. 'z]) AND String.LowerCase[patternChar] = String.LowerCase[char]) THEN -- chars match -- {patternPos _ patternPos + 1; textPos _ textPos + 1} ELSE BEGIN WHILE stackPtr # 0 DO charType _ dsD.GetCharBreakProp[Inline.BITAND [ovD.CharMask, IF textPosStack[stackPtr] IN [get.first .. get.free) THEN message.buffer.chars[textPosStack[stackPtr] + get.floor - get.first] ELSE vmD.GetMessageChar[message, textPosStack[stackPtr]]]]; IF (patternString[patternPosStack[stackPtr]] = anyNonAlphaPattern AND charType # alphaNumeric) OR (patternString[patternPosStack[stackPtr]] = anyAlphaPattern AND charType = alphaNumeric) THEN BEGIN patternPos _ patternPosStack[stackPtr] + 1; textPos _ textPosStack[stackPtr] _ textPosStack[stackPtr] + 1; EXIT END; stackPtr _ stackPtr - 1; ENDLOOP; IF stackPtr = 0 THEN -- implicit AND patternAnchor # 0 (there was a *) BEGIN patternPos _ patternAnchor; textPos _ textAnchor _ textAnchor + 1; END; IF patternAnchor = 0 AND stackPtr = 0 THEN EXIT; END; END; END; ENDLOOP; ENDLOOP; GO TO Return; EXITS Return => {Storage.Free[textPosStack]; Storage.Free[patternPosStack]} END; -- of EXITS -- END; -- of FindOperation -- SelectNextBlank: PUBLIC PROCEDURE [mnp: MessageTextNbrPtr, forward: BOOLEAN _ TRUE] = -- Implements editor NEXT command. BEGIN target: TextSelectionPtr = @intC.target; nextStart, nextEnd: CharIndex; message: vmD.VirtualMessagePtr = mnp.message; messageLength: CharIndex = vmD.GetMessageSize[message]; targetRange: vmD.MessageRange; IF forward THEN BEGIN FOR nextStart _ IF target.pendingDelete THEN target.end ELSE target.point, nextStart + 1 UNTIL nextStart >= messageLength DO IF vmD.GetMessageChar[message, nextStart] = Ascii.ControlA THEN EXIT; REPEAT FINISHED => GO TO NoBlankFound; ENDLOOP; FOR nextEnd _ nextStart + 1, nextEnd + 1 UNTIL nextEnd >= messageLength DO IF vmD.GetMessageChar[message, nextEnd] = Ascii.ControlB THEN EXIT; REPEAT FINISHED => GO TO NoBlankFound; ENDLOOP; END ELSE BEGIN nextEnd _ IF target.pendingDelete THEN target.start ELSE target.point; UNTIL nextEnd = 0 DO nextEnd _ nextEnd - 1; IF vmD.GetMessageChar[message, nextEnd] = Ascii.ControlB THEN EXIT; REPEAT FINISHED => GO TO NoBlankFound; ENDLOOP; FOR nextStart _ nextEnd, nextStart - 1 UNTIL nextStart = 0 DO IF vmD.GetMessageChar[message, nextStart - 1] = Ascii.ControlA THEN {nextStart _ nextStart - 1; EXIT}; REPEAT FINISHED => GO TO NoBlankFound; ENDLOOP; END; targetRange _ [nextStart, nextEnd + 1, message]; IF mnp.protectedFieldPtr # NIL THEN BEGIN prD.FindUnprotectedSubrange[pfp: mnp.protectedFieldPtr, rangePtr: @targetRange, fixStart: FALSE]; IF targetRange.start # nextStart THEN exD.DisplayExceptionString ["Placeholders not edited properly. Next item would have crossed a protected field!"L]; END; CancelTargetSelection[]; target^ _ TextSelection [mnp, targetRange.start, targetRange.end, targetRange.start, 0, word, TRUE]; intC.pendingDeleteSetByControl _ FALSE; DoTargetSelection[]; [] _ MakeCharIndexVisible[nextStart, mnp]; EXITS NoBlankFound => exD.DisplayException[exD.noBlankFound]; END; -- of SelectNextBlank -- SelectEverything: PUBLIC PROCEDURE [mnp: MessageTextNbrPtr] = -- Implements editor E command. BEGIN IF mnp.protectedFieldPtr # NIL THEN BEGIN exD.DisplayExceptionString["Protected fields exist. Cannot select entire message!"L]; RETURN; END; CancelTargetSelection[]; intC.target _ TextSelection [mnp, 0, vmD.GetMessageSize[mnp.message], 0, 0, char, intC.editorType = modeless]; intC.pendingDeleteSetByControl _ FALSE; DoTargetSelection[]; END; -- of SelectEverything -- END. -- of EditorFind --z19932(635)\f1