diff --git a/.gitattributes b/.gitattributes index 47e0253dfc..809051806b 100644 --- a/.gitattributes +++ b/.gitattributes @@ -2121,6 +2121,7 @@ components/synedit/syneditmarks.pp svneol=native#text/plain components/synedit/syneditmarkup.pp svneol=native#text/plain components/synedit/syneditmarkupbracket.pp svneol=native#text/plain components/synedit/syneditmarkupctrlmouselink.pp svneol=native#text/plain +components/synedit/syneditmarkupguttermark.pp svneol=native#text/pascal components/synedit/syneditmarkuphighall.pp svneol=native#text/plain components/synedit/syneditmarkupselection.pp svneol=native#text/plain components/synedit/syneditmarkupspecialline.pp svneol=native#text/plain diff --git a/components/synedit/Makefile.fpc b/components/synedit/Makefile.fpc index 0caa8d8270..8b9c8445a9 100644 --- a/components/synedit/Makefile.fpc +++ b/components/synedit/Makefile.fpc @@ -39,7 +39,8 @@ implicitunits=synbeautifier syncompletion syndesignstringconstants \ synedit syneditautocomplete syneditexport syneditfoldedview synedithighlighter \ synedithighlighterfoldbase synedithighlighterxmlbase syneditkeycmds \ syneditlazdsgn syneditlines syneditmarks \ - syneditmarkup syneditmarkupbracket syneditmarkupctrlmouselink syneditmarkuphighall \ + syneditmarkup syneditmarkupbracket syneditmarkupctrlmouselink \ + syneditmarkuphighall syneditmarkupguttermark\ syneditmarkupselection syneditmarkupspecialline syneditmarkupwordgroup \ syneditmiscclasses syneditmiscprocs syneditmousecmds syneditplugins \ syneditpointclasses syneditregexsearch syneditsearch syneditstrconst synedittextbase \ diff --git a/components/synedit/allsyneditunits.pp b/components/synedit/allsyneditunits.pp index e232305f60..d6d84fb6ef 100644 --- a/components/synedit/allsyneditunits.pp +++ b/components/synedit/allsyneditunits.pp @@ -43,6 +43,9 @@ uses SynHighlighterDiff, SynGutter, SynGutterChanges, SynGutterCodeFolding, SynGutterLineNumber, SynGutterMarks, SynGutterLineOverview, + SynEditMarkup, SynEditMarkupBracket, SynEditMarkupCtrlMouseLink, + SynEditMarkupHighAll, SynEditMarkupSelection, SynEditMarkupSpecialLine, + SynEditMarkupWordGroup, SynEditMarkupGutterMark, SynPropertyEditObjectList, SynDesignStringConstants; implementation diff --git a/components/synedit/syneditmarkup.pp b/components/synedit/syneditmarkup.pp index 25c5dc5a9c..cd916d33b9 100644 --- a/components/synedit/syneditmarkup.pp +++ b/components/synedit/syneditmarkup.pp @@ -95,6 +95,7 @@ type Procedure EndMarkup; virtual; Function GetMarkupAttributeAtRowCol(const aRow, aCol : Integer) : TSynSelectedColor; virtual; abstract; Function GetNextMarkupColAfterRowCol(const aRow, aCol : Integer) : Integer; virtual; abstract; + procedure MergeMarkupAttributeAtRowCol(const aRow, aCol, AEndCol : Integer; AMarkup: TSynSelectedColor); virtual; // Notifications about Changes to the text Procedure TextChanged(aFirstCodeLine, aLastCodeLine: Integer); virtual; @@ -148,9 +149,9 @@ type Procedure PrepareMarkupForRow(aRow : Integer); override; Procedure FinishMarkupForRow(aRow : Integer); override; Procedure EndMarkup; override; - Function GetMarkupAttributeAtRowCol(const aRow, aCol : Integer) : TSynSelectedColor; override; - Function GetNextMarkupColAfterRowCol(const aRow, aCol : Integer) : Integer; override; - Procedure MergeMarkupAttributeAtRowCol(const aRow, aCol, AEndCol : Integer; AMarkup: TSynSelectedColor); + Function GetMarkupAttributeAtRowCol(const aRow, aCol : Integer) : TSynSelectedColor; override; + Function GetNextMarkupColAfterRowCol(const aRow, aCol : Integer) : Integer; override; + procedure MergeMarkupAttributeAtRowCol(const aRow, aCol, AEndCol : Integer; AMarkup: TSynSelectedColor); override; // Notifications about Changes to the text Procedure TextChanged(aFirstCodeLine, aLastCodeLine: Integer); override; @@ -358,6 +359,16 @@ procedure TSynEditMarkup.EndMarkup; begin end; +procedure TSynEditMarkup.MergeMarkupAttributeAtRowCol(const aRow, aCol, AEndCol: Integer; + AMarkup: TSynSelectedColor); +var + c: TSynSelectedColor; +begin + c := GetMarkupAttributeAtRowCol(aRow, aCol); + if assigned(c) then + AMarkup.Merge(c, aCol, AEndCol); +end; + procedure TSynEditMarkup.TextChanged(aFirstCodeLine, aLastCodeLine: Integer); begin DoTextChanged(aFirstCodeLine, aLastCodeLine); @@ -483,14 +494,11 @@ procedure TSynEditMarkupManager.MergeMarkupAttributeAtRowCol (const aRow, aCol, AEndCol: Integer; AMarkup: TSynSelectedColor); var i : integer; - c : TSynSelectedColor; begin for i := 0 to fMarkUpList.Count-1 do begin - if not TSynEditMarkup(fMarkUpList[i]).Enabled then continue; - c := TSynEditMarkup(fMarkUpList[i]).GetMarkupAttributeAtRowCol(aRow, aCol); - if assigned(c) then begin - AMarkup.Merge(c, aCol, AEndCol); - end; + if TSynEditMarkup(fMarkUpList[i]).Enabled then + TSynEditMarkup(fMarkUpList[i]).MergeMarkupAttributeAtRowCol + (aRow, aCol, AEndCol, AMarkup); end; end; diff --git a/components/synedit/syneditmarkupguttermark.pp b/components/synedit/syneditmarkupguttermark.pp new file mode 100644 index 0000000000..b1fe8069c6 --- /dev/null +++ b/components/synedit/syneditmarkupguttermark.pp @@ -0,0 +1,160 @@ +{------------------------------------------------------------------------------- +The contents of this file are subject to the Mozilla Public License +Version 1.1 (the "License"); you may not use this file except in compliance +with the License. You may obtain a copy of the License at +http://www.mozilla.org/MPL/ + +Software distributed under the License is distributed on an "AS IS" basis, +WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License for +the specific language governing rights and limitations under the License. + +Alternatively, the contents of this file may be used under the terms of the +GNU General Public License Version 2 or later (the "GPL"), in which case +the provisions of the GPL are applicable instead of those above. +If you wish to allow use of your version of this file only under the terms +of the GPL and not to allow others to use your version of this file +under the MPL, indicate your decision by deleting the provisions above and +replace them with the notice and other provisions required by the GPL. +If you do not delete the provisions above, a recipient may use your version +of this file under either the MPL or the GPL. + +-------------------------------------------------------------------------------} +unit SynEditMarkupGutterMark; + +{$mode objfpc}{$H+} + +interface + +uses + Classes, SysUtils, Graphics, Controls, LCLProc, + SynEditMarkup, SynEditMiscClasses, SynEditMarks; + +type + + TMarkSection = record + StartX, EndX: Integer; // Physical + Priority: Integer; + Markup: TSynSelectedColor; + end; + + { TSynEditMarkupMark } + + TSynEditMarkupMark = class(TSynEditMark) + private + FSourceMarkup: TSynSelectedColor; + public + property SourceMarkup: TSynSelectedColor read FSourceMarkup write FSourceMarkup; + end; + + + { TSynEditMarkupGutterMark } + + TSynEditMarkupGutterMark = class(TSynEditMarkup) + // TODO: subscribe to mark changes for line invalidation => currently done by synedit itself + private + FRowData: Array of TMarkSection; + FWordBreaker: TSynWordBreaker; + protected + procedure DoMarkupChanged(AMarkup: TSynSelectedColor); override; + public + constructor Create(ASynEdit: TSynEditBase; AWordBreaker: TSynWordBreaker); + + procedure PrepareMarkupForRow(ARow: Integer); override; + function GetMarkupAttributeAtRowCol(const ARow, ACol: Integer): TSynSelectedColor; override; + function GetNextMarkupColAfterRowCol(const ARow, ACol: Integer): Integer; override; + //procedure MergeMarkupAttributeAtRowCol(const aRow, aCol, AEndCol : Integer; AMarkup: TSynSelectedColor); override; + end; + +implementation + +uses + SynEdit; + + +{ TSynEditMarkupGutterMark } + +procedure TSynEditMarkupGutterMark.DoMarkupChanged(AMarkup: TSynSelectedColor); +begin + inherited DoMarkupChanged(AMarkup); + SynEdit.Invalidate; +end; + +constructor TSynEditMarkupGutterMark.Create(ASynEdit: TSynEditBase; + AWordBreaker: TSynWordBreaker); +begin + FWordBreaker := AWordBreaker; + inherited Create(ASynEdit); +end; + +procedure TSynEditMarkupGutterMark.PrepareMarkupForRow(ARow: Integer); +var + MLine: TSynEditMarkLine; + i, j: Integer; + s: string; +begin + MLine := TSynEdit(SynEdit).Marks.Line[ARow]; + if MLine = nil then begin + SetLength(FRowData, 0); + exit; + end; + SetLength(FRowData, MLine.Count); + + j := 0; + for i := 0 to MLine.Count - 1 do begin + if not ( (MLine[i] is TSynEditMarkupMark) and + (TSynEditMarkupMark(MLine[i]).SourceMarkup <> nil) ) + then + continue; + + FRowData[i].Markup := TSynEditMarkupMark(MLine[i]).SourceMarkup; + FRowData[i].Priority := MLine[i].Priority; + + s := Lines[MLine[i].Line - 1]; + FRowData[i].StartX := LogicalToPhysicalPos + (Point(FWordBreaker.PrevBoundary(s, MLine[i].Column, True), MLine[i].Line)).x; + FRowData[i].EndX := LogicalToPhysicalPos + (Point(FWordBreaker.NextBoundary(s, MLine[i].Column), MLine[i].Line)).x; + + if (FRowData[i].StartX > 0) and (FRowData[i].EndX > 0) then + inc(j); + end; + + SetLength(FRowData, j); +end; + +function TSynEditMarkupGutterMark.GetMarkupAttributeAtRowCol(const ARow, + ACol: Integer): TSynSelectedColor; +var + i, FoundPri: Integer; +begin + FoundPri := 0; + for i := 0 to length(FRowData) - 1 do begin + if (FRowData[i].StartX <= ACol) and (FRowData[i].EndX > ACol) and + ( (FRowData[i].Priority < FoundPri) or (i = 0) ) + then begin + Result := FRowData[i].Markup; + Result.StartX := FRowData[i].StartX; + Result.EndX := FRowData[i].EndX-1; + FoundPri := FRowData[i].Priority; + end; + end; +end; + +function TSynEditMarkupGutterMark.GetNextMarkupColAfterRowCol(const ARow, + ACol: Integer): Integer; +var + i: Integer; +begin + Result := -1; + if length(FRowData) = 0 then + exit; + for i := 0 to length(FRowData) - 1 do begin + if FRowData[i].StartX < Result then + Result := FRowData[0].StartX;; + if FRowData[i].EndX < Result then + Result := FRowData[0].EndX;; + end; +end; + +end. + diff --git a/components/synedit/syneditmiscclasses.pp b/components/synedit/syneditmiscclasses.pp index fd2baf4050..63460044ad 100644 --- a/components/synedit/syneditmiscclasses.pp +++ b/components/synedit/syneditmiscclasses.pp @@ -68,6 +68,7 @@ type procedure Reset; // aX is the position between the chars (as in CaretX) + // 1 is in front of the first char function IsInWord (aLine: String; aX: Integer ): Boolean; function IsAtWordStart(aLine: String; aX: Integer): Boolean; @@ -82,6 +83,8 @@ type aIncludeCurrent: Boolean = False): Integer; function NextBoundary (aLine: String; aX: Integer): Integer; + function PrevBoundary (aLine: String; aX: Integer; + aIncludeCurrent: Boolean = False): Integer; property IdentChars: TSynIdentChars read FIdentChars write SetIdentChars; property WordChars: TSynIdentChars read FWordChars; @@ -1467,6 +1470,25 @@ begin Result := aX; end; +function TSynWordBreaker.PrevBoundary(aLine: String; aX: Integer; + aIncludeCurrent: Boolean): Integer; +var + len: Integer; +begin + len := Length(aLine); + if not aIncludeCurrent then dec(ax); + if (aX < 1) or (aX > len) then exit(-1); + + if (aLine[aX] in FWordChars) then + while (aX > 1) and (aLine[aX] in FWordChars) do dec(ax) + else + if (aLine[aX] in FWordBreakChars) then + while (aX > 1) and (aLine[aX] in FWordBreakChars) do dec(ax) + else + while (aX > 1) and (aLine[aX] in FWhiteChars) do dec(ax); + Result := aX + 1; +end; + { TSynMethodList } function TSynMethodList.IndexToObjectIndex(const AnObject: TObject; AnIndex: Integer): integer;