add dynamic linting, close #166

This commit is contained in:
Basile Burg 2017-07-07 14:59:04 +02:00
parent f725da9f5c
commit 629413662b
No known key found for this signature in database
GPG Key ID: 1868039F415CB8CF
1 changed files with 270 additions and 25 deletions

View File

@ -13,7 +13,8 @@ uses
//SynEditMarkupFoldColoring, //SynEditMarkupFoldColoring,
Clipbrd, fpjson, jsonparser, LazUTF8, LazUTF8Classes, Buttons, StdCtrls, Clipbrd, fpjson, jsonparser, LazUTF8, LazUTF8Classes, Buttons, StdCtrls,
ce_common, ce_writableComponent, ce_d2syn, ce_txtsyn, ce_dialogs, ce_dastworx, 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 type
@ -116,6 +117,26 @@ type
procedure next; procedure next;
end; 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; TSortDialog = class;
TGutterIcon = ( TGutterIcon = (
@ -125,9 +146,14 @@ type
giBreak = 3, // break point reached giBreak = 3, // break point reached
giStep = 4, // step / signal / pause giStep = 4, // step / signal / pause
giWatch = 5, // watch point reached 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 //TODO-cGDB: add a system allowing to define watch points
TCESynMemo = class(TSynEdit, ICEDebugObserver) TCESynMemo = class(TSynEdit, ICEDebugObserver)
@ -151,8 +177,10 @@ type
fDDocWin: TCEEditorHintWindow; fDDocWin: TCEEditorHintWindow;
fDDocDelay: Integer; fDDocDelay: Integer;
fAutoDotDelay: Integer; fAutoDotDelay: Integer;
fDscannerDelay: Integer;
fDDocTimer: TIdleTimer; fDDocTimer: TIdleTimer;
fAutoDotTimer: TIdleTimer; fAutoDotTimer: TIdleTimer;
fDscannerTimer: TIdleTimer;
fCanShowHint: boolean; fCanShowHint: boolean;
fCanAutoDot: boolean; fCanAutoDot: boolean;
fOldMousePos: TPoint; fOldMousePos: TPoint;
@ -187,6 +215,12 @@ type
fCloseCompletionChars: TSysCharSet; fCloseCompletionChars: TSysCharSet;
fCompletionMenuAutoClose: boolean; fCompletionMenuAutoClose: boolean;
fTransparentGutter: boolean; fTransparentGutter: boolean;
fDscanner: TCEProcess;
fDscannerResults: TDscannerResults;
fCanDscan: boolean;
fKnowsDscanner: boolean;
fDscannerEnabled: boolean;
procedure showHintEvent(Sender: TObject; HintInfo: PHintInfo);
procedure setGutterTransparent(value: boolean); procedure setGutterTransparent(value: boolean);
procedure decCallTipsLvl; procedure decCallTipsLvl;
procedure setMatchOpts(value: TIdentifierMatchOptions); procedure setMatchOpts(value: TIdentifierMatchOptions);
@ -199,6 +233,10 @@ type
procedure setDefaultFontSize(value: Integer); procedure setDefaultFontSize(value: Integer);
procedure DDocTimerEvent(sender: TObject); procedure DDocTimerEvent(sender: TObject);
procedure AutoDotTimerEvent(sender: TObject); procedure AutoDotTimerEvent(sender: TObject);
procedure dscannerTimerEvent(sender: TObject);
procedure dscannerTerminate(sender: TObject);
procedure removeDscannerWarnings;
function getDscannerWarning(line: integer): string;
procedure InitHintWins; procedure InitHintWins;
function getIfTemp: boolean; function getIfTemp: boolean;
procedure setDDocDelay(value: Integer); procedure setDDocDelay(value: Integer);
@ -221,7 +259,8 @@ type
procedure setSelectionOrWordCase(upper: boolean); procedure setSelectionOrWordCase(upper: boolean);
procedure sortSelectedLines(descending, caseSensitive: boolean); procedure sortSelectedLines(descending, caseSensitive: boolean);
procedure tokFoundForCaption(const token: PLexToken; out stop: 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 patchClipboardIndentation;
// //
procedure gutterClick(Sender: TObject; X, Y, Line: integer; mark: TSynEditMark); procedure gutterClick(Sender: TObject; X, Y, Line: integer; mark: TSynEditMark);
@ -287,6 +326,7 @@ type
procedure insertDdocTemplate; procedure insertDdocTemplate;
function implementMain: THasMain; function implementMain: THasMain;
procedure replaceUndoableContent(const value: string); procedure replaceUndoableContent(const value: string);
procedure setDscannerOptions(dsEnabled: boolean; dsDelay: integer);
// //
property IdentifierMatchOptions: TIdentifierMatchOptions read fMatchOpts write setMatchOpts; property IdentifierMatchOptions: TIdentifierMatchOptions read fMatchOpts write setMatchOpts;
property Identifier: string read fIdentifier; property Identifier: string read fIdentifier;
@ -711,6 +751,7 @@ constructor TCESynMemo.Create(aOwner: TComponent);
begin begin
inherited; inherited;
OnShowHint:= @showHintEvent;
OnStatusChange:= @handleStatusChanged; OnStatusChange:= @handleStatusChanged;
fDefaultFontSize := 10; fDefaultFontSize := 10;
Font.Size:=10; Font.Size:=10;
@ -735,6 +776,21 @@ begin
fAutoDotTimer.Interval := fAutoDotDelay; fAutoDotTimer.Interval := fAutoDotDelay;
fAutoDotTimer.OnTimer := @AutoDotTimerEvent; 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.ShowOnlyLineNumbersMultiplesOf := 5;
Gutter.LineNumberPart.MarkupInfo.Foreground := clWindowText; Gutter.LineNumberPart.MarkupInfo.Foreground := clWindowText;
Gutter.LineNumberPart.MarkupInfo.Background := clBtnFace; Gutter.LineNumberPart.MarkupInfo.Background := clBtnFace;
@ -788,6 +844,7 @@ begin
fImages.AddResourceName(HINSTANCE, 'BREAKS'); fImages.AddResourceName(HINSTANCE, 'BREAKS');
fImages.AddResourceName(HINSTANCE, 'STEP'); fImages.AddResourceName(HINSTANCE, 'STEP');
fImages.AddResourceName(HINSTANCE, 'CAMERA_GO'); fImages.AddResourceName(HINSTANCE, 'CAMERA_GO');
fImages.AddResourceName(HINSTANCE, 'WARNING');
fBreakPoints := TFPList.Create; fBreakPoints := TFPList.Create;
fPositions := TCESynMemoPositions.create(self); fPositions := TCESynMemoPositions.create(self);
@ -830,6 +887,7 @@ begin
fLexToks.Clear; fLexToks.Clear;
fLexToks.Free; fLexToks.Free;
fSortDialog.Free; fSortDialog.Free;
fDscannerResults.Free;
if fTempFileName.fileExists then if fTempFileName.fileExists then
sysutils.DeleteFile(fTempFileName); sysutils.DeleteFile(fTempFileName);
@ -2200,6 +2258,141 @@ begin
end; end;
{$ENDREGION --------------------------------------------------------------------} {$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 ----------------------------------------------------} {$REGION Coedit memo things ----------------------------------------------------}
procedure TCESynMemo.handleStatusChanged(Sender: TObject; Changes: TSynStatusChanges); procedure TCESynMemo.handleStatusChanged(Sender: TObject; Changes: TSynStatusChanges);
begin begin
@ -2391,6 +2584,7 @@ begin
end; end;
end; end;
subjDocChanged(TCEMultiDocSubject(fMultiDocSubject), self); subjDocChanged(TCEMultiDocSubject(fMultiDocSubject), self);
fCanDscan := true;
end; end;
procedure TCESynMemo.saveToFile(const fname: string); procedure TCESynMemo.saveToFile(const fname: string);
@ -2617,13 +2811,17 @@ var
lxd: boolean; lxd: boolean;
begin begin
case Key of case Key of
VK_BACK: if fCallTipWin.Visible and (CaretX > 1) VK_BACK:
and (LineText[LogicalCaretXY.X-1] = '(') then begin
decCallTipsLvl; fCanDscan:=true;
if fCallTipWin.Visible and (CaretX > 1)
and (LineText[LogicalCaretXY.X-1] = '(') then
decCallTipsLvl;
end;
VK_RETURN: VK_RETURN:
begin begin
fCanDscan:=true;
line := LineText; line := LineText;
case fAutoCloseCurlyBrace of case fAutoCloseCurlyBrace of
autoCloseOnNewLineAlways: if (CaretX > 1) and (line[LogicalCaretXY.X - 1] = '{') then autoCloseOnNewLineAlways: if (CaretX > 1) and (line[LogicalCaretXY.X - 1] = '{') then
begin begin
@ -2721,6 +2919,7 @@ var
begin begin
c := Key; c := Key;
inherited; inherited;
fCanDscan := true;
case c of case c of
#39: if autoCloseSingleQuote in fAutoClosedPairs then #39: if autoCloseSingleQuote in fAutoClosedPairs then
autoClosePair(autoCloseSingleQuote); autoClosePair(autoCloseSingleQuote);
@ -2838,7 +3037,7 @@ procedure TCESynMemo.addBreakPoint(line: integer);
begin begin
if findBreakPoint(line) then if findBreakPoint(line) then
exit; exit;
setGutterIcon(line, giBulletRed); addGutterIcon(line, giBulletRed);
{$PUSH}{$WARNINGS OFF}{$HINTS OFF} {$PUSH}{$WARNINGS OFF}{$HINTS OFF}
fBreakPoints.Add(pointer(line)); fBreakPoints.Add(pointer(line));
{$POP} {$POP}
@ -2850,7 +3049,7 @@ procedure TCESynMemo.removeBreakPoint(line: integer);
begin begin
if not findBreakPoint(line) then if not findBreakPoint(line) then
exit; exit;
setGutterIcon(line, giNone); removeGutterIcon(line, giBulletRed);
{$PUSH}{$WARNINGS OFF}{$HINTS OFF} {$PUSH}{$WARNINGS OFF}{$HINTS OFF}
fBreakPoints.Remove(pointer(line)); fBreakPoints.Remove(pointer(line));
{$POP} {$POP}
@ -2858,15 +3057,37 @@ begin
fDebugger.removeBreakPoint(fFilename, line); fDebugger.removeBreakPoint(fFilename, line);
end; 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; procedure TCESynMemo.removeDebugTimeMarks;
var var
i: integer; i: integer;
begin begin
for i:= 0 to Lines.Count-1 do for i:= marks.Count-1 downto 0 do
begin begin
Marks.ClearLine(i); if TGutterIcon(Marks.Items[i].ImageIndex) in debugTimeGutterIcons then
if findBreakPoint(i) then Marks.Delete(i);
setGutterIcon(i, giBulletRed);
end; end;
end; end;
@ -2887,22 +3108,46 @@ begin
EnsureCursorPosVisible; EnsureCursorPosVisible;
end; end;
procedure TCESynMemo.setGutterIcon(line: integer; value: TGutterIcon); procedure TCESynMemo.addGutterIcon(line: integer; value: TGutterIcon);
var var
m: TSynEditMark; m: TSynEditMarkLine;
n: TSynEditMark;
i: integer;
begin 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 if value <> giNone then
begin begin
m:= TSynEditMark.Create(self); n:= TSynEditMark.Create(self);
m.Line := line; n.Line := line;
m.ImageList := fImages; n.ImageList := fImages;
m.ImageIndex := longint(value); n.ImageIndex := longint(value);
m.Visible := true; n.Visible := true;
Marks.Add(m); Marks.Add(n);
end; end;
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); procedure TCESynMemo.debugStart(debugger: ICEDebugger);
begin begin
fDebugger := debugger; fDebugger := debugger;
@ -2942,9 +3187,9 @@ begin
EnsureCursorPosVisible; EnsureCursorPosVisible;
removeDebugTimeMarks; removeDebugTimeMarks;
case reason of case reason of
dbBreakPoint: setGutterIcon(line, giBreak); dbBreakPoint: addGutterIcon(line, giBreak);
dbStep, dbSignal: setGutterIcon(line, giStep); dbStep, dbSignal: addGutterIcon(line, giStep);
dbWatch: setGutterIcon(line, giWatch); dbWatch: addGutterIcon(line, giWatch);
end; end;
end; end;
{$ENDREGION --------------------------------------------------------------------} {$ENDREGION --------------------------------------------------------------------}