mirror of https://gitlab.com/basile.b/dexed.git
add dynamic linting, close #166
This commit is contained in:
parent
f725da9f5c
commit
629413662b
|
@ -13,7 +13,8 @@ uses
|
|||
//SynEditMarkupFoldColoring,
|
||||
Clipbrd, fpjson, jsonparser, LazUTF8, LazUTF8Classes, Buttons, StdCtrls,
|
||||
ce_common, ce_writableComponent, ce_d2syn, ce_txtsyn, ce_dialogs, ce_dastworx,
|
||||
ce_sharedres, ce_dlang, ce_stringrange, ce_dbgitf, ce_observer, ce_diff;
|
||||
ce_sharedres, ce_dlang, ce_stringrange, ce_dbgitf, ce_observer, ce_diff,
|
||||
ce_processes;
|
||||
|
||||
type
|
||||
|
||||
|
@ -116,6 +117,26 @@ type
|
|||
procedure next;
|
||||
end;
|
||||
|
||||
PDscannerResult = ^TDscannerResult;
|
||||
TDscannerResult = record
|
||||
warning: string;
|
||||
line, column: integer;
|
||||
end;
|
||||
|
||||
TDscannerResults = class
|
||||
private
|
||||
fList: TFPList;
|
||||
function getItem(index: integer): PDscannerResult;
|
||||
function getCount: integer;
|
||||
public
|
||||
constructor create;
|
||||
destructor destroy; override;
|
||||
procedure clear;
|
||||
procedure push(const warning: string; line, column: integer);
|
||||
property count: integer read getCount;
|
||||
property item[index: integer]: PDscannerResult read getItem; default;
|
||||
end;
|
||||
|
||||
TSortDialog = class;
|
||||
|
||||
TGutterIcon = (
|
||||
|
@ -125,9 +146,14 @@ type
|
|||
giBreak = 3, // break point reached
|
||||
giStep = 4, // step / signal / pause
|
||||
giWatch = 5, // watch point reached
|
||||
giNone = high(byte) // remove
|
||||
giWarn = 6, // Dscanner result with text hint
|
||||
giNone = high(byte) //
|
||||
);
|
||||
|
||||
const debugTimeGutterIcons = [giBreak, giStep, giWatch];
|
||||
|
||||
type
|
||||
|
||||
//TODO-cGDB: add a system allowing to define watch points
|
||||
|
||||
TCESynMemo = class(TSynEdit, ICEDebugObserver)
|
||||
|
@ -151,8 +177,10 @@ type
|
|||
fDDocWin: TCEEditorHintWindow;
|
||||
fDDocDelay: Integer;
|
||||
fAutoDotDelay: Integer;
|
||||
fDscannerDelay: Integer;
|
||||
fDDocTimer: TIdleTimer;
|
||||
fAutoDotTimer: TIdleTimer;
|
||||
fDscannerTimer: TIdleTimer;
|
||||
fCanShowHint: boolean;
|
||||
fCanAutoDot: boolean;
|
||||
fOldMousePos: TPoint;
|
||||
|
@ -187,6 +215,12 @@ type
|
|||
fCloseCompletionChars: TSysCharSet;
|
||||
fCompletionMenuAutoClose: boolean;
|
||||
fTransparentGutter: boolean;
|
||||
fDscanner: TCEProcess;
|
||||
fDscannerResults: TDscannerResults;
|
||||
fCanDscan: boolean;
|
||||
fKnowsDscanner: boolean;
|
||||
fDscannerEnabled: boolean;
|
||||
procedure showHintEvent(Sender: TObject; HintInfo: PHintInfo);
|
||||
procedure setGutterTransparent(value: boolean);
|
||||
procedure decCallTipsLvl;
|
||||
procedure setMatchOpts(value: TIdentifierMatchOptions);
|
||||
|
@ -199,6 +233,10 @@ type
|
|||
procedure setDefaultFontSize(value: Integer);
|
||||
procedure DDocTimerEvent(sender: TObject);
|
||||
procedure AutoDotTimerEvent(sender: TObject);
|
||||
procedure dscannerTimerEvent(sender: TObject);
|
||||
procedure dscannerTerminate(sender: TObject);
|
||||
procedure removeDscannerWarnings;
|
||||
function getDscannerWarning(line: integer): string;
|
||||
procedure InitHintWins;
|
||||
function getIfTemp: boolean;
|
||||
procedure setDDocDelay(value: Integer);
|
||||
|
@ -221,7 +259,8 @@ type
|
|||
procedure setSelectionOrWordCase(upper: boolean);
|
||||
procedure sortSelectedLines(descending, caseSensitive: boolean);
|
||||
procedure tokFoundForCaption(const token: PLexToken; out stop: boolean);
|
||||
procedure setGutterIcon(line: integer; value: TGutterIcon);
|
||||
procedure addGutterIcon(line: integer; value: TGutterIcon);
|
||||
procedure removeGutterIcon(line: integer; value: TGutterIcon);
|
||||
procedure patchClipboardIndentation;
|
||||
//
|
||||
procedure gutterClick(Sender: TObject; X, Y, Line: integer; mark: TSynEditMark);
|
||||
|
@ -287,6 +326,7 @@ type
|
|||
procedure insertDdocTemplate;
|
||||
function implementMain: THasMain;
|
||||
procedure replaceUndoableContent(const value: string);
|
||||
procedure setDscannerOptions(dsEnabled: boolean; dsDelay: integer);
|
||||
//
|
||||
property IdentifierMatchOptions: TIdentifierMatchOptions read fMatchOpts write setMatchOpts;
|
||||
property Identifier: string read fIdentifier;
|
||||
|
@ -711,6 +751,7 @@ constructor TCESynMemo.Create(aOwner: TComponent);
|
|||
begin
|
||||
inherited;
|
||||
|
||||
OnShowHint:= @showHintEvent;
|
||||
OnStatusChange:= @handleStatusChanged;
|
||||
fDefaultFontSize := 10;
|
||||
Font.Size:=10;
|
||||
|
@ -735,6 +776,21 @@ begin
|
|||
fAutoDotTimer.Interval := fAutoDotDelay;
|
||||
fAutoDotTimer.OnTimer := @AutoDotTimerEvent;
|
||||
|
||||
fDscannerDelay := 500;
|
||||
fDscannerTimer := TIdleTimer.Create(self);
|
||||
fDscannerTimer.AutoEnabled:=true;
|
||||
fDscannerTimer.Interval := fDscannerDelay;
|
||||
fDscannerTimer.OnTimer := @dscannerTimerEvent;
|
||||
fDscanner := TCEProcess.create(self);
|
||||
fDscanner.Executable:= exeFullName('dscanner' + exeExt);
|
||||
fDscanner.Options:=[poUsePipes];
|
||||
fDscanner.ShowWindow:=swoHIDE;
|
||||
fDscanner.OnTerminate:=@dscannerTerminate;
|
||||
fDscanner.Parameters.add('-S');
|
||||
fDscanner.Parameters.add('stdin');
|
||||
fDscannerResults:= TDscannerResults.create;
|
||||
fKnowsDscanner := fDscanner.Executable.fileExists;
|
||||
|
||||
Gutter.LineNumberPart.ShowOnlyLineNumbersMultiplesOf := 5;
|
||||
Gutter.LineNumberPart.MarkupInfo.Foreground := clWindowText;
|
||||
Gutter.LineNumberPart.MarkupInfo.Background := clBtnFace;
|
||||
|
@ -788,6 +844,7 @@ begin
|
|||
fImages.AddResourceName(HINSTANCE, 'BREAKS');
|
||||
fImages.AddResourceName(HINSTANCE, 'STEP');
|
||||
fImages.AddResourceName(HINSTANCE, 'CAMERA_GO');
|
||||
fImages.AddResourceName(HINSTANCE, 'WARNING');
|
||||
fBreakPoints := TFPList.Create;
|
||||
|
||||
fPositions := TCESynMemoPositions.create(self);
|
||||
|
@ -830,6 +887,7 @@ begin
|
|||
fLexToks.Clear;
|
||||
fLexToks.Free;
|
||||
fSortDialog.Free;
|
||||
fDscannerResults.Free;
|
||||
|
||||
if fTempFileName.fileExists then
|
||||
sysutils.DeleteFile(fTempFileName);
|
||||
|
@ -2200,6 +2258,141 @@ begin
|
|||
end;
|
||||
{$ENDREGION --------------------------------------------------------------------}
|
||||
|
||||
{$REGION Dscanner --------------------------------------------------------------}
|
||||
constructor TDscannerResults.create;
|
||||
begin
|
||||
fList := TFPList.Create;
|
||||
end;
|
||||
|
||||
destructor TDscannerResults.destroy;
|
||||
begin
|
||||
clear;
|
||||
fList.Free;
|
||||
inherited;
|
||||
end;
|
||||
|
||||
procedure TDscannerResults.clear;
|
||||
var
|
||||
i: integer;
|
||||
begin
|
||||
for i:= 0 to fList.Count-1 do
|
||||
dispose(PDscannerResult(fList[i]));
|
||||
fList.Clear
|
||||
end;
|
||||
|
||||
procedure TDscannerResults.push(const warning: string; line, column: integer);
|
||||
var
|
||||
r: PDscannerResult;
|
||||
begin
|
||||
r := new(PDscannerResult);
|
||||
r^.column:=column;
|
||||
r^.warning:=warning;
|
||||
r^.line:=line;
|
||||
fList.Add(r);
|
||||
end;
|
||||
|
||||
function TDscannerResults.getCount: integer;
|
||||
begin
|
||||
result := fList.Count;
|
||||
end;
|
||||
|
||||
function TDscannerResults.getItem(index: integer): PDscannerResult;
|
||||
begin
|
||||
result := PDscannerResult(fList[index]);
|
||||
end;
|
||||
|
||||
procedure TCESynMemo.setDscannerOptions(dsEnabled: boolean; dsDelay: integer);
|
||||
begin
|
||||
fDscannerTimer.Interval:=dsDelay;
|
||||
fDscannerEnabled := dsEnabled;
|
||||
if not dsEnabled then
|
||||
begin
|
||||
removeDscannerWarnings;
|
||||
fDscannerResults.clear;
|
||||
end
|
||||
else dscannerTimerEvent(nil);
|
||||
end;
|
||||
|
||||
procedure TCESynMemo.dscannerTimerEvent(sender: TObject);
|
||||
var
|
||||
s: string;
|
||||
begin
|
||||
if not fDscannerEnabled or not fKnowsDscanner or not isDSource
|
||||
or not fCanDscan then
|
||||
exit;
|
||||
|
||||
fDscannerResults.clear;
|
||||
removeDscannerWarnings;
|
||||
Repaint();
|
||||
fCanDscan := false;
|
||||
fDScanner.execute;
|
||||
s := Lines.strictText;
|
||||
if s.length > 0 then
|
||||
fDscanner.Input.Write(s[1], s.length);
|
||||
fDscanner.CloseInput;
|
||||
end;
|
||||
|
||||
procedure TCESynMemo.dscannerTerminate(sender: TObject);
|
||||
procedure processLine(const lne: string);
|
||||
var
|
||||
r: TStringRange = (ptr:nil; pos:0; len: 0);
|
||||
line: integer;
|
||||
column: integer;
|
||||
begin
|
||||
if lne.isBlank then
|
||||
exit;
|
||||
r.init(lne);
|
||||
line := r.popUntil('(')^.popFront^.takeWhile(['0'..'9']).yield.toIntNoExcept();
|
||||
column := r.popFront^.takeWhile(['0'..'9']).yield.toIntNoExcept();
|
||||
r.popUntil(':');
|
||||
r.popFront;
|
||||
fDscannerResults.push(r.takeUntil(#0).yield, line, column);
|
||||
|
||||
addGutterIcon(line, giWarn);
|
||||
|
||||
end;
|
||||
var
|
||||
i: integer;
|
||||
s: string;
|
||||
m: TStringList;
|
||||
begin
|
||||
removeDscannerWarnings;
|
||||
m := TStringList.Create;
|
||||
try
|
||||
fDscanner.getFullLines(m);
|
||||
for i := 0 to m.Count-1 do
|
||||
begin
|
||||
s := m[i];
|
||||
processLine(s);
|
||||
end;
|
||||
finally
|
||||
m.free;
|
||||
end;
|
||||
end;
|
||||
|
||||
procedure TCESynMemo.removeDscannerWarnings;
|
||||
var
|
||||
i: integer;
|
||||
begin
|
||||
for i:= Marks.Count-1 downto 0 do
|
||||
if marks.Items[i].ImageIndex = longint(giWarn) then
|
||||
marks.Delete(i);
|
||||
repaint;
|
||||
end;
|
||||
|
||||
function TCESynMemo.getDscannerWarning(line: integer): string;
|
||||
const
|
||||
spec = '@column %d: %s' + LineEnding;
|
||||
var
|
||||
i: integer;
|
||||
begin
|
||||
result := '';
|
||||
for i := 0 to fDscannerResults.count-1 do
|
||||
if fDscannerResults[i]^.line = line then
|
||||
result += format(spec, [fDscannerResults[i]^.column, fDscannerResults[i]^.warning]);
|
||||
end;
|
||||
{$ENDREGION --------------------------------------------------------------------}
|
||||
|
||||
{$REGION Coedit memo things ----------------------------------------------------}
|
||||
procedure TCESynMemo.handleStatusChanged(Sender: TObject; Changes: TSynStatusChanges);
|
||||
begin
|
||||
|
@ -2391,6 +2584,7 @@ begin
|
|||
end;
|
||||
end;
|
||||
subjDocChanged(TCEMultiDocSubject(fMultiDocSubject), self);
|
||||
fCanDscan := true;
|
||||
end;
|
||||
|
||||
procedure TCESynMemo.saveToFile(const fname: string);
|
||||
|
@ -2617,13 +2811,17 @@ var
|
|||
lxd: boolean;
|
||||
begin
|
||||
case Key of
|
||||
VK_BACK: if fCallTipWin.Visible and (CaretX > 1)
|
||||
and (LineText[LogicalCaretXY.X-1] = '(') then
|
||||
decCallTipsLvl;
|
||||
VK_BACK:
|
||||
begin
|
||||
fCanDscan:=true;
|
||||
if fCallTipWin.Visible and (CaretX > 1)
|
||||
and (LineText[LogicalCaretXY.X-1] = '(') then
|
||||
decCallTipsLvl;
|
||||
end;
|
||||
VK_RETURN:
|
||||
begin
|
||||
fCanDscan:=true;
|
||||
line := LineText;
|
||||
|
||||
case fAutoCloseCurlyBrace of
|
||||
autoCloseOnNewLineAlways: if (CaretX > 1) and (line[LogicalCaretXY.X - 1] = '{') then
|
||||
begin
|
||||
|
@ -2721,6 +2919,7 @@ var
|
|||
begin
|
||||
c := Key;
|
||||
inherited;
|
||||
fCanDscan := true;
|
||||
case c of
|
||||
#39: if autoCloseSingleQuote in fAutoClosedPairs then
|
||||
autoClosePair(autoCloseSingleQuote);
|
||||
|
@ -2838,7 +3037,7 @@ procedure TCESynMemo.addBreakPoint(line: integer);
|
|||
begin
|
||||
if findBreakPoint(line) then
|
||||
exit;
|
||||
setGutterIcon(line, giBulletRed);
|
||||
addGutterIcon(line, giBulletRed);
|
||||
{$PUSH}{$WARNINGS OFF}{$HINTS OFF}
|
||||
fBreakPoints.Add(pointer(line));
|
||||
{$POP}
|
||||
|
@ -2850,7 +3049,7 @@ procedure TCESynMemo.removeBreakPoint(line: integer);
|
|||
begin
|
||||
if not findBreakPoint(line) then
|
||||
exit;
|
||||
setGutterIcon(line, giNone);
|
||||
removeGutterIcon(line, giBulletRed);
|
||||
{$PUSH}{$WARNINGS OFF}{$HINTS OFF}
|
||||
fBreakPoints.Remove(pointer(line));
|
||||
{$POP}
|
||||
|
@ -2858,15 +3057,37 @@ begin
|
|||
fDebugger.removeBreakPoint(fFilename, line);
|
||||
end;
|
||||
|
||||
procedure TCESynMemo.showHintEvent(Sender: TObject; HintInfo: PHintInfo);
|
||||
var
|
||||
p: TPoint;
|
||||
s: string;
|
||||
begin
|
||||
if cursor <> crDefault then
|
||||
exit;
|
||||
p := ScreenToClient(mouse.CursorPos);
|
||||
if p.x > Gutter.Width then
|
||||
exit;
|
||||
|
||||
p := self.PixelsToRowColumn(p);
|
||||
s := getDscannerWarning(p.y);
|
||||
if s.isNotEmpty then
|
||||
begin
|
||||
s := 'Warning(s):' + LineEnding + s;
|
||||
fDDocWin.FontSize := Font.Size;
|
||||
fDDocWin.HintRect := fDDocWin.CalcHintRect(0, s, nil);
|
||||
fDDocWin.OffsetHintRect(mouse.CursorPos, Font.Size);
|
||||
fDDocWin.ActivateHint(fDDocWin.HintRect, s);
|
||||
end;
|
||||
end;
|
||||
|
||||
procedure TCESynMemo.removeDebugTimeMarks;
|
||||
var
|
||||
i: integer;
|
||||
begin
|
||||
for i:= 0 to Lines.Count-1 do
|
||||
for i:= marks.Count-1 downto 0 do
|
||||
begin
|
||||
Marks.ClearLine(i);
|
||||
if findBreakPoint(i) then
|
||||
setGutterIcon(i, giBulletRed);
|
||||
if TGutterIcon(Marks.Items[i].ImageIndex) in debugTimeGutterIcons then
|
||||
Marks.Delete(i);
|
||||
end;
|
||||
end;
|
||||
|
||||
|
@ -2887,22 +3108,46 @@ begin
|
|||
EnsureCursorPosVisible;
|
||||
end;
|
||||
|
||||
procedure TCESynMemo.setGutterIcon(line: integer; value: TGutterIcon);
|
||||
procedure TCESynMemo.addGutterIcon(line: integer; value: TGutterIcon);
|
||||
var
|
||||
m: TSynEditMark;
|
||||
m: TSynEditMarkLine;
|
||||
n: TSynEditMark;
|
||||
i: integer;
|
||||
begin
|
||||
Marks.ClearLine(line);
|
||||
m := Marks.Line[line];
|
||||
if m.isNotNil then
|
||||
for i := 0 to m.Count-1 do
|
||||
if m.Items[i].ImageIndex = longint(value) then
|
||||
exit;
|
||||
|
||||
if value <> giNone then
|
||||
begin
|
||||
m:= TSynEditMark.Create(self);
|
||||
m.Line := line;
|
||||
m.ImageList := fImages;
|
||||
m.ImageIndex := longint(value);
|
||||
m.Visible := true;
|
||||
Marks.Add(m);
|
||||
n:= TSynEditMark.Create(self);
|
||||
n.Line := line;
|
||||
n.ImageList := fImages;
|
||||
n.ImageIndex := longint(value);
|
||||
n.Visible := true;
|
||||
Marks.Add(n);
|
||||
end;
|
||||
end;
|
||||
|
||||
procedure TCESynMemo.removeGutterIcon(line: integer; value: TGutterIcon);
|
||||
var
|
||||
m: TSynEditMarkLine;
|
||||
n: TSynEditMark;
|
||||
i: integer;
|
||||
begin
|
||||
m := Marks.Line[line];
|
||||
if m.isNotNil then
|
||||
for i := m.Count-1 downto 0 do
|
||||
begin
|
||||
n := m.Items[i];
|
||||
if n.ImageIndex = longint(value) then
|
||||
m.Delete(i);
|
||||
end;
|
||||
Repaint;
|
||||
end;
|
||||
|
||||
procedure TCESynMemo.debugStart(debugger: ICEDebugger);
|
||||
begin
|
||||
fDebugger := debugger;
|
||||
|
@ -2942,9 +3187,9 @@ begin
|
|||
EnsureCursorPosVisible;
|
||||
removeDebugTimeMarks;
|
||||
case reason of
|
||||
dbBreakPoint: setGutterIcon(line, giBreak);
|
||||
dbStep, dbSignal: setGutterIcon(line, giStep);
|
||||
dbWatch: setGutterIcon(line, giWatch);
|
||||
dbBreakPoint: addGutterIcon(line, giBreak);
|
||||
dbStep, dbSignal: addGutterIcon(line, giStep);
|
||||
dbWatch: addGutterIcon(line, giWatch);
|
||||
end;
|
||||
end;
|
||||
{$ENDREGION --------------------------------------------------------------------}
|
||||
|
|
Loading…
Reference in New Issue