From 667e92e966392e69aca5a11beb5db5ac06779a39 Mon Sep 17 00:00:00 2001 From: Olly Date: Fri, 8 Sep 2023 03:31:03 +0100 Subject: [PATCH] Completion cleanup and support adding keywords --- .github/workflows/build.yml | 2 +- Source/Simba.lpi | 6 +- .../simba.ide_codetools_keywords.pas | 51 +++ .../codetools/simba.ide_codetools_parser.pas | 88 ++++- .../components/simba.component_scrollbar.pas | 16 +- Source/editor/simba.editor_autocomplete.pas | 356 +++++++++++------- Source/forms/simba.settingsform_codetools.pas | 2 + Source/package/simba.package_components.pas | 4 +- Source/simba.settings.pas | 2 + 9 files changed, 380 insertions(+), 147 deletions(-) create mode 100644 Source/codetools/simba.ide_codetools_keywords.pas diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index bdd42fb77..5b6753eef 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -10,7 +10,7 @@ on: env: FPC_VER: release_3_2_2 - LAZ_VER: lazarus_2_2_4 + LAZ_VER: fixes_3_0 jobs: build: diff --git a/Source/Simba.lpi b/Source/Simba.lpi index 6c3ecd8f0..5929d2458 100644 --- a/Source/Simba.lpi +++ b/Source/Simba.lpi @@ -341,7 +341,7 @@ - + @@ -979,6 +979,10 @@ + + + + diff --git a/Source/codetools/simba.ide_codetools_keywords.pas b/Source/codetools/simba.ide_codetools_keywords.pas new file mode 100644 index 000000000..a19471f95 --- /dev/null +++ b/Source/codetools/simba.ide_codetools_keywords.pas @@ -0,0 +1,51 @@ +{ + Author: Raymond van Venetiƫ and Merlijn Wajer + Project: Simba (https://github.com/MerlijnWajer/Simba) + License: GNU General Public License (https://www.gnu.org/licenses/gpl-3.0) +} +unit simba.ide_codetools_keywords; + +{$i simba.inc} + +interface + +uses + Classes, SysUtils, + simba.mufasatypes, simba.ide_codetools_parser, simba.ide_initialization; + +function GetKeywords: TDeclarationArray; + +implementation + +uses + lpparser, + simba.list; + +type + TKeywordList = specialize TSimbaObjectList; + +var + KeywordsList: TKeywordList; + +procedure CreateKeywords; +var + Keyword: TLapeKeyword; +begin + KeywordsList := TKeywordList.Create(True); + for Keyword in Lape_Keywords do + KeywordsList.Add(TDeclaration_Keyword.Create(Keyword.Keyword.ToLower())); +end; + +function GetKeywords: TDeclarationArray; +begin + Result := KeywordsList.ToArray(); +end; + +initialization + SimbaIDEInitialization_AddBeforeCreate(@CreateKeywords, 'Codetools Create Keywords'); + +finalization + if (KeywordsList <> nil) then + FreeAndNil(KeywordsList); + +end. diff --git a/Source/codetools/simba.ide_codetools_parser.pas b/Source/codetools/simba.ide_codetools_parser.pas index 5890cc1f0..c2e3f5725 100644 --- a/Source/codetools/simba.ide_codetools_parser.pas +++ b/Source/codetools/simba.ide_codetools_parser.pas @@ -94,9 +94,9 @@ TDeclaration = class({$IFDEF PARSER_LEAK_CHECKS}TLeakChecker{$ELSE}TObject{$EN FFlags: UInt32; - procedure ClearFlags; - procedure SetFlags(const Index: Integer; const AValue: Boolean); - function GetFlags(const Index: Integer): Boolean; + procedure ClearFlags; inline; + procedure SetFlags(const Index: Integer; const AValue: Boolean); inline; + function GetFlags(const Index: Integer): Boolean; inline; function GetText: String; function GetTextNoComments: String; @@ -137,6 +137,12 @@ TDeclaration = class({$IFDEF PARSER_LEAK_CHECKS}TLeakChecker{$ELSE}TObject{$EN property isOverrideMethod: Boolean index 6 read GetFlags write SetFlags; property isOverloadMethod: Boolean index 7 read GetFlags write SetFlags; property isStaticMethod: Boolean index 8 read GetFlags write SetFlags; + property isVar: Boolean index 9 read GetFlags write SetFlags; + property isConst: Boolean index 10 read GetFlags write SetFlags; + property isField: Boolean index 11 read GetFlags write SetFlags; + property isEnumElement: Boolean index 12 read GetFlags write SetFlags; + property isType: Boolean index 13 read GetFlags write SetFlags; + property isKeyword: Boolean index 14 read GetFlags write SetFlags; constructor Create(AParser: TCodeParser; AOwner: TDeclaration; AStart: Integer; AEnd: Integer); virtual; reintroduce; destructor Destroy; override; @@ -147,6 +153,11 @@ TDeclaration_Root = class(TDeclaration) constructor Create; reintroduce; end; + TDeclaration_Keyword = class(TDeclaration) + public + constructor Create(Keyword: String); reintroduce; + end; + TDeclaration_Anchor = class(TDeclaration); TDeclaration_WithStatement = class(TDeclaration); @@ -180,7 +191,11 @@ TDeclaration_Identifier = class(TDeclaration); TDeclaration_ParentType = class(TDeclaration_Identifier); TDeclaration_OrdinalType = class(TDeclaration); - TDeclaration_Type = class(TDeclaration); + TDeclaration_Type = class(TDeclaration) + public + constructor Create(AParser: TCodeParser; AOwner: TDeclaration; AStart: Integer; AEnd: Integer); override; + end; + TDeclaration_TypeRecord = class(TDeclaration_Type) public function Parent: TDeclaration; @@ -211,8 +226,10 @@ TDeclaration_TypeAlias = class(TDeclaration_Type) end; TDeclaration_EnumElement = class(TDeclaration) - public + protected function GetName: string; override; + public + constructor Create(AParser: TCodeParser; AOwner: TDeclaration; AStart: Integer; AEnd: Integer); override; end; TDeclaration_EnumElementName = class(TDeclaration) @@ -261,14 +278,21 @@ TDeclaration_Var = class(TDeclaration) public DefToken: TptTokenKind; + constructor Create(AParser: TCodeParser; AOwner: TDeclaration; AStart: Integer; AEnd: Integer); override; + property VarType: TDeclaration read GetVarType; property VarTypeString: String read GetVarTypeString; property VarDefaultString: String read GetVarDefaultString; end; TDeclaration_VarClass = class of TDeclaration_Var; - TDeclaration_Const = class(TDeclaration_Var); - TDeclaration_Field = class(TDeclaration_Var); + TDeclaration_Const = class(TDeclaration_Var) + constructor Create(AParser: TCodeParser; AOwner: TDeclaration; AStart: Integer; AEnd: Integer); override; + end; + + TDeclaration_Field = class(TDeclaration_Var) + constructor Create(AParser: TCodeParser; AOwner: TDeclaration; AStart: Integer; AEnd: Integer); override; + end; TDeclaration_Method = class(TDeclaration) protected @@ -540,6 +564,24 @@ constructor TDeclaration_Root.Create; inherited Create(nil, nil, 0, 0); end; +constructor TDeclaration_Keyword.Create(Keyword: String); +begin + inherited Create(nil, nil, 0, 0); + + FName.Value := Keyword; + + ClearFlags(); + isKeyword := True; +end; + +constructor TDeclaration_Type.Create(AParser: TCodeParser; AOwner: TDeclaration; AStart: Integer; AEnd: Integer); +begin + inherited Create(AParser, AOwner, AStart, AEnd); + + ClearFlags(); + isType := True; +end; + function TDeclaration_EnumElement.GetName: string; begin if FName.IsNull then @@ -548,6 +590,14 @@ function TDeclaration_EnumElement.GetName: string; Result := inherited; end; +constructor TDeclaration_EnumElement.Create(AParser: TCodeParser; AOwner: TDeclaration; AStart: Integer; AEnd: Integer); +begin + inherited Create(AParser, AOwner, AStart, AEnd); + + ClearFlags(); + isEnumElement := True; +end; + function TDeclaration_TypeAlias.VarType: TDeclaration; begin Result := Items.GetByClassFirst(TDeclaration_VarType); @@ -627,6 +677,30 @@ function TDeclaration_Var.GetVarDefaultString: String; Result := FVarDefaultString.Value; end; +constructor TDeclaration_Var.Create(AParser: TCodeParser; AOwner: TDeclaration; AStart: Integer; AEnd: Integer); +begin + inherited Create(AParser, AOwner, AStart, AEnd); + + ClearFlags(); + isVar := True; +end; + +constructor TDeclaration_Const.Create(AParser: TCodeParser; AOwner: TDeclaration; AStart: Integer; AEnd: Integer); +begin + inherited Create(AParser, AOwner, AStart, AEnd); + + ClearFlags(); + isConst := True; +end; + +constructor TDeclaration_Field.Create(AParser: TCodeParser; AOwner: TDeclaration; AStart: Integer; AEnd: Integer); +begin + inherited Create(AParser, AOwner, AStart, AEnd); + + ClearFlags(); + isField := True; +end; + function TDeclaration_EnumElementName.GetName: string; begin if FName.IsNull then diff --git a/Source/components/simba.component_scrollbar.pas b/Source/components/simba.component_scrollbar.pas index b8aa9d055..1bf4e696c 100644 --- a/Source/components/simba.component_scrollbar.pas +++ b/Source/components/simba.component_scrollbar.pas @@ -10,7 +10,7 @@ interface uses - Classes, SysUtils, Controls, ComCtrls, + Classes, SysUtils, Controls, ComCtrls, StdCtrls, ATScrollBar, simba.settings; type @@ -23,6 +23,8 @@ TSimbaScrollBar = class(TATScrollBar) public ForwardScrollControl: TControl; + procedure Assign(From: TScrollBar); + constructor Create(AOwner: TComponent); override; end; @@ -53,6 +55,18 @@ procedure TSimbaScrollBar.DoSettingChange_ScrollBarArrowSize(Setting: TSimbaSett Update(); end; +procedure TSimbaScrollBar.Assign(From: TScrollBar); +begin + Min := From.Min; + Max := From.Max+1; + SmallChange := From.SmallChange; + LargeChange := From.LargeChange; + PageSize := From.PageSize; + Position := From.Position; + + Update(); +end; + constructor TSimbaScrollBar.Create(AOwner: TComponent); begin inherited Create(AOwner); diff --git a/Source/editor/simba.editor_autocomplete.pas b/Source/editor/simba.editor_autocomplete.pas index bd668c714..335d3bf70 100644 --- a/Source/editor/simba.editor_autocomplete.pas +++ b/Source/editor/simba.editor_autocomplete.pas @@ -12,10 +12,10 @@ interface uses - Classes, SysUtils, Graphics, StdCtrls, Controls, Forms, LCLType, Types, + Classes, SysUtils, Graphics, StdCtrls, Controls, Forms, LCLType, SynEdit, SynEditTypes, SynCompletion, SynEditKeyCmds, SynEditHighlighter, - simba.mufasatypes, simba.settings, simba.ide_codetools_parser, simba.ide_codetools_insight, - simba.component_scrollbar; + simba.mufasatypes, simba.settings, simba.component_scrollbar, + simba.ide_codetools_parser, simba.ide_codetools_insight; type TSimbaAutoCompleteSizeDrag = class(TSynBaseCompletionFormSizeDrag) @@ -27,7 +27,6 @@ TSimbaAutoComplete_Form = class(TSynCompletionForm) protected RealScroll: TSimbaScrollBar; - procedure DoPaintSizeDrag(Sender: TObject); procedure DoScrollChange(Sender: TObject); procedure FontChanged(Sender: TObject); override; @@ -35,6 +34,7 @@ TSimbaAutoComplete_Form = class(TSynCompletionForm) procedure Paint; override; procedure DoShow; override; procedure DoHide; override; + procedure MouseDown(Button: TMouseButton; Shift: TShiftState; X, Y: Integer); override; procedure MouseMove(Shift: TShiftState; X, Y: Integer); override; procedure KeyDown(var Key: Word; Shift: TShiftState); override; public @@ -45,7 +45,9 @@ TSimbaAutoComplete_Form = class(TSynCompletionForm) TSimbaAutoComplete_Hint = class(TSynBaseCompletionHint) public - procedure SetBounds(ALeft, ATop, AWidth, AHeight: integer); override; + function UseBGThemes: Boolean; override; + function UseFGThemes: Boolean; override; + procedure ActivateSub; override; procedure EraseBackground(DC: HDC); override; procedure Paint; override; public @@ -56,6 +58,9 @@ TSimbaAutoComplete_Hint = class(TSynBaseCompletionHint) end; TSimbaAutoComplete = class(TSynCompletion) + public + type + TPaintItemEvent = procedure(ACanvas: TCanvas; ItemRect: TRect; Index: Integer; Selected: Boolean) of object; protected FForm: TSimbaAutoComplete_Form; FHintForm: TSimbaAutoComplete_Hint; @@ -63,11 +68,13 @@ TSimbaAutoComplete = class(TSynCompletion) FDecls: TDeclarationArray; FLocalDecls: TDeclarationArray; + FOnPaintListItem: TPaintItemEvent; + FOnPaintHintItem: TPaintItemEvent; + FFilteredDecls: TDeclarationArray; FFilteredWeights: TIntegerArray; // cache FColumnWidth: Integer; - FDrawOffsetY: Integer; procedure DoSettingChanged_CompletionKey(Setting: TSimbaSetting); @@ -78,11 +85,13 @@ TSimbaAutoComplete = class(TSynCompletion) procedure ContinueCompletion(Data: PtrInt); - procedure PaintColumn(Canvas: TCanvas; var X, Y: Integer; Decl: TDeclaration); - procedure PaintName(Canvas: TCanvas; var X, Y: Integer; AName: String); - procedure PaintText(Canvas: TCanvas; X, Y: Integer; AText: String); + procedure PaintColumn(Canvas: TCanvas; var R: TRect; Decl: TDeclaration); + procedure PaintName(Canvas: TCanvas; var R: TRect; AName: String); + procedure PaintText(Canvas: TCanvas; var R: TRect; AText: String); + + procedure DoPaintListItem(ACanvas: TCanvas; ItemRect: TRect; Index: Integer; Selected: Boolean); + procedure DoPaintHintItem(ACanvas: TCanvas; ItemRect: TRect; Index: Integer; Selected: Boolean); - function DoPaintItem(const Key: String; Canvas: TCanvas; X, Y: Integer; Selected: Boolean; Index: Integer): Boolean; function DoMeasureItem(const AKey: string; ACanvas: TCanvas; Selected: boolean; Index: integer): TPoint; procedure DoCodeCompletion(var Value: String; SourceValue: String; var SourceStart, SourceEnd: TPoint; KeyChar: TUTF8Char; Shift: TShiftState); @@ -98,6 +107,9 @@ TSimbaAutoComplete = class(TSynCompletion) destructor Destroy; override; property Form: TSimbaAutoComplete_Form read FForm; + + property OnPaintListItem: TPaintItemEvent read FOnPaintListItem write FOnPaintListItem; + property OnPaintHintItem: TPaintItemEvent read FOnPaintHintItem write FOnPaintHintItem; public class var AutoCompleteCommand: TSynEditorCommand; class function IsAutoCompleteCommand(Command: TSynEditorCommand; AChar: TUTF8Char): Boolean; @@ -107,7 +119,9 @@ TSimbaAutoComplete = class(TSynCompletion) implementation uses - simba.algo_sort, simba.editor, simba.ide_codetools_setup, simba.theme; + ATCanvasPrimitives, + simba.algo_sort, simba.editor, simba.theme, simba.fonthelpers, + simba.ide_codetools_setup, simba.ide_codetools_keywords; {$IFDEF WINDOWS} function SetClassLong(Handle: HWND; Index: Integer = -26; Value: Integer = 0): UInt32; stdcall; external 'user32' name 'SetClassLongA'; @@ -136,10 +150,11 @@ procedure TSimbaAutoCompleteSizeDrag.Paint; Canvas.FillRect(ClientRect); I := 2; - while (I < Height-3) do + while (I < Height - 3) do begin Canvas.MoveTo(ClientRect.Right-I, ClientRect.Bottom-1-1); Canvas.LineTo(ClientRect.Right-1, ClientRect.Bottom-I-1); + Inc(I, 3); end; end; @@ -166,47 +181,46 @@ procedure TVirtualStringList.SetCount(Value: Integer); procedure TSimbaAutoComplete_Hint.Paint; begin - if (AutoComplete.Position = Index) then - Canvas.Brush.Color := AutoComplete.SelectedColor - else - Canvas.Brush.Color := AutoComplete.Form.Color; - Canvas.FillRect(ClientRect); - - AutoComplete.OnPaintItem('', Canvas, AutoComplete.Form.DrawBorderWidth, 0, AutoComplete.Position = Index, Index); + if Assigned(AutoComplete.OnPaintHintItem) then + AutoComplete.OnPaintHintItem(Canvas, ClientRect, Index, Index = AutoComplete.Position); end; constructor TSimbaAutoComplete_Hint.Create(AOwner: TComponent); begin inherited Create(AOwner); - Color := clRed; // disable "UseBGThemes" to stop flickering. We custom draw so this color doesn't matter. - {$IFDEF WINDOWS} SetClassLong(Handle); // Clear CS_DROPSHADOW {$ENDIF} end; -procedure TSimbaAutoComplete_Hint.SetBounds(ALeft, ATop, AWidth, AHeight: integer); +function TSimbaAutoComplete_Hint.UseBGThemes: Boolean; begin - if (AutoComplete <> nil) then - begin - ATop := HintRect.Top + AutoComplete.Form.DrawBorderWidth; - AWidth := TextWidth; - AHeight := AutoComplete.FontHeight - 3; - end; + Result := False; +end; - inherited SetBounds(ALeft, ATop, AWidth, AHeight); +function TSimbaAutoComplete_Hint.UseFGThemes: Boolean; +begin + Result := False; end; -procedure TSimbaAutoComplete_Hint.EraseBackground(DC: HDC); +procedure TSimbaAutoComplete_Hint.ActivateSub; begin - { nothing } + Visible := False; + + SetBounds( + HintRect.Left + AutoComplete.Form.DrawBorderWidth, + HintRect.Top + AutoComplete.Form.DrawBorderWidth, + TextWidth, + (HintRect.Bottom - HintRect.Top) - 2 + ); + + Visible := True; end; -procedure TSimbaAutoComplete_Form.DoPaintSizeDrag(Sender: TObject); +procedure TSimbaAutoComplete_Hint.EraseBackground(DC: HDC); begin - Canvas.Brush.Color := 255; - Canvas.FillRect(ClientRect); + { nothing } end; procedure TSimbaAutoComplete_Form.DoScrollChange(Sender: TObject); @@ -215,33 +229,68 @@ procedure TSimbaAutoComplete_Form.DoScrollChange(Sender: TObject); end; procedure TSimbaAutoComplete_Form.FontChanged(Sender: TObject); -var - Size: TSize; begin inherited FontChanged(Sender); if (FHint <> nil) then FHint.Font := Self.Font; - FFontHeight := FFontHeight + 4; if (AutoComplete <> nil) then - begin - Size := Canvas.TextExtent('class const '); + AutoComplete.FColumnWidth := Canvas.TextWidth('procedure '); - AutoComplete.FColumnWidth := Size.Width; - AutoComplete.FDrawOffsetY := (((FFontHeight - 2) - Size.Height) div 2) - 1; + with TBitmap.Create() do + try + Canvas.Font := Self.Font; + Canvas.Font.Size := GetFontSize(Self, 3); + + FFontHeight := Canvas.TextHeight('TaylorSwift'); + finally + Free(); end; end; procedure TSimbaAutoComplete_Form.Paint; +var + I: Integer; + ItemRect: TRect; begin - inherited Paint; + // update scroll bar + Scroll.Enabled := ItemList.Count > NbLinesInWindow; + Scroll.Visible := (ItemList.Count > NbLinesInWindow) or ShowSizeDrag; + + if Scroll.Visible and Scroll.Enabled then + begin + Scroll.Max := ItemList.Count - 1; + Scroll.LargeChange := NbLinesInWindow; + Scroll.PageSize := NbLinesInWindow; + end else + begin + Scroll.PageSize := 1; + Scroll.Max := 0; + end; + + for i := 0 to Min(NbLinesInWindow - 1, ItemList.Count - Scroll.Position - 1) do + begin + ItemRect.Left := DrawBorderWidth; + ItemRect.Right := RealScroll.Left; + ItemRect.Top := DrawBorderWidth + FFontHeight * i; + ItemRect.Bottom := ItemRect.Top + FontHeight; + + if Assigned(AutoComplete.FOnPaintListItem) then + AutoComplete.FOnPaintListItem(Canvas, ItemRect, i + Scroll.Position, i + Scroll.Position = Position); + end; + + // draw a rectangle around the window + if DrawBorderWidth > 0 then + begin + Canvas.Brush.Color := DrawBorderColor; + Canvas.FillRect(0, 0, Width, DrawBorderWidth); + Canvas.FillRect(Width-DrawBorderWidth, 0, Width, Height); + Canvas.FillRect(0, Height-DrawBorderWidth, Width, Height); + Canvas.FillRect(0, 0, DrawBorderWidth, Height); + end; - RealScroll.Max := ItemList.Count; - RealScroll.LargeChange := NbLinesInWindow; - RealScroll.PageSize := NbLinesInWindow; - RealScroll.Position := Scroll.Position; - RealScroll.Update(); + RealScroll.Assign(Scroll); end; procedure TSimbaAutoComplete_Form.DoShow; @@ -262,6 +311,17 @@ procedure TSimbaAutoComplete_Form.DoHide; SimbaSettings.Editor.AutoCompleteLines.Value := NbLinesInWindow; end; +procedure TSimbaAutoComplete_Form.MouseDown(Button: TMouseButton; Shift: TShiftState; X, Y: Integer); +var + OldPosition: Integer; +begin + OldPosition := Position; + + Position := Scroll.Position + ((Y - DrawBorderWidth) div FFontHeight); + if DoubleClickSelects and (ssDouble in Shift) and (Position = OldPosition) and Assigned(OnValidate) then + OnValidate(Self, '', Shift); +end; + procedure TSimbaAutoComplete_Form.MouseMove(Shift: TShiftState; X, Y: Integer); begin if ((Scroll.Visible) and (X > Scroll.Left)) or (Y < DrawBorderWidth) or (Y >= ClientHeight - DrawBorderWidth) then @@ -306,9 +366,10 @@ constructor TSimbaAutoComplete_Form.Create(AOwner: TComponent); SizeDrag.Parent := Self; SizeDrag.BevelInner := bvNone; SizeDrag.BevelOuter := bvNone; - SizeDrag.Caption := ''; - SizeDrag.AutoSize := False; SizeDrag.BorderStyle := bsNone; + SizeDrag.Cursor := crSizeNWSE; + SizeDrag.AutoSize := False; + SizeDrag.Visible := False; SizeDrag.Anchors := [akBottom, akRight, akLeft]; SizeDrag.AnchorSideLeft.Side := asrTop; SizeDrag.AnchorSideLeft.Control := RealScroll; @@ -316,10 +377,8 @@ constructor TSimbaAutoComplete_Form.Create(AOwner: TComponent); SizeDrag.AnchorSideRight.Control := Self; SizeDrag.AnchorSideBottom.Side := asrBottom; SizeDrag.AnchorSideBottom.Control := Self; - SizeDrag.Cursor := crSizeNWSE; - SizeDrag.Visible := False; - SizeDrag.Constraints.MinHeight := RealScroll.Width-DrawBorderWidth; - SizeDrag.Constraints.MaxHeight := RealScroll.Width-DrawBorderWidth; + SizeDrag.Constraints.MinHeight := RealScroll.Width - DrawBorderWidth; + SizeDrag.Constraints.MaxHeight := RealScroll.Width - DrawBorderWidth; RealScroll.AnchorSide[akBottom].Control := SizeDrag; @@ -327,7 +386,7 @@ constructor TSimbaAutoComplete_Form.Create(AOwner: TComponent); FItemList.Free(); FItemList := TVirtualStringList.Create(){%H-}; - DrawBorderWidth := 4; + DrawBorderWidth := 5; {$IFDEF WINDOWS} SetClassLong(FHint.Handle); // Clear CS_DROPSHADOW @@ -457,7 +516,7 @@ procedure TSimbaAutoComplete.DoExecute(Sender: TObject); FForm.TextSelectedColor := Editor.Highlighter.IdentifierAttribute.Foreground; FForm.DrawBorderColor := SimbaTheme.ColorScrollBarInActive; FForm.BackgroundColor := SimbaTheme.ColorScrollBarInActive; - Fform.ClSelect := Editor.SelectedColor.Background; + FForm.ClSelect := Editor.SelectedColor.Background; FForm.Font := Editor.Font; end; end; @@ -572,7 +631,11 @@ procedure TSimbaAutoComplete.DoEditorCommand(Sender: TObject; AfterProcessing: B begin Filter := Expression; - FDecls := FCodeinsight.GetGlobals(); + if SimbaSettings.CodeTools.CompletionAddKeywords.Value then + FDecls := FCodeinsight.GetGlobals() + GetKeywords() + else + FDecls := FCodeinsight.GetGlobals(); + FLocalDecls := FCodeinsight.GetLocals(); end; @@ -611,70 +674,47 @@ procedure TSimbaAutoComplete.DoEditorRemoving(Value: TCustomSynEdit); inherited DoEditorRemoving(Value); end; -procedure TSimbaAutoComplete.PaintColumn(Canvas: TCanvas; var X, Y: Integer; Decl: TDeclaration); +procedure TSimbaAutoComplete.PaintColumn(Canvas: TCanvas; var R: TRect; Decl: TDeclaration); type TColumnFormat = record Text: String; Color: TColor; end; const - COLUMN_VAR: TColumnFormat = (Text: 'var'; Color: clPurple); - COLUMN_FUNC: TColumnFormat = (Text: 'function'; Color: $008CFF); - COLUMN_PROC: TColumnFormat = (Text: 'procedure'; Color: clNavy); - COLUMN_TYPE: TColumnFormat = (Text: 'type'; Color: clGreen); - COLUMN_ENUM: TColumnFormat = (Text: 'enum'; Color: $9314FF); - COLUMN_CONST: TColumnFormat = (Text: 'const'; Color: $8B8B00); - COLUMN_CLASS_VAR: TColumnFormat = (Text: 'class var'; Color: clPurple); - COLUMN_CLASS_CONST: TColumnFormat = (Text: 'class const'; Color: $8B8B00); + COLUMN_VAR: TColumnFormat = (Text: 'var'; Color: $3cb44b); + COLUMN_FUNC: TColumnFormat = (Text: 'function'; Color: $ffe119); + COLUMN_PROC: TColumnFormat = (Text: 'procedure'; Color: $e632f0); + COLUMN_TYPE: TColumnFormat = (Text: 'type'; Color: $45efbf); + COLUMN_ENUM: TColumnFormat = (Text: 'enum'; Color: $3182f5); + COLUMN_CONST: TColumnFormat = (Text: 'const'; Color: $b1d8ff); + COLUMN_KEYWORD: TColumnFormat = (Text: 'keyword'; Color: $BB7DC7); + COLUMN_UNKNOWN: TColumnFormat = (Text: ''; Color: $000000); var Column: TColumnFormat; begin - if (Decl is TDeclaration_Method) then - begin - if Decl.isFunction then - Column := COLUMN_FUNC - else - Column := COLUMN_PROC; - end else - if (Decl.Owner is TDeclaration_TypeRecord) then - begin - if (Decl is TDeclaration_Field) then - Column := COLUMN_VAR - else - if (Decl is TDeclaration_Const) then - Column := COLUMN_CLASS_CONST - else - if (Decl is TDeclaration_Var) then - Column := COLUMN_CLASS_VAR; - end else - begin - if (Decl is TDeclaration_EnumElement) then - Column := COLUMN_ENUM - else - if (Decl is TDeclaration_Const) then - Column := COLUMN_CONST - else - if (Decl is TDeclaration_Var) then - Column := COLUMN_VAR - else - if (Decl is TDeclaration_Type) then - Column := COLUMN_TYPE; - end; + if Decl.isFunction then Column := COLUMN_FUNC else + if Decl.isProcedure then Column := COLUMN_PROC else + if Decl.isType then Column := COLUMN_TYPE else + if Decl.isVar then Column := COLUMN_VAR else + if Decl.isConst then Column := COLUMN_CONST else + if Decl.isEnumElement then Column := COLUMN_ENUM else + if Decl.isKeyword then Column := COLUMN_KEYWORD else + Column := COLUMN_UNKNOWN; Canvas.Font.Color := Column.Color; - Canvas.TextOut(X + 2, Y, Column.Text); + Canvas.TextRect(R, R.Left, R.Top, Column.Text); - X := FColumnWidth; + R.Left := FColumnWidth; end; -procedure TSimbaAutoComplete.PaintName(Canvas: TCanvas; var X, Y: Integer; AName: String); +procedure TSimbaAutoComplete.PaintName(Canvas: TCanvas; var R: TRect; AName: String); - procedure DrawText(Str: String; Color: TColor); + procedure DrawText(const Str: String); begin - Canvas.Font.Color := Color; - Canvas.TextOut(X, Y, Str); + Canvas.Font.Color := Form.TextColor; + Canvas.TextRect(R, R.Left, R.Top, Str); - X := X + Canvas.TextWidth(Str); + R.Left := R.Left + Canvas.TextWidth(Str); end; var @@ -682,28 +722,46 @@ procedure TSimbaAutoComplete.PaintName(Canvas: TCanvas; var X, Y: Integer; AName begin Canvas.Font.Bold := True; - if (CurrentString = '') then - DrawText(AName, Form.TextColor) - else + if (CurrentString <> '') then begin Strings := AName.Partition(CurrentString, False); - DrawText(Strings[0], Form.TextColor); - DrawText(Strings[1], $00008B); - DrawText(Strings[2], Form.TextColor); - end; + DrawText(Strings[0]); + + // Underline matching part + Canvas.Pen.EndCap := pecFlat; + Canvas.Pen.Color := Form.TextColor; + Canvas.Line( + R.Left + 1, + R.Top + FontHeight - 4, + R.Left + Canvas.TextWidth(Strings[1]) - 1, + R.Top + FontHeight - 4 + ); + + Canvas.Pen.Color := ColorBlendHalf(Form.BackgroundColor, Form.TextColor); + Canvas.Line( + R.Left + 1, + R.Top + FontHeight - 3, + R.Left + Canvas.TextWidth(Strings[1]) - 1, + R.Top + FontHeight - 3 + ); + + DrawText(Strings[1]); + DrawText(Strings[2]); + end else + DrawText(AName); Canvas.Font.Bold := False; end; -procedure TSimbaAutoComplete.PaintText(Canvas: TCanvas; X, Y: Integer; AText: String); +procedure TSimbaAutoComplete.PaintText(Canvas: TCanvas; var R: TRect; AText: String); var Highlighter: TSynCustomHighlighter; TokStart: PChar; TokLen: Integer; TokString: String; begin - SetLength(TokString, 64); + SetLength(TokString, Length(AText)); Highlighter := Editor.Highlighter; Highlighter.ResetRange(); @@ -715,8 +773,6 @@ procedure TSimbaAutoComplete.PaintText(Canvas: TCanvas; X, Y: Integer; AText: St if (TokLen > 0) then begin - if (TokLen > Length(TokString)) then - SetLength(TokString, (Length(TokString) * 2) + TokLen); Move(TokStart^, TokString[1], TokLen); with Highlighter.GetTokenAttribute() do @@ -727,9 +783,9 @@ procedure TSimbaAutoComplete.PaintText(Canvas: TCanvas; X, Y: Integer; AText: St Canvas.Font.Color := ColorToRGB(Foreground); Canvas.Font.Style := []; - Canvas.TextOut(X, Y, TokString.CopyRange(1, TokLen)); + Canvas.TextRect(R, R.Left, R.Top, TokString.CopyRange(1, TokLen)); - X := X + Canvas.TextWidth(TokString.CopyRange(1, TokLen)); + R.Left := R.Left + Canvas.TextWidth(TokString.CopyRange(1, TokLen)); end; end; @@ -737,39 +793,62 @@ procedure TSimbaAutoComplete.PaintText(Canvas: TCanvas; X, Y: Integer; AText: St end; end; -function TSimbaAutoComplete.DoPaintItem(const Key: String; Canvas: TCanvas; X, Y: Integer; Selected: Boolean; Index: Integer): Boolean; +procedure TSimbaAutoComplete.DoPaintListItem(ACanvas: TCanvas; ItemRect: TRect; Index: Integer; Selected: Boolean); var Decl: TDeclaration; - Text: String; begin - Result := True; - Decl := GetDecl(Index); + if (Decl <> nil) then begin - Canvas.Brush.Style := bsClear; + with ACanvas.TextStyle do + Layout := tlCenter; - Text := GetHintText(Decl, Canvas = FHintForm.Canvas); - if (Canvas = FHintForm.Canvas) then - FHintForm.Caption := Text; + ACanvas.Brush.Style := bsClear; + if Selected then + begin + ACanvas.Brush.Color := Form.ClSelect; + ACanvas.FillRect(ItemRect); + end; - Y := Y + FDrawOffsetY; + ItemRect.Bottom := ItemRect.Bottom - 2; // Ensure extra space for underline - PaintColumn(Canvas, X, Y, Decl); - PaintName(Canvas, X, Y, Decl.Name); - PaintText(Canvas, X, Y, Text); + PaintColumn(ACanvas, ItemRect, Decl); + PaintName(ACanvas, ItemRect, Decl.Name); + PaintText(ACanvas, ItemRect, GetHintText(Decl, False)); end; end; -function TSimbaAutoComplete.DoMeasureItem(const AKey: string; ACanvas: TCanvas; Selected: boolean; Index: integer): TPoint; +procedure TSimbaAutoComplete.DoPaintHintItem(ACanvas: TCanvas; ItemRect: TRect; Index: Integer; Selected: Boolean); +var + Decl: TDeclaration; +begin + Decl := GetDecl(Index); - function Measure(const Text: String; Bold: Boolean): Integer; + if (Decl <> nil) then begin - ACanvas.Font.Bold := Bold; - Result := ACanvas.TextWidth(Text); - ACanvas.Font.Bold := False; + with ACanvas.TextStyle do + Layout := tlCenter; + + if Selected then + ACanvas.Brush.Color := Form.ClSelect + else + ACanvas.Brush.Color := Form.BackgroundColor; + ACanvas.FillRect(ItemRect); + + ItemRect.Bottom := ItemRect.Bottom - 2; // Ensure extra space for underline + + PaintColumn(ACanvas, ItemRect, Decl); + + // Hint window does not include left border + ItemRect.Left := FColumnWidth - TheForm.DrawBorderWidth; + + PaintName(ACanvas, ItemRect, Decl.Name); + PaintText(ACanvas, ItemRect, GetHintText(Decl, True)); end; +end; +function TSimbaAutoComplete.DoMeasureItem(const AKey: string; ACanvas: TCanvas; Selected: boolean; Index: integer): TPoint; var Decl: TDeclaration; MaxRight: Integer; @@ -778,7 +857,12 @@ function TSimbaAutoComplete.DoMeasureItem(const AKey: string; ACanvas: TCanvas; if (Decl <> nil) then begin - FHintForm.TextWidth := FColumnWidth + Measure(Decl.Name, True) + Measure(GetHintText(Decl, True), False) + FForm.DrawBorderWidth; + FHintForm.TextWidth := FColumnWidth + FForm.DrawBorderWidth; + + ACanvas.Font.Bold := True; + FHintForm.TextWidth += ACanvas.TextWidth(Decl.Name); + ACanvas.Font.Bold := False; + FHintForm.TextWidth += ACanvas.TextWidth(GetHintText(Decl, True)); // Either clip to screen right or main form right, whatever is more right. MaxRight := Max(Application.MainForm.BoundsRect.Right, FForm.Monitor.BoundsRect.Right); @@ -803,7 +887,9 @@ constructor TSimbaAutoComplete.Create(AOwner: TComponent); FForm.FHint := FHintForm; - OnPaintItem := @DoPaintItem; + OnPaintListItem := @DoPaintListItem; + OnPaintHintItem := @DoPaintHintItem; + OnCodeCompletion := @DoCodeCompletion; OnSearchPosition := @DoFiltering; OnKeyCompletePrefix := @DoTabPressed; diff --git a/Source/forms/simba.settingsform_codetools.pas b/Source/forms/simba.settingsform_codetools.pas index 82eeed54b..9169d92cf 100644 --- a/Source/forms/simba.settingsform_codetools.pas +++ b/Source/forms/simba.settingsform_codetools.pas @@ -78,6 +78,7 @@ procedure TSimbaCodetoolsFrame.Load; begin AddKeysToComboBoxes(); + CompletionKeywordsCheckbox.Checked := SimbaSettings.CodeTools.CompletionAddKeywords.Value; IgnoreDirectiveCheckbox.Checked := SimbaSettings.CodeTools.IgnoreIDEDirective.Value; AutoOpenParamHintCheckbox.Checked := SimbaSettings.CodeTools.ParamHintOpenAutomatically.Value; AutoOpenCompletionCheckbox.Checked := SimbaSettings.CodeTools.CompletionOpenAutomatically.Value; @@ -99,6 +100,7 @@ procedure TSimbaCodetoolsFrame.Save; begin SimbaSettings.CodeTools.IgnoreIDEDirective.Value := IgnoreDirectiveCheckbox.Checked; SimbaSettings.CodeTools.ParamHintOpenAutomatically.Value := AutoOpenParamHintCheckbox.Checked; + SimbaSettings.CodeTools.CompletionAddKeywords.Value := CompletionKeywordsCheckbox.Checked; SimbaSettings.CodeTools.CompletionOpenAutomatically.Value := AutoOpenCompletionCheckbox.Checked; if (CompletionKeyCombo.ItemIndex > -1) then diff --git a/Source/package/simba.package_components.pas b/Source/package/simba.package_components.pas index d50609ef6..a01155569 100644 --- a/Source/package/simba.package_components.pas +++ b/Source/package/simba.package_components.pas @@ -74,7 +74,7 @@ TVersionInfo = class procedure DoLineWrapping(Foo: PtrInt); procedure DoFontCalculate; - procedure DrawCellText(aCol, aRow: Integer; aRect: TRect; aState: TGridDrawState; {$if LCL_FULLVERSION >= 3000001}const{$ENDIF} aText: String); override; + procedure DrawCellText(aCol, aRow: Integer; aRect: TRect; aState: TGridDrawState; aText: String); override; procedure DrawFocusRect(aCol, aRow: Integer; ARect: TRect); override; procedure PrepareCanvas(aCol, aRow: Integer; aState: TGridDrawState); override; public @@ -561,7 +561,7 @@ procedure TPackageVersionGrid.DoOnResize; Application.QueueAsyncCall(@DoLineWrapping, 0); end; -procedure TPackageVersionGrid.DrawCellText(aCol, aRow: Integer; aRect: TRect; aState: TGridDrawState; {$if LCL_FULLVERSION >= 3000001}const{$ENDIF} aText: String); +procedure TPackageVersionGrid.DrawCellText(aCol, aRow: Integer; aRect: TRect; aState: TGridDrawState; aText: String); var Data: TObject; Info: TVersionInfo absolute Data; diff --git a/Source/simba.settings.pas b/Source/simba.settings.pas index 57223f18f..a30ef5d12 100644 --- a/Source/simba.settings.pas +++ b/Source/simba.settings.pas @@ -130,6 +130,7 @@ TSimbaSettings = class CodeTools: record IgnoreIDEDirective: TSimbaSetting; + CompletionAddKeywords: TSimbaSetting; CompletionOpenAutomatically: TSimbaSetting; CompletionKey: TSimbaSetting; CompletionKeyModifiers: TSimbaSetting; @@ -523,6 +524,7 @@ constructor TSimbaSettings.Create; CodeTools.IgnoreIDEDirective := TSimbaSetting_Boolean.Create(Self, 'CodeTools', 'IgnoreIDEDirective', False); + Codetools.CompletionAddKeywords := TSimbaSetting_Boolean.Create(Self, 'CodeTools', 'CompletionAddKeywords', True); CodeTools.CompletionOpenAutomatically := TSimbaSetting_Boolean.Create(Self, 'CodeTools', 'CompletionOpenAutomatically', True); CodeTools.CompletionKey := TSimbaSetting_Integer.Create(Self, 'CodeTools', 'CompletionKey', VK_SPACE); CodeTools.CompletionKeyModifiers := TSimbaSetting_Integer.Create(Self, 'CodeTools', 'CompletionKeyModifiers', Integer(TShiftState([ssCtrl])));