@@ -52,6 +52,7 @@ interface
5252 { $IFDEF FPC} RichMemo, RichMemoHelpers,{ $ENDIF}
5353 // windows pipes wrapper
5454 FpcPipes,
55+ CustomLogger,
5556 // configuration manager
5657 Config;
5758
@@ -68,13 +69,16 @@ TFSIViewer = class
6869 _config: TConfiguration;
6970 _editor: TRichEdit;
7071 _pipedConsole: TPipeConsole;
72+ _logger: TCustomLogger;
7173 _editableAreaStartCoord: TPoint;
7274 _editableAreaStartPos: Integer;
7375 _defEditorWndProc: TWndMethod;
7476 _onSendText: TNotifyEvent;
7577 _onResultOutput: TNotifyEvent;
7678 _onErrorOutput: TNotifyEvent;
77- private
79+ procedure SetSelection ;
80+ procedure TrimSelection ;
81+ procedure WritePrompt (ClearFirst: Boolean = False);
7882
7983 // / <summary>
8084 // / Create an instance of FSI and also the pipes needed to interact with it.
@@ -138,6 +142,11 @@ TFSIViewer = class
138142 // / Check for non-empty text selection and enable "Copy" menu item if true
139143 // / </summary>
140144 procedure doOnContextMenuPopup (sender: TObject);
145+
146+ // / <summary>
147+ // / Fill the REPL prompt from the user's input history.
148+ // / </summary>
149+ procedure LoadFromHistory (Direction: TScrollDirection);
141150 public
142151 constructor Create;
143152 destructor Destroy; override;
@@ -175,6 +184,7 @@ TFSIViewer = class
175184 // / Get the instance of the editor that interfaces with FSI.
176185 // / </summary>
177186 property Editor: TRichEdit read _editor;
187+ property Logger: TCustomLogger read _logger;
178188
179189 // / <summary>
180190 // / Event raised when text is sent to FSI.
@@ -217,6 +227,8 @@ destructor TFSIViewer.Destroy;
217227
218228 if Assigned(_pipedConsole) then
219229 FreeAndNil(_pipedConsole);
230+ if Assigned(_logger) then
231+ FreeAndNil(_logger);
220232 if Assigned(_editor) then
221233 FreeAndNil(_editor);
222234 if Assigned(_config) then
@@ -293,6 +305,7 @@ procedure TFSIViewer.SendText(const selText: WideString; addDelimiter, appendEdi
293305 if (appendEditor) then
294306 AddToEditor(finalText);
295307 _pipedConsole.Write(finalText[1 ], SizeOf(Char) * Length(finalText));
308+ _logger.Add(Trim(finalText));
296309 end ;
297310
298311 if Assigned(_onSendText) then
@@ -340,6 +353,7 @@ procedure TFSIViewer.createFSI;
340353 _pipedConsole := TPipeConsole.Create(Nil );
341354 _pipedConsole.OnOutput := doOnPipeOutput;
342355 _pipedConsole.OnError := doOnPipeError;
356+ _logger := TCustomLogger.Create(IncludeTrailingPathDelimiter(Npp.GetPluginConfigDirectory) + ' fsi_history.txt' );
343357end ;
344358
345359procedure TFSIViewer.createContextMenu ;
@@ -462,15 +476,14 @@ procedure TFSIViewer.doOnPipeError(sender: TObject; stream: TStream);
462476 finally
463477 strStream.Free;
464478 end ;
465- AddToEditor(' > ' );
466- updateEditableAreaStart;
479+ WritePrompt;
467480end ;
468481
469482procedure TFSIViewer.doOnEditorKeyDown (sender: TObject; var Key: Word;
470483 Shift: TShiftState);
471484var
472485 newText: WideString;
473- IsCtrl, IsShift: Boolean;
486+ IsCtrl, IsShift, DelimiterNeeded : Boolean;
474487begin
475488 if (not isConsoleRunning) then
476489 Exit;
@@ -485,21 +498,33 @@ procedure TFSIViewer.doOnEditorKeyDown(sender: TObject; var Key: Word;
485498 end
486499 else if (isEditorCaretInAValidPos) then
487500 begin
501+ TrimSelection;
488502 if (Key = VK_RETURN) then
489503 begin
490- _editor.SelStart := _editableAreaStartPos;
491- _editor.SelLength := _editor.GetTextLen - _editableAreaStartPos;
504+ SetSelection;
492505 newText := { $IFDEF FPC} UTF8Decode{ $ENDIF} (_editor.SelText);
493506 updateEditableAreaStart;
494507 if WideSameText(Copy(newText, 0 , 2 ), ' > ' ) then
495508 newText := Copy(newText, 2 );
496509 newText := newText + #13 #10 ;
497- SendText(newText, false, false);
510+ if WideSameText(newText, #13 #10 ) then
511+ // send a simple RETURN for multi-line input or paging
512+ DelimiterNeeded := True
513+ else
514+ DelimiterNeeded := False;
515+ SendText(newText, DelimiterNeeded, false);
498516 end
499- else if (Key = VK_UP) then
517+ else if (Key in [ VK_UP, VK_DOWN] ) then
500518 begin
501- if (_editor.CaretPos.Y = _editableAreaStartCoord.Y) then
519+ if (_editor.CaretPos.Y >= _editableAreaStartCoord.Y) then
520+ begin
521+ if (_editor.SelLength = 0 ) then
522+ case Key of
523+ VK_UP: LoadFromHistory(sdBack);
524+ VK_DOWN: LoadFromHistory(sdForward);
525+ end ;
502526 Key := 0 ;
527+ end ;
503528 end
504529 else if (Key = VK_LEFT) then
505530 begin
@@ -536,7 +561,9 @@ procedure TFSIViewer.doOnEditorKeyDown(sender: TObject; var Key: Word;
536561 begin
537562 if Key in [VK_INSERT, VK_DELETE] then
538563 MessageBeep(MB_ICONWARNING);
539- end ;
564+ end // do not restrict caret movement via arrow keys
565+ else if (Key in [VK_LEFT, VK_UP, VK_RIGHT, VK_DOWN]) then
566+ Exit;
540567 Key := 0 ;
541568 end ;
542569end ;
@@ -548,18 +575,18 @@ procedure TFSIViewer.doOnEditorChange(Sender: TObject);
548575
549576procedure TFSIViewer.doOnEditorClearContextMenuClick (sender: TObject);
550577begin
551- _editor.Clear;
552- AddToEditor(' > ' );
553- updateEditableAreaStart;
578+ WritePrompt(True);
554579end ;
555580
556581procedure TFSIViewer.doOnEditorCutContextMenuClick (Sender: TObject);
557582begin
583+ TrimSelection;
558584 _editor.CutToClipboard;
559585end ;
560586
561587procedure TFSIViewer.doOnEditorCopyContextMenuClick (sender: TObject);
562588begin
589+ TrimSelection;
563590 _editor.CopyToClipboard;
564591 updateEditableAreaStart;
565592end ;
@@ -588,4 +615,32 @@ procedure TFSIViewer.doOnContextMenuPopup(sender: TObject);
588615
589616{ $ENDREGION}
590617
618+ procedure TFSIViewer.LoadFromHistory (Direction: TScrollDirection);
619+ begin
620+ SetSelection;
621+ _editor.ClearSelection;
622+ AddToEditor(_logger.Scroll(Direction));
623+ _editor.SelStart := _editor.GetTextLen;
624+ _editor.SelLength := 0 ;
625+ end ;
626+
627+ procedure TFSIViewer.WritePrompt (ClearFirst: Boolean);
628+ begin
629+ if ClearFirst then _editor.Clear;
630+ AddToEditor(' > ' );
631+ updateEditableAreaStart;
632+ end ;
633+
634+ procedure TFSIViewer.TrimSelection ;
635+ begin
636+ if SameStr(Copy(_editor.SelText, 0 , 2 ), ' > ' ) and (_editor.SelStart < _editableAreaStartPos) then
637+ SetSelection;
638+ end ;
639+
640+ procedure TFSIViewer.SetSelection ;
641+ begin
642+ _editor.SelStart := _editableAreaStartPos;
643+ _editor.SelLength := _editor.GetTextLen - _editableAreaStartPos;
644+ end ;
645+
591646end .
0 commit comments