From 1592af26bbf48c4a185bb952b3e78644f27c849f Mon Sep 17 00:00:00 2001 From: Basile Burg Date: Tue, 9 Dec 2014 03:48:59 +0100 Subject: [PATCH] added TCEStaticEditorMacro --- lazproj/coedit.lpi | 35 ++++--- lazproj/coedit.lpr | 3 +- src/ce_staticmacro.pas | 202 +++++++++++++++++++++++++++++++++++++++++ src/ce_synmemo.pas | 5 +- 4 files changed, 228 insertions(+), 17 deletions(-) create mode 100644 src/ce_staticmacro.pas diff --git a/lazproj/coedit.lpi b/lazproj/coedit.lpi index 325ed540..1ead881a 100644 --- a/lazproj/coedit.lpi +++ b/lazproj/coedit.lpi @@ -142,7 +142,7 @@ - + @@ -296,47 +296,52 @@ - + + + + + + - - + + - - + + - - + + - - + + - - + + - - + + - + diff --git a/lazproj/coedit.lpr b/lazproj/coedit.lpr index aa5eadf4..3a8625f4 100644 --- a/lazproj/coedit.lpr +++ b/lazproj/coedit.lpr @@ -7,7 +7,8 @@ uses cthreads, {$ENDIF}{$ENDIF} Interfaces, Forms, lazcontrols, runtimetypeinfocontrols, ce_libman, ce_tools, - ce_dcd, ce_observer, ce_main, ce_writableComponent, ce_options, ce_symstring; + ce_dcd, ce_observer, ce_main, ce_writableComponent, ce_options, ce_symstring, + ce_staticmacro; {$R *.res} diff --git a/src/ce_staticmacro.pas b/src/ce_staticmacro.pas new file mode 100644 index 00000000..a1f498c7 --- /dev/null +++ b/src/ce_staticmacro.pas @@ -0,0 +1,202 @@ +unit ce_staticmacro; + +{$I ce_defines.inc} + +interface + +uses + Classes, Sysutils, SynEdit, SynEditAutoComplete, + ce_interfaces, ce_writableComponent, ce_synmemo; + +type + + (** + * TCEStaticEditorMacro is used to insert static macros (parameter-less code snippets) + * in an editor. A macro begins with the dollar symbol and ends with an alphanum. + * + * The dollar symbol is used as starter because it's usually accessible without + * modifier: no CTRL, no ALT, no SHIFT. + * Erroneous insertion is avoided because in D '$' is either followed + * by a symbol: '$-1', '$]' or by a blank '$ ]' + * + * Shift + SPACE works automatically on the right editor (ICEMultiDocObserver) + * Automatic insertion is handled in TCESynMemo.KeyUp() + *) + TCEStaticEditorMacro = class(TWritableComponent, ICEMultiDocObserver) + private + fCompletor: TSynEditAutoComplete; + fMacros: TStringList; + fDoc: TCESynMemo; + fAutomatic: boolean; + procedure sanitize; + procedure addDefaults; + procedure updateCompletor; + procedure setMacros(aValue: TStringList); + // ICEMultiDocObserver + procedure docNew(aDoc: TCESynMemo); + procedure docFocused(aDoc: TCESynMemo); + procedure docChanged(aDoc: TCESynMemo); + procedure docClosing(aDoc: TCESynMemo); + published + // list of string with the format $<..>alnum=<..> + property macros: TStringList read fMacros write setMacros; + property automatic: boolean read fAutomatic write fAutomatic default true; + public + constructor create(aOwner: TComponent); override; + destructor destroy; override; + // execute using the editor + procedure Execute; overload; + // execute in aEditor, according to aToken + procedure Execute(aEditor: TCustomSynEdit; const aToken: string); overload; + end; + +const + macFname = 'staticMacros.txt'; + + defMacros: array[0..5] of string = ( + '$a=auto', + '$c=class {}', + '$s=struct {}', + '$ut=unittest{}', + '$fo=for(auto i = 0; ; )', + '$fe=foreach(elem; )' + ); + +var + StaticEditorMacro: TCEStaticEditorMacro = nil; + +implementation + +uses + ce_observer, ce_common; + +{$REGION Standard Comp/Obj -----------------------------------------------------} +constructor TCEStaticEditorMacro.create(aOwner: TComponent); +var + fname: string; +begin + inherited; + fAutomatic := true; + fCompletor := TSynEditAutoComplete.Create(self); + fMacros := TStringList.Create; + fMacros.Delimiter := '='; + // + fname := getDocPath + macFname; + if fileExists(fname) then loadFromFile(fname); + addDefaults; + // + sanitize; + updateCompletor; + // + EntitiesConnector.addObserver(Self); + EntitiesConnector.endUpdate; +end; + +destructor TCEStaticEditorMacro.destroy; +begin + saveToFile(getDocPath + macFname); + EntitiesConnector.removeObserver(Self); + // + fMacros.Free; + inherited; +end; + +procedure TCEStaticEditorMacro.setMacros(aValue: TStringList); +begin + fMacros.Assign(aValue); + addDefaults; + sanitize; + updateCompletor; +end; +{$ENDREGION} + +{$REGION ICEMultiDocObserver ---------------------------------------------------} +procedure TCEStaticEditorMacro.docNew(aDoc: TCESynMemo); +begin + fDoc := aDoc; +end; + +procedure TCEStaticEditorMacro.docFocused(aDoc: TCESynMemo); +begin + fDoc := aDoc; +end; + +procedure TCEStaticEditorMacro.docChanged(aDoc: TCESynMemo); +begin + if aDoc <> fDoc then + exit; +end; + +procedure TCEStaticEditorMacro.docClosing(aDoc: TCESynMemo); +begin + if aDoc <> fDoc then + exit; + fDoc := nil; +end; +{$ENDREGION} + +{$REGION Macros things ---------------------------------------------------------} +procedure TCEStaticEditorMacro.sanitize; +var + i: Integer; + text: string; + macro: string; +begin + for i := fMacros.Count-1 downto 0 do + begin + text := fMacros.Strings[i]; + if length(text) >= 4 then + if text[1] = '$' then + if Pos('=', text) > 2 then + begin + macro := fMacros.Names[i]; + if (macro[length(macro)] in ['a'..'z', 'A'..'Z', '0'..'9']) then + continue; + end; + fMacros.Delete(i); + end; +end; + +procedure TCEStaticEditorMacro.addDefaults; +var + i: Integer; +begin + for i := 0 to high(defMacros) do + if fMacros.IndexOf(defMacros[i]) = -1 then + fMacros.Add(defMacros[i]); +end; + +procedure TCEStaticEditorMacro.updateCompletor; +var + i: Integer; + tok, val: string; +begin + fCompletor.AutoCompleteList.Clear; + for i := 0 to fMacros.Count-1 do + begin + tok := fMacros.Names[i]; + val := fMacros.ValueFromIndex[i]; + fCompletor.AutoCompleteList.Add(tok); + fCompletor.AutoCompleteList.Add('=' + val); + end; +end; + +procedure TCEStaticEditorMacro.Execute; +begin + if fDoc <> nil then + fCompletor.ExecuteCompletion(fDoc.Identifier, fDoc); +end; + +procedure TCEStaticEditorMacro.Execute(aEditor: TCustomSynEdit; const aToken: string); +begin + if aEditor <> nil then + fCompletor.ExecuteCompletion(aToken, aEditor); +end; +{$ENDREGION} + +initialization + StaticEditorMacro := TCEStaticEditorMacro.create(nil); +finalization + StaticEditorMacro.Free;; +end. + diff --git a/src/ce_synmemo.pas b/src/ce_synmemo.pas index c5fa3198..4616d3d0 100644 --- a/src/ce_synmemo.pas +++ b/src/ce_synmemo.pas @@ -76,7 +76,7 @@ var implementation uses - graphics, ce_interfaces; + graphics, ce_interfaces, ce_staticmacro; {$REGION TCESynMemoPositions ---------------------------------------------------} constructor TCESynMemoPositions.create(aMemo: TCustomSynEdit); @@ -314,6 +314,9 @@ begin if Key in [VK_PRIOR, VK_NEXT, Vk_UP] then fPositions.store; inherited; + // + if StaticEditorMacro.automatic then + StaticEditorMacro.Execute; end; procedure TCESynMemo.MouseMove(Shift: TShiftState; X, Y: Integer);