{
 *****************************************************************************
  See the file COPYING.modifiedLGPL.txt, included in this distribution,
  for details about the license.
 *****************************************************************************

  Abstract:
    Defines interface to source editors.
}
unit SrcEditorIntf;

{$mode objfpc}{$H+}

interface

uses
  Classes,
  // LCL
  LCLType, Forms, Controls, Graphics, ComCtrls,
  // LazUtils
  Laz2_XMLCfg, LazStringUtils,
  // BuildIntf
  ProjectIntf,
  // IdeIntf
  IDECommands;
  
type
  TSourceMarklingType = (
    smtHint,    // something is unusual or awkward
    smtNote,    // something could be wrong or is not optimized
    smtWarning, // something is probably wrong or likely to fail
    smtError    // something is definitely wrong
    );

  { TSourceMarklingProducer }

  TSourceMarklingProducer = class(TComponent)
  public
    procedure InvalidateAllFiles;
    procedure InvalidateFile(aFilename: string);
    function GetMarklings(aFilename: string;
                          out FreeList, FreeMarklings: boolean): TFPList; virtual; abstract;
  end;

  { TSourceMarkling }

  TSourceMarkling = class
  private
    FColumn: integer;
    FProducer: TSourceMarklingProducer;
    FId: integer;
    FLine: integer;
    fType: TSourceMarklingType;
  public
    constructor Create(aProducer: TSourceMarklingProducer;
                    TheID, aLine, aColumn: integer; aType: TSourceMarklingType);
    property Producer: TSourceMarklingProducer read FProducer;
    property Id: integer read FId;
    property Line: integer read FLine;
    property Column: integer read FColumn;
    property TheType: TSourceMarklingType read fType;
  end;

type
  TSrcEditSearchOption = (
    sesoMatchCase,
    sesoWholeWord,
    sesoBackwards,
    sesoEntireScope,
    sesoSelectedOnly,
    sesoReplace,
    sesoReplaceAll,
    sesoPrompt,
    sesoRegExpr,
    sesoMultiLine
    );
  TSrcEditSearchOptions = set of TSrcEditSearchOption;

  TSrcEditReplaceAction = (seraCancel, seraSkip, seraReplace, seraReplaceAll);

  TSrcEditProjectUpdateNeeded = (sepuNewShared, sepuChangedHighlighter);
  TSrcEditProjectUpdatesNeeded = set of TSrcEditProjectUpdateNeeded;

  { TSourceEditorInterface }

  TSourceEditorInterface = class
  protected
    function GetBlockBegin: TPoint; virtual; abstract;
    function GetBlockEnd: TPoint; virtual; abstract;
    function GetCodeToolsBuffer: TObject; virtual; abstract;
    function GetCursorScreenXY: TPoint; virtual; abstract;
    function GetCursorTextXY: TPoint; virtual; abstract;
    function GetEditorControl: TWinControl; virtual; abstract;
    function GetFileName: string; virtual; abstract;
    function GetLines: TStrings; virtual; abstract;
    function GetLineText: string; virtual; abstract;
    function GetLinesInWindow: Integer; virtual; abstract;
    function GetModified: Boolean; virtual; abstract;
    function GetPageCaption: string; virtual; abstract;
    function GetPageName: string; virtual; abstract;
    function GetReadOnly: Boolean; virtual; abstract;
    function GetSelection: string; virtual; abstract;
    function GetSelEnd: Integer; virtual; abstract;
    function GetSelStart: Integer; virtual; abstract;
    function GetSourceText: string; virtual; abstract;
    function GetTopLine: Integer; virtual; abstract;
    procedure SetBlockBegin(const AValue: TPoint); virtual; abstract;
    procedure SetBlockEnd(const AValue: TPoint); virtual; abstract;
    procedure SetCursorScreenXY(const AValue: TPoint); virtual; abstract;
    procedure SetCursorTextXY(const AValue: TPoint); virtual; abstract;
    procedure SetLines(const AValue: TStrings); virtual; abstract;
    procedure SetLineText(const AValue: string); virtual; abstract;
    procedure SetModified(const NewValue: Boolean); virtual; abstract;
    procedure SetReadOnly(const AValue: Boolean); virtual; abstract;
    procedure SetSelection(const AValue: string); virtual; abstract;
    procedure SetSelEnd(const AValue: Integer); virtual; abstract;
    procedure SetSelStart(const AValue: Integer); virtual; abstract;
    procedure SetSourceText(const AValue: string); virtual; abstract;
    procedure SetTopLine(const AValue: Integer); virtual; abstract;
  public
    // selections
    function SelectionAvailable: boolean; virtual; abstract;
    function GetText(OnlySelection: boolean): string; virtual; abstract;
    procedure SelectText(LineNum, CharStart, LineNum2, CharEnd: Integer);
    procedure SelectText(const StartPos, EndPos: TPoint); virtual; abstract;
    procedure InsertLine(StartLine: Integer; const NewText: String; aKeepMarks: Boolean = False); virtual; abstract;
    procedure ReplaceLines(StartLine, EndLine: integer; const NewText: string; aKeepMarks: Boolean = False); virtual; abstract;
    procedure ReplaceText(const StartPos, EndPos: TPoint; const NewText: string);
    procedure AskReplace(Sender: TObject; const ASearch, AReplace: string;
                        Line, Column: integer;
                        out Action: TSrcEditReplaceAction); virtual; abstract;
    procedure CopyToClipboard; virtual; abstract;
    procedure CutToClipboard; virtual; abstract;
    function GetBookMark(BookMark: Integer; out X, Y: Integer): Boolean; virtual; abstract;
    procedure SetBookMark(BookMark: Integer; X, Y: Integer); virtual; abstract;

    // screen and text position mapping
    function LineCount: Integer; virtual; abstract;
    function TextToScreenPosition(const Position: TPoint): TPoint; virtual; abstract; // aka byte to glyph
    function ScreenToTextPosition(const Position: TPoint): TPoint; virtual; abstract; // aka glyph to byte

    // characters and pixels
    function WidthInChars: Integer; virtual; abstract;
    function HeightInLines: Integer; virtual; abstract;
    function CharWidth: integer; virtual; abstract;
    function CursorInPixel: TPoint; virtual; abstract;
    function ScreenToPixelPosition(const Position: TPoint): TPoint; virtual; abstract;// ScreenXY to pixel in EditorControl.
      // To get the desktop pixel coords use:
      //   with SourceEditorWindow.ActiveEditor do
      //     DesktopXY:=EditorControl.ClientToScreen(ScreenToPixelPosition(ScreenXY));

    // update
    procedure BeginUndoBlock{$IFDEF SynUndoDebugBeginEnd}(ACaller: String = ''){$ENDIF}; virtual; abstract;
    procedure EndUndoBlock{$IFDEF SynUndoDebugBeginEnd}(ACaller: String = ''){$ENDIF}; virtual; abstract;
    procedure BeginUpdate; virtual; abstract; // block painting
    procedure EndUpdate; virtual; abstract;
    procedure IncreaseIgnoreCodeBufferLock; virtual; abstract;
    procedure DecreaseIgnoreCodeBufferLock; virtual; abstract;
    procedure UpdateCodeBuffer; virtual; abstract;// copy the source from EditorComponent to the codetools
    function NeedsUpdateCodeBuffer: boolean; virtual; abstract;// needs UpdateCodeBuffer

    // search and replace
    function SearchReplace(const ASearch, AReplace: string;
                           SearchOptions: TSrcEditSearchOptions): integer; virtual; abstract;

    // context
    function GetProjectFile: TLazProjectFile; virtual; abstract;
    procedure UpdateProjectFile(AnUpdates: TSrcEditProjectUpdatesNeeded = []); virtual; abstract;
    function GetDesigner(LoadForm: boolean): TIDesigner; virtual; abstract;

    // editor commands
    procedure DoEditorExecuteCommand(EditorCommand: word); virtual; abstract;

    // move/copy to another SourceEditorWindow
    procedure MoveToWindow(AWindowIndex: Integer); virtual; abstract;
    procedure CopyToWindow(AWindowIndex: Integer); virtual; abstract;

    //
    function  DebugToSourceLine(aLinePos: Integer): Integer; virtual; abstract;
  public
    property BlockBegin: TPoint read GetBlockBegin write SetBlockBegin;
    property BlockEnd: TPoint read GetBlockEnd write SetBlockEnd;
    property CodeToolsBuffer: TObject read GetCodeToolsBuffer;
    property CursorScreenXY: TPoint read GetCursorScreenXY write SetCursorScreenXY; // glyph/character,line coord, both 1-based
    property CursorTextXY: TPoint read GetCursorTextXY write SetCursorTextXY; // byte,line coord, both 1-based
    property EditorControl: TWinControl read GetEditorControl;// normally TSynEdit
    property FileName: string read GetFileName;
    property Lines: TStrings read GetLines write SetLines;// the whole file
    property CurrentLineText: string read GetLineText write SetLineText;// source of current line
    property PageCaption: string read GetPageCaption;// the beatified unit name with modifiers ('*', '#') as it is shown in tab caption
    property PageName: string read GetPageName;// the beatified unit name
    property ReadOnly: Boolean read GetReadOnly write SetReadOnly;
    property Selection: string read GetSelection write SetSelection; // setting selection is undoable
    property SelEnd: Integer read GetSelEnd write SetSelEnd;
    property SelStart: Integer read GetSelStart write SetSelStart;
    property SourceText: string read GetSourceText write SetSourceText;// the whole file, raw format, writing clears undo list
    property TopLine: Integer read GetTopLine write SetTopLine;// first visible line
    property LinesInWindow: Integer read GetLinesInWindow;
    property Modified: Boolean read GetModified write SetModified;
  end;

  { TSourceEditorCompletionPlugin }

  TSourceEditorCompletionPlugin = class(TComponent)
  public
    procedure Init(SrcEdit: TSourceEditorInterface; JumpToError: boolean;
                   var Handled, Abort: boolean; var Prefix: string;
                   var BoxX, BoxY: integer); virtual; abstract; // check if this plugin is responsible
    function Collect(List: TStrings): boolean; virtual; abstract; // collect values
    procedure Cancel; virtual; abstract; // completion was cancelled
    procedure Complete(var Value: string; SourceValue: string;
                       var SourceStart, SourceEnd: TPoint;
                       KeyChar: TUTF8Char; Shift: TShiftState); virtual; abstract; // execute completion

    procedure IndexChanged(Position: integer); virtual; abstract;
    procedure PrefixChanged(const NewPrefix: string; var NewIndex: integer;
                            var s: TStrings); virtual; abstract;
    procedure CompletePrefix(var Prefix: string); virtual; abstract;

    function HasCustomPaint: boolean; virtual;
    procedure PaintItem(const {%H-}AKey: string; {%H-}ACanvas: TCanvas;
                 {%H-}X, {%H-}Y: integer; {%H-}ItemSelected: boolean; {%H-}Index: integer); virtual;
    function MeasureItem(const {%H-}AKey: string; {%H-}ACanvas: TCanvas;
                         {%H-}ItemSelected: boolean; {%H-}Index: integer): TPoint; virtual;
  end;

  TSourceEditorStatusPanelInterface = class;

  TDrawSourceEditPanelEvent = procedure(APanel: TSourceEditorStatusPanelInterface;
    ACanvas: TCanvas; const Rect: TRect) of object;

  { TSourceEditorStatusPanelInterface }

  TSourceEditorStatusPanelInterface = class
  private
    FOnClick: TNotifyEvent;
    FOnContextPopup: TContextPopupEvent;
    FOnDoubleClick: TNotifyEvent;
    FOnDrawPanel: TDrawSourceEditPanelEvent;
    FOwner: TClass;
    FTag: PtrUInt;
    FOnDestroy: TNotifyEvent;
    FOnResize: TNotifyEvent;

  protected
    function GetAlignment: TAlignment; virtual; abstract;
    function GetBevel: TStatusPanelBevel; virtual; abstract;
    function GetBidiMode: TBiDiMode; virtual; abstract;
    function GetHeight: integer; virtual; abstract;
    function GetScreenBounds: TRect;virtual; abstract;
    function GetText: string; virtual; abstract;
    function GetVisible: boolean; virtual; abstract;
    function GetWidth: integer; virtual; abstract;
    procedure SetAlignment(AValue: TAlignment); virtual; abstract;
    procedure SetBevel(AValue: TStatusPanelBevel); virtual; abstract;
    procedure SetBidiMode(AValue: TBiDiMode); virtual; abstract;
    procedure SetText(AValue: string); virtual; abstract;
    procedure SetOnDrawPanel(AValue: TDrawSourceEditPanelEvent); virtual;
    procedure SetVisible(AValue: boolean); virtual; abstract;
  public
    constructor Create(AnOwner: TClass; ATag: PtrUInt);

    property Owner: TClass read FOwner;
    property Tag: PtrUInt read FTag;

    property Alignment: TAlignment read GetAlignment write SetAlignment;
    property Bevel: TStatusPanelBevel read GetBevel write SetBevel;
    property BidiMode: TBiDiMode read GetBidiMode write SetBidiMode;
    property Text: string read GetText write SetText;
    property Visible: boolean read GetVisible write SetVisible;

    function RequestWidth(AWidth: Integer): Integer; virtual; abstract;
    property Height: integer read GetHeight;
    property Width: integer read GetWidth;
    property ScreenBounds: TRect read GetScreenBounds;

    property OnDestroy: TNotifyEvent read FOnDestroy write FOnDestroy;
    property OnResize: TNotifyEvent read FOnResize write FOnResize;
    property OnClick: TNotifyEvent read FOnClick write FOnClick;
    property OnDoubleClick: TNotifyEvent read FOnDoubleClick write FOnDoubleClick;
    property OnContextPopup: TContextPopupEvent read FOnContextPopup write FOnContextPopup;
    property OnDrawPanel: TDrawSourceEditPanelEvent read FOnDrawPanel write SetOnDrawPanel; // change draw mode
  end;

  { TSourceEditorWindowInterface }

  TSourceEditorWindowInterface = class(TForm)
  protected
    function GetActiveCompletionPlugin: TSourceEditorCompletionPlugin; virtual; abstract;
    function GetActiveEditor: TSourceEditorInterface; virtual; abstract;
    function GetBaseCaption: String; virtual; abstract;
    function GetCompletionPlugins(Index: integer): TSourceEditorCompletionPlugin; virtual; abstract;
    function GetItems(Index: integer): TSourceEditorInterface; virtual; abstract;
    procedure SetActiveEditor(const AValue: TSourceEditorInterface); virtual; abstract;
    procedure SetBaseCaption(AValue: String); virtual; abstract;
    function GetWindowID: Integer; virtual; abstract;

    function GetStatusPanel(AnOwner: TClass; AnIdx: integer): TSourceEditorStatusPanelInterface; virtual; abstract;
    function GetStatusPanelTagged(AnOwner: TClass; ATag: PtrUInt; AnIdx: integer): TSourceEditorStatusPanelInterface; virtual; abstract;
  public
    procedure IncUpdateLock; virtual; abstract;
    procedure DecUpdateLock; virtual; abstract;
    function IndexOfEditorInShareWith(AnOtherEditor: TSourceEditorInterface): Integer; virtual; abstract;
    function SourceEditorIntfWithFilename(const Filename: string): TSourceEditorInterface; virtual; abstract;
    property ActiveEditor: TSourceEditorInterface read GetActiveEditor write SetActiveEditor;
    function Count: integer; virtual; abstract;
    property Items[Index: integer]: TSourceEditorInterface read GetItems; default;
    property ActiveCompletionPlugin: TSourceEditorCompletionPlugin read GetActiveCompletionPlugin;
    // The number in the Form.Caption minus 1 (0-based), if multiple Win are open
    property WindowID: Integer read GetWindowID;
    // Editor Page Caption update
    procedure AddUpdateEditorPageCaptionHandler(AEvent: TNotifyEvent; const AsLast: Boolean = True); virtual; abstract;
    procedure RemoveUpdateEditorPageCaptionHandler(AEvent: TNotifyEvent); virtual; abstract;
    property BaseCaption: String read GetBaseCaption write SetBaseCaption;

    function AddStatusPanel(AnOwner: TClass; ATag: PtrUInt = 0): TSourceEditorStatusPanelInterface; virtual; abstract;
    function StatusPanelCount(AnOwner: TClass): integer; virtual; abstract;
    function StatusPanelTaggedCount(AnOwner: TClass; ATag: PtrUInt): integer; virtual; abstract;
    property StatusPanel[AnOwner: TClass; AnIdx: integer]: TSourceEditorStatusPanelInterface read GetStatusPanel;
    property StatusPanelTagged[AnOwner: TClass; ATag: PtrUInt; AnIdx: integer]: TSourceEditorStatusPanelInterface read GetStatusPanelTagged;
  end;

  TSemChangeReason = (
    semWindowCreate,    // Called after creation of a Window
    semWindowDestroy,   // Called after removal of a Window
    semWindowActivate,  // Window is now ActiveSourceWindow (does not vave to be focused)
    semWindowFocused,   // The window became the active win of the application
    semWindowShow,      // Called after a Window is shown
    semWindowHide,      // Called after a Window is hidden
    semEditorCreate,    // Called after a new editor was created and added to list
    semEditorDestroy,   // Called when an Editor is destroyed / after it is removed fron the list of editors
    semEditorOptsChanged, // Called when EditorOptions changed / called with TIDEEditorOptions as Sender
    semEditorActivate,  // Editor is ActiveEditor
    semEditorStatus,    // any status change of the editor (Caret, Selection, topline, ...)
    semEditorMouseDown,
    semEditorMouseUp
  );

  TSemSelectionMode = (
    semsmNormal,
    semsmLine,
    semsmColumn,
    semsmCurrent);
  TSemCopyPasteAction = (
    semcaContinue,  // normal paste with specials like folding
    semcaPlainText, // paste as normal text, ignore folding and other specials
    semcaAbort      // cancel, use this if you handled the paste yourself
    );
  // ToDo: use the right clipboard
  TSemCopyPasteEvent = procedure(Sender: TSourceEditorInterface;
    var AText: String; var AMode: TSemSelectionMode; ALogStartPos: TPoint;
    var AnAction: TSemCopyPasteAction) of object;

  TSemBeautyFlag = (
    sembfNotBreakDots
    );
  TSemBeautyFlags = set of TSemBeautyFlag;

  { TSourceEditorManagerInterface }

  TSourceEditorManagerInterface = class(TComponent)
  protected
    function GetActiveSourceWindow: TSourceEditorWindowInterface; virtual; abstract;
    procedure SetActiveSourceWindow(const AValue: TSourceEditorWindowInterface);
                                    virtual; abstract;
    function GetSourceWindows(Index: integer): TSourceEditorWindowInterface;
                              virtual; abstract;
    function GetActiveEditor: TSourceEditorInterface; virtual; abstract;
    procedure SetActiveEditor(const AValue: TSourceEditorInterface);
                              virtual; abstract;
    function GetSourceEditors(Index: integer): TSourceEditorInterface;
                              virtual; abstract;
    function GetUniqueSourceEditors(Index: integer): TSourceEditorInterface;
                                    virtual; abstract;
    function GetMarklingProducers(Index: integer): TSourceMarklingProducer;
                                  virtual; abstract;
    function GetShowTabs: Boolean; virtual; abstract;
    procedure SetShowTabs(const AShowTabs: Boolean); virtual; abstract;
  public
    // List of SourceEditorWindows
    function SourceWindowWithEditor(const AEditor: TSourceEditorInterface): TSourceEditorWindowInterface;
             virtual; abstract;
    function  IndexOfSourceWindowWithID(const AnID: Integer): Integer; virtual; abstract;
    procedure ShowActiveWindowOnTop(Focus: Boolean = False); virtual; abstract;
    function SourceWindowCount: integer; virtual; abstract;
    property SourceWindows[Index: integer]: TSourceEditorWindowInterface
             read GetSourceWindows;
    property ActiveSourceWindow: TSourceEditorWindowInterface
             read GetActiveSourceWindow write SetActiveSourceWindow;
    // List of SourceEditors (accross all Windows)
    function SourceEditorIntfWithFilename(const Filename: string): TSourceEditorInterface;
             virtual; abstract; // first hit, there might be more
    function SourceEditorCount: integer; virtual; abstract;
    property SourceEditors[Index: integer]: TSourceEditorInterface
             read GetSourceEditors;
    property ActiveEditor: TSourceEditorInterface
             read GetActiveEditor  write SetActiveEditor;
    // List of unique SourceEditors (excluding DualView)
    function UniqueSourceEditorCount: integer; virtual; abstract;
    property UniqueSourceEditors[Index: integer]: TSourceEditorInterface
             read GetUniqueSourceEditors;
    // Editor Preferences
    function GetEditorControlSettings(EditControl: TControl): boolean; virtual; abstract;
    function GetHighlighterSettings(Highlighter: TObject): boolean; virtual; abstract;
    // Messages
    procedure ClearErrorLines; virtual; abstract;
    // General source functions
    function Beautify(const Src: string; const Flags: TSemBeautyFlags = []): string; virtual; abstract;
  protected
    // Completion Plugins
    function  GetActiveCompletionPlugin: TSourceEditorCompletionPlugin; virtual; abstract;
    function  GetCompletionBoxPosition: integer; virtual; abstract;
    function  GetCompletionPlugins(Index: integer): TSourceEditorCompletionPlugin; virtual; abstract;
    function  GetDefaultSynCompletionForm: TCustomForm; virtual; abstract;
    function  GetSynCompletionLinesInWindow: integer; virtual; abstract;
    procedure SetSynCompletionLinesInWindow(LineCnt: integer); virtual; abstract;
  public
    // Completion Plugins
    function CompletionPluginCount: integer; virtual; abstract;
    property CompletionPlugins[Index: integer]: TSourceEditorCompletionPlugin
                 read GetCompletionPlugins;
    property DefaultSynCompletionForm: TCustomForm read GetDefaultSynCompletionForm;
    property SynCompletionLinesInWindow: integer read GetSynCompletionLinesInWindow
                                                write SetSynCompletionLinesInWindow;
    procedure DeactivateCompletionForm; virtual; abstract;
    property ActiveCompletionPlugin: TSourceEditorCompletionPlugin read GetActiveCompletionPlugin;
    property CompletionBoxPosition: integer read GetCompletionBoxPosition;
    procedure RegisterCompletionPlugin(Plugin: TSourceEditorCompletionPlugin); virtual; abstract;
    procedure UnregisterCompletionPlugin(Plugin: TSourceEditorCompletionPlugin); virtual; abstract;
  public
    procedure RegisterChangeEvent(AReason: TsemChangeReason; AHandler: TNotifyEvent); virtual; abstract;
    procedure UnRegisterChangeEvent(AReason: TsemChangeReason; AHandler: TNotifyEvent); virtual; abstract;
    procedure RegisterCopyPasteEvent(AHandler: TSemCopyPasteEvent); virtual; abstract;
    procedure UnRegisterCopyPasteEvent(AHandler: TSemCopyPasteEvent); virtual; abstract;
  public
    // source marklings
    function MarklingProducerCount: integer; virtual; abstract;
    property MarklingProducers[Index: integer]: TSourceMarklingProducer read GetMarklingProducers;
    procedure RegisterMarklingProducer(aProducer: TSourceMarklingProducer); virtual; abstract;
    procedure UnregisterMarklingProducer(aProducer: TSourceMarklingProducer); virtual; abstract;
    procedure InvalidateMarklingsOfAllFiles(aProducer: TSourceMarklingProducer); virtual; abstract;
    procedure InvalidateMarklings(aProducer: TSourceMarklingProducer; aFilename: string); virtual; abstract;
    property ShowTabs: Boolean read GetShowTabs write SetShowTabs;
  end;


var
  SourceEditorManagerIntf: TSourceEditorManagerInterface=nil; // set by the IDE

type
  TEditorMacroState = (emStopped, emRecording, emPlaying, emRecPaused); // msPaused = paused recording
  TEditorMacro = class;

  { TEditorMacroKeyBinding }

  TEditorMacroKeyBinding = class
  protected
    FOwner: TEditorMacro;
    function GetIdeCmd: TIDECommand; virtual;
  public
    constructor Create(AOwner: TEditorMacro); virtual;
    procedure WriteToXmlConf(AConf: TXMLConfig; const APath: String); virtual; abstract;
    procedure ReadFromXmlConf(AConf: TXMLConfig; const APath: String); virtual; abstract;

    procedure MacroNameChanged; virtual; // must be called by sub-class of TEditorMacro
    property  IdeCmd: TIDECommand read GetIdeCmd; // TKeyCommandRelation
    function  ShortCutAsText: String; virtual;
  end;

  TEditorMacroKeyBindingClass = class of TEditorMacroKeyBinding;

  { TEditorMacro
    "aEditor: TWinControl" must be TCustomSynEdit

  }

  TEditorMacro = class
  private
    FOnChange: TNotifyEvent;
    FOnStateChange: TNotifyEvent;
    procedure SetMacroNameFull(AValue: String);
    procedure SetOnChange(AValue: TNotifyEvent);
  protected
    procedure DoChanged;
    procedure DoStateChanged;

    // (Un)setActivated: Must be called, whenever the state changes
    procedure SetActivated;
    procedure UnsetActivated;
    function  IsActivated: Boolean;
    procedure CheckStateAndActivated;

    function  GetMacroName: String; virtual; abstract;
    procedure SetMacroName(AValue: string); virtual; abstract;
    function  GetState: TEditorMacroState; virtual; abstract;
    function GetErrorMsg: String; virtual;
    function GetDefaultKeyBinding: TEditorMacroKeyBinding;
    function GetKeyBinding: TEditorMacroKeyBinding; virtual; abstract;

    procedure DoRecordMacro(aEditor: TWinControl); virtual; abstract;
    procedure DoPlaybackMacro(aEditor: TWinControl); virtual; abstract;
    procedure DoStop; virtual; abstract;
    procedure DoPause; virtual; abstract;
    procedure DoResume; virtual; abstract;
  public
    constructor Create(aOwner: TComponent); virtual; abstract;
    procedure AssignEventsFrom(AMacroRecorder: TEditorMacro); virtual; abstract;
    procedure WriteToXmlConf(AConf: TXMLConfig; const APath: String); virtual; abstract;
    procedure ReadFromXmlConf(AConf: TXMLConfig; const APath: String); virtual; abstract;
    function  GetAsSource: String; virtual; abstract;
    procedure SetFromSource(const AText: String); virtual; abstract;

    procedure RecordMacro(aEditor: TWinControl);
    procedure PlaybackMacro(aEditor: TWinControl);
    procedure Stop;
    procedure Pause;
    procedure Resume;

    procedure Clear; virtual; abstract;
    function  IsEmpty: Boolean; virtual; abstract;
    function  IsInvalid: Boolean; virtual; abstract;
    function  IsRecording(AnEditor: TWinControl): Boolean; virtual; abstract;

    property  MacroName: String read GetMacroName write SetMacroNameFull;
    property  State: TEditorMacroState read GetState;
    property  ErrorMsg: String read GetErrorMsg;
    property  OnStateChange: TNotifyEvent read FOnStateChange write FOnStateChange;
    property  OnChange: TNotifyEvent read FOnChange write FOnChange;            // Name, body
    property  KeyBinding: TEditorMacroKeyBinding read GetKeyBinding;
  end;

  TEditorMacroClass = class of TEditorMacro;

var
  // EditorMacroForRecording:
  // Used to record new Macros. On Completion it will be assigned to a player-macro
  EditorMacroForRecording: TEditorMacro = nil; // set by SourceEditor
  // ActiveEditorMacro:
  // Will be set whenever a macro is playing/recording. Ensures only one Macro is active
  ActiveEditorMacro: TEditorMacro = nil;
  DefaultBindingClass: TEditorMacroKeyBindingClass = nil;
  EditorMacroPlayerClass: TEditorMacroClass = nil;

function GetMacroListViewerWarningText: String;
procedure SetMacroListViewerWarningText(AValue: String);
function GetMacroListViewerWarningChanged: TNotifyProcedure;
procedure SetMacroListViewerWarningChanged(AValue: TNotifyProcedure);

property MacroListViewerWarningText: string read GetMacroListViewerWarningText write SetMacroListViewerWarningText;
property MacroListViewerWarningChanged: TNotifyProcedure read GetMacroListViewerWarningChanged write SetMacroListViewerWarningChanged;


type
  { TIDEInteractiveStringValue }

  TIDEInteractiveStringValue = class(TPersistent)
  private
    FValue: string;
  published
    property Value: string read FValue write FValue;
  end;

  { TIDETemplateParser }

  TIDETemplateParser = class
  protected
    function GetSrcPosition: Integer; virtual; abstract;
    function GetDestPosition: Integer; virtual; abstract;
    function GetDestPosX: Integer; virtual; abstract;
    function GetDestPosY: Integer; virtual; abstract;
    function GetSrcTemplate: String; virtual; abstract;
    function GetDestTemplate: String; virtual; abstract;
  public
    property SrcTemplate: String read GetSrcTemplate;
    property DestTemplate: String read GetDestTemplate;
    property SrcPosition: Integer read GetSrcPosition;
    property DestPosition: Integer read GetDestPosition;
    property DestPosX: Integer read GetDestPosX; // logical, byte position
    property DestPosY: Integer read GetDestPosY;
  end;

  { TIDECodeMacro - templates for sources, invoked by Ctrl-j }

  TIDECodeMacroGetValueProc = function(const Parameter: string;
                      InteractiveValue: TPersistent;
                      SrcEdit: TSourceEditorInterface;
                      var Value, ErrorMsg: string): boolean;
  TIDECodeMacroGetValueMethod = function(const Parameter: string;
                      InteractiveValue: TPersistent;
                      SrcEdit: TSourceEditorInterface;
                      var Value, ErrorMsg: string): boolean of object;

  TIDECodeMacroGetValueExProc = function(const Parameter: string;
                      InteractiveValue: TPersistent;
                      SrcEdit: TSourceEditorInterface;
                      var Value, ErrorMsg: string;
                      TemplateParser: TIDETemplateParser): boolean;
  TIDECodeMacroGetValueExMethod = function(const Parameter: string;
                      InteractiveValue: TPersistent;
                      SrcEdit: TSourceEditorInterface;
                      var Value, ErrorMsg: string;
                      TemplateParser: TIDETemplateParser): boolean of object;

  TIDECodeMacro = class
  private
    FInteractive: boolean;
    FInteractiveValueClass: TPersistentClass;
    FLongDescription: string;
    FName: string;
    FOnGetValueMethod: TIDECodeMacroGetValueMethod;
    FOnGetValueProc: TIDECodeMacroGetValueProc;
    FOnGetValueExMethod: TIDECodeMacroGetValueExMethod;
    FOnGetValueExProc: TIDECodeMacroGetValueExProc;
    FShortDescription: string;
  protected
    procedure Init; virtual;
  public
    constructor Create(const TheName: string);
    property Name: string read FName;
    property ShortDescription: string read FShortDescription write FShortDescription;
    property LongDescription: string read FLongDescription write FLongDescription;
    property OnGetValueProc: TIDECodeMacroGetValueProc read FOnGetValueProc
                                                       write FOnGetValueProc;
    property OnGetValueMethod: TIDECodeMacroGetValueMethod read FOnGetValueMethod
                                                       write FOnGetValueMethod;
    property OnGetValueExProc: TIDECodeMacroGetValueExProc read FOnGetValueExProc
                                                       write FOnGetValueExProc;
    property OnGetValueExMethod: TIDECodeMacroGetValueExMethod read FOnGetValueExMethod
                                                       write FOnGetValueExMethod;
    function GetValue(const Parameter: string; InteractiveValue: TPersistent;
                      SrcEdit: TSourceEditorInterface; out Value, ErrorMsg: string;
                      TemplateParser: TIDETemplateParser = nil): boolean; virtual;
    property Interactive: boolean read FInteractive write FInteractive;
    property InteractiveValueClass: TPersistentClass read FInteractiveValueClass
                                                   write FInteractiveValueClass;
  end;


  { TIDECodeMacros }

  TIDECodeMacros = class
  protected
    function GetItems(Index: integer): TIDECodeMacro; virtual; abstract;
  public
    property Items[Index: integer]: TIDECodeMacro read GetItems; default;
    function Count: integer; virtual; abstract;
    function Add(Macro: TIDECodeMacro): integer; virtual; abstract;
    function FindByName(const AName: string): TIDECodeMacro; virtual; abstract;
    function CreateUniqueName(const AName: string): string; virtual; abstract;
  end;

var
  IDECodeMacros: TIDECodeMacros = nil; // set by the IDE

function RegisterCodeMacro(const Name: string;
  const ShortDescription, LongDescription: string;
  OnGetValueProc: TIDECodeMacroGetValueProc;
  OnGetValueMethod: TIDECodeMacroGetValueMethod): TIDECodeMacro;

function RegisterCodeMacroEx(const Name: string;
  const ShortDescription, LongDescription: string;
  OnGetValueProc: TIDECodeMacroGetValueExProc;
  OnGetValueMethod: TIDECodeMacroGetValueExMethod): TIDECodeMacro;

{ SearchInFile to search in a file.
  This can be interactively or without user interaction.
  If the file is open in the source editor, changes will be added to the undo
  history.
}
type
  TIDESearchInTextAddMatch = procedure(const Filename: string;
                                  const StartPos, EndPos: TPoint;
                                  const Lines: string) of object;

  { TIDESearchInTextProgress }

  TIDESearchInTextProgress = class
  private
    FAbort: boolean;
    FOnAddMatch: TIDESearchInTextAddMatch;
  protected
    procedure SetAbort(const AValue: boolean); virtual;
  public
    property Abort: boolean read FAbort write SetAbort;
    property OnAddMatch: TIDESearchInTextAddMatch read FOnAddMatch write FOnAddMatch;
  end;

  TIDESearchInTextFunction = function(const TheFileName: string;
    var TheText: string;// if TheFileName='' then use TheText
    SearchFor, ReplaceText: string; Flags: TSrcEditSearchOptions;
    var Prompt: boolean; Progress: TIDESearchInTextProgress = nil): TModalResult;

  TBookmarkNumRange = 0..9;

var
  IDESearchInText: TIDESearchInTextFunction = nil;// set by the IDE

implementation

function RegisterCodeMacro(const Name: string; const ShortDescription,
  LongDescription: string; OnGetValueProc: TIDECodeMacroGetValueProc;
  OnGetValueMethod: TIDECodeMacroGetValueMethod): TIDECodeMacro;
var
  NewName: String;
begin
  NewName:=IDECodeMacros.CreateUniqueName(Name);
  Result:=TIDECodeMacro.Create(NewName);
  Result.ShortDescription:=LineBreaksToSystemLineBreaks(ShortDescription);
  Result.LongDescription:=LineBreaksToSystemLineBreaks(LongDescription);
  Result.OnGetValueProc:=OnGetValueProc;
  Result.OnGetValueMethod:=OnGetValueMethod;
  IDECodeMacros.Add(Result);
end;

function RegisterCodeMacroEx(const Name: string; const ShortDescription,
  LongDescription: string; OnGetValueProc: TIDECodeMacroGetValueExProc;
  OnGetValueMethod: TIDECodeMacroGetValueExMethod): TIDECodeMacro;
var
  NewName: String;
begin
  NewName:=IDECodeMacros.CreateUniqueName(Name);
  Result:=TIDECodeMacro.Create(NewName);
  Result.ShortDescription:=LineBreaksToSystemLineBreaks(ShortDescription);
  Result.LongDescription:=LineBreaksToSystemLineBreaks(LongDescription);
  Result.OnGetValueExProc:=OnGetValueProc;
  Result.OnGetValueExMethod:=OnGetValueMethod;
  IDECodeMacros.Add(Result);
end;

{ TEditorMacroKeyBinding }

function TEditorMacroKeyBinding.GetIdeCmd: TIDECommand;
begin
  Result :=nil;
end;

procedure TEditorMacroKeyBinding.MacroNameChanged;
begin
  //
end;

constructor TEditorMacroKeyBinding.Create(AOwner: TEditorMacro);
begin
  FOwner := AOwner;
end;

function TEditorMacroKeyBinding.ShortCutAsText: String;
begin
  Result := '';
end;

procedure TEditorMacro.CheckStateAndActivated;
begin
  if (State = emStopped) then begin
    if IsActivated then
      UnsetActivated;
  end
  else
  if not IsActivated then
    SetActivated;
end;

function TEditorMacro.GetDefaultKeyBinding: TEditorMacroKeyBinding;
begin
  Result := DefaultBindingClass.Create(Self);
end;

procedure TEditorMacro.SetMacroNameFull(AValue: String);
begin
  SetMacroName(AValue);
  if KeyBinding <> nil then
    KeyBinding.MacroNameChanged;
end;

procedure TEditorMacro.SetOnChange(AValue: TNotifyEvent);
begin
  if FOnChange=AValue then Exit;
  FOnChange:=AValue;
end;

function TEditorMacro.GetErrorMsg: String;
begin
  Result := '';
end;

procedure TEditorMacro.DoChanged;
begin
  if Assigned(FOnChange) then
    FOnChange(Self);
end;

procedure TEditorMacro.DoStateChanged;
begin
  if Assigned(FOnStateChange) then
    FOnStateChange(Self);
end;

procedure TEditorMacro.SetActivated;
begin
  Assert(ActiveEditorMacro=nil, 'TEditorMacro.SetActivated: There already is an active Macro');
  ActiveEditorMacro := self;
end;

procedure TEditorMacro.UnsetActivated;
begin
  Assert(ActiveEditorMacro=self, 'TEditorMacro.SetActivated: This is not the active Macro');
  ActiveEditorMacro := nil;
end;

function TEditorMacro.IsActivated: Boolean;
begin
  Result := ActiveEditorMacro = self;
end;

procedure TEditorMacro.RecordMacro(aEditor: TWinControl);
begin
  SetActivated;
  DoRecordMacro(aEditor);
  CheckStateAndActivated;
end;

procedure TEditorMacro.PlaybackMacro(aEditor: TWinControl);
begin
  SetActivated;
  DoPlaybackMacro(aEditor);
  CheckStateAndActivated;
end;

procedure TEditorMacro.Stop;
begin
  DoStop;
  CheckStateAndActivated;
end;

procedure TEditorMacro.Pause;
begin
  DoPause;
  CheckStateAndActivated;
end;

procedure TEditorMacro.Resume;
begin
  DoResume;
  CheckStateAndActivated;
end;

var
  FMacroListViewerWarningText: String;
  FMacroListViewerWarningChanged: TNotifyProcedure;

function GetMacroListViewerWarningText: String;
begin
  Result := FMacroListViewerWarningText;
end;

procedure SetMacroListViewerWarningText(AValue: String);
begin
  FMacroListViewerWarningText := AValue;
  if FMacroListViewerWarningChanged <> nil then
    FMacroListViewerWarningChanged(nil);
end;

function GetMacroListViewerWarningChanged: TNotifyProcedure;
begin
  Result := FMacroListViewerWarningChanged;
end;

procedure SetMacroListViewerWarningChanged(AValue: TNotifyProcedure);
begin
  FMacroListViewerWarningChanged := AValue;
  if (FMacroListViewerWarningChanged <> nil) and
     (FMacroListViewerWarningText <> '')
  then
    FMacroListViewerWarningChanged(nil);
end;

{ TEditorMacro }

{ TSourceEditorInterface }

procedure TSourceEditorInterface.SelectText(LineNum, CharStart, LineNum2,
  CharEnd: Integer);
begin
  SelectText(Point(CharStart,LineNum),Point(CharEnd,LineNum2));
end;

procedure TSourceEditorInterface.ReplaceText(const StartPos, EndPos: TPoint;
  const NewText: string);
begin
  BeginUpdate;
  BeginUndoBlock{$IFDEF SynUndoDebugBeginEnd}('TSourceEditorInterface.ReplaceText'){$ENDIF};
  try
    SelectText(StartPos,EndPos);
    CursorTextXY:=StartPos;
    Selection:=NewText;
  finally
    EndUndoBlock{$IFDEF SynUndoDebugBeginEnd}('TSourceEditorInterface.ReplaceText'){$ENDIF};
    EndUpdate;
  end;
end;

{ TIDECodeMacro }

procedure TIDECodeMacro.Init;
begin
  FInteractiveValueClass:=TIDEInteractiveStringValue;
end;

constructor TIDECodeMacro.Create(const TheName: string);
begin
  FName:=TheName;
  FShortDescription:=FName;
  FLongDescription:=FName;
end;

function TIDECodeMacro.GetValue(const Parameter: string;
  InteractiveValue: TPersistent; SrcEdit: TSourceEditorInterface;
  out Value, ErrorMsg: string; TemplateParser: TIDETemplateParser = nil): boolean;
begin
  Value:=Parameter;
  ErrorMsg:='';
  if Assigned(OnGetValueProc) then
    Result:=OnGetValueProc(Parameter,InteractiveValue,SrcEdit,Value,ErrorMsg)
  else if Assigned(OnGetValueMethod) then
    Result:=OnGetValueMethod(Parameter,InteractiveValue,SrcEdit,Value,ErrorMsg)
  else if Assigned(OnGetValueExProc) then
    Result:=OnGetValueExProc(Parameter,InteractiveValue,SrcEdit,Value,ErrorMsg,
                             TemplateParser)
  else if Assigned(OnGetValueExMethod) then
    Result:=OnGetValueExMethod(Parameter,InteractiveValue,SrcEdit,Value,ErrorMsg,
                               TemplateParser)
  else
    Result:=true;
end;

{ TIDESearchInTextProgress }

procedure TIDESearchInTextProgress.SetAbort(const AValue: boolean);
begin
  if FAbort=AValue then exit;
  fAbort:=AValue;
end;

{ TSourceEditorCompletionPlugin }

function TSourceEditorCompletionPlugin.HasCustomPaint: boolean;
begin
  Result:=false;
end;

procedure TSourceEditorCompletionPlugin.PaintItem(const AKey: string;
  ACanvas: TCanvas; X, Y: integer; ItemSelected: boolean; Index: integer);
begin
  // this is called if HasCustomPaint returns true
end;

function TSourceEditorCompletionPlugin.MeasureItem(const AKey: string;
  ACanvas: TCanvas; ItemSelected: boolean; Index: integer): TPoint;
begin
  // this is called if HasCustomPaint returns true
  Result:=Point(0,0);
end;

{ TSourceEditorStatusPanelInterface }

procedure TSourceEditorStatusPanelInterface.SetOnDrawPanel(
  AValue: TDrawSourceEditPanelEvent);
begin
  if FOnDrawPanel = AValue then Exit;
  FOnDrawPanel := AValue;
end;

constructor TSourceEditorStatusPanelInterface.Create(AnOwner: TClass;
  ATag: PtrUInt);
begin
  FOwner := AnOwner;
  FTag := ATag;
  inherited Create;
end;

{ TSourceMarkling }

constructor TSourceMarkling.Create(aProducer: TSourceMarklingProducer; TheID,
  aLine, aColumn: integer; aType: TSourceMarklingType);
begin
  FProducer:=aProducer;
  FLine:=aLine;
  FColumn:=aColumn;
  FId:=TheID;
  fType:=aType;
end;

{ TSourceMarklingProducer }

procedure TSourceMarklingProducer.InvalidateAllFiles;
begin
  SourceEditorManagerIntf.InvalidateMarklingsOfAllFiles(Self);
end;

procedure TSourceMarklingProducer.InvalidateFile(aFilename: string);
begin
  SourceEditorManagerIntf.InvalidateMarklings(Self,aFilename);
end;

end.

