[no repro] ATSynEdit: TFPSList.CheckIndex error

Solved bugs are moved into this topic...
ruma1974
Posts: 11
Joined: 23.08.2019 16:12

[no repro] ATSynEdit: TFPSList.CheckIndex error

Post by ruma1974 »

TFPSList.CheckIndex(AIndex : Integer); ATSynEdit_fgl threw an error

I am not able back track it. I searched for the problem and there was an indexed web page on this problem but the web page have been deleted. From the Google search it suggested that this error can have something to do with undo redo.

Any idea what could cause such an error?

Thanks,

Rune
Alexey
Posts: 1633
Joined: 05.10.2012 22:10

Post by Alexey »

First tell us how to reproduce it? in CudaText or in ATSynEdit demo?
ruma1974
Posts: 11
Joined: 23.08.2019 16:12

Post by ruma1974 »

Sorry for taking a long time to reply. I had to make a simple example that reproduce the error which toke me some time since I was not sure what caused the error. I am now rather confident that the problem is related to the lexers. I made the below program using the components from CudaText. Note it is possible that I am not using the components correctly. For the below program, I have a directory in the program folder named "lex" with the Lexers from CudaText. I then open four files (three small R files and one big R file of 5000 lines) and then move up and down in the listbox to change the code. After a short while "procedure TFPSList.CheckIndex(AIndex : Integer);" is triggered. If I do not load a big R file then I have not been able to trigger "procedure TFPSList.CheckIndex(AIndex : Integer);". However, it might be that this just requires more time for the error to appear. The error can be reproduced with different big R files typically above 3000 lines.

Thanks,

Rune

Code: Select all

unit main;

{$mode objfpc}{$H+}

interface

uses
  Classes, SysUtils, Forms, Controls, Graphics, Dialogs, StdCtrls, ExtCtrls,
  Menus, ComCtrls,LazFileUtils, IniFiles,
  ATSynEdit,ec_SyntAnal,FileUtil,proc_globdata,ec_LexerList,proc_lexer_styles,ATSynEdit_Adapter_EControl,ATSynEdit_Gutter_Decor;

type

  { TForm1 }

  TForm1 = class(TForm)
    btnStop: TButton;
    ed: TATSynEdit;
    GroupBox1: TGroupBox;
    GroupBox2: TGroupBox;
    GroupBox3: TGroupBox;
    Label1: TLabel;
    Label2: TLabel;
    ListBox1: TListBox;
    MainMenu1: TMainMenu;
    MenuItem1: TMenuItem;
    MenuItem2: TMenuItem;
    OpenDialog1: TOpenDialog;
    progress: TProgressBar;
    Splitter1: TSplitter;
    Splitter2: TSplitter;
    Splitter3: TSplitter;
    StatusMsg: TStatusBar;
    procedure edChange(Sender: TObject);
    procedure edChangeCaretPos(Sender: TObject);
    procedure FormCreate(Sender: TObject);
    procedure ListBox1Click(Sender: TObject);
    procedure MenuItem2Click(Sender: TObject);
  private
  homedir:string;
  codefiles:Tstringlist;
  codefilesL:array of tstringlist;
  Icurrent:integer;
  Adapter:TATAdapterEControl;
  function getIndexOfCurrentLine:integer;
  function getIndexOfCursorX:integer;

  public

  end;

var
  Form1: TForm1;

implementation

{$R *.lfm}

{ TForm1 }

procedure TForm1.edChangeCaretPos(Sender: TObject);
begin
  label2.Caption:='X: '+inttostr(self.getIndexOfCursorX)+' Y: '+inttostr(self.getIndexOfCurrentLine); // +' cnt:'+inttostr(self.globalPositionOfCursor)+' '+ed.text[self.globalPositionOfCursor]

end;

procedure TForm1.edChange(Sender: TObject);
begin
  ed.Gutter.Update;
end;

procedure TForm1.FormCreate(Sender: TObject);
var
i,j:integer;
ini: TIniFile;
an: TecSyntAnalyzer;
fn, fn_ops,LexName:string;
ListFiles:Tstringlist;
begin
  homedir:=ExtractFileDir(ParamStr(0));//GetCurrentDir;
// basic options for editor
ed.Font.Name:= 'Courier New';
ed.Font.size:=10;
ed.Align:= alClient;
ed.OptUnprintedVisible:= false;
ed.OptRulerVisible:= true;
ed.OptWrapMode:=cWrapOn; 
ed.Colors.TextBG:= $ffffff;  // ;
ed.OptCaretVirtual:=false;
// adapters
ListFiles:=Tstringlist.Create;
FindAllFiles(ListFiles, form1.homedir+'/lex', '*.lcf', false);
ListFiles.Sort;
if ListFiles.Count=0 then
  showmessage('No lex files found!');
AppManager:= TecLexerList.Create(Self);
//ListFiles.Add('/media/rune/DATA/New2/lazarus/programs/RscripterCool/lex/R.lcf');
for i:= 0 to ListFiles.Count-1 do
    begin
      an:= AppManager.AddLexer;
      //an.Name:= '_lx_'+LexerFilenameToComponentName(ListFiles[i]);
      an.LoadFromFile(ListFiles[i]);
      //load *.cuda-lexops
      fn_ops:= GetAppLexerOpsFilename(an.LexerName);
      if FileExistsUTF8(fn_ops) then
        DoLoadLexerStylesFromFile_JsonLexerOps(an, fn_ops, UiOps.LexerThemes);
    end;
ListFiles.Destroy;
//
//correct sublexer links
    for i:= 0 to AppManager.LexerCount-1 do
    begin
      an:= AppManager.Lexers[i];
      fn:= GetAppLexerMapFilename(an.LexerName);
      if FileExists(fn) then
      begin
        ini:= TIniFile.Create(fn);
        try
          for j:= 0 to an.SubAnalyzers.Count-1 do
          begin
            LexName:= ini.ReadString('ref', IntToStr(j), '');
            if LexName<>'' then
              an.SubAnalyzers[j].SyntAnalyzer:= AppManager.FindLexerByName(LexName);
          end;
        finally
          FreeAndNil(ini);
        end;
      end;
    end;

//an:=AppManager.Lexers[idx];
Adapter:= TATAdapterEControl.Create(Self);
//Adapter.Lexer:=AppManager.Lexers[idx];
Adapter.Lexer:=AppManager.FindLexerByName('R');
Adapter.AddEditor(ed);

end;


procedure TForm1.ListBox1Click(Sender: TObject);
var
ext:string;
begin
ext:=ExtractFileExt(self.codefiles[listbox1.ItemIndex]);
codeFilesL[Icurrent].Text:=ed.Text;
Icurrent:=listbox1.ItemIndex;
ed.Text:=codeFilesL[Icurrent].Text;
//
if ext='.R' then
  Adapter.Lexer:=AppManager.FindLexerByName('R');
if ext='.py' then
  Adapter.Lexer:=AppManager.FindLexerByName('Python');

//
ed.SetFocus;
ed.update(true,true);
end;

procedure TForm1.MenuItem2Click(Sender: TObject);
begin
if opendialog1.Execute then
  begin
  if codefiles=nil then
    codefiles:=Tstringlist.Create;
  codefiles.Add(opendialog1.FileName);
  listbox1.Items.Add(ExtractFileName(opendialog1.FileName));
  //
  setlength(codefilesL,length(codefilesL)+1);
  codefilesL[length(codefilesL)-1]:=tstringlist.Create;
  codefilesL[length(codefilesL)-1].LoadFromFile(opendialog1.FileName);
  Icurrent:=codefiles.Count-1;
  ed.Text:=codeFilesL[Icurrent].Text;
  listbox1.ItemIndex:=Icurrent;
  end;
end;


function TForm1.getIndexOfCurrentLine: integer;
begin
  result:=ed.Carets[0].PosY;
end;

function TForm1.getIndexOfCursorX: integer;
begin
  result:=ed.Carets[0].PosX+1;
end;

end.
Alexey
Posts: 1633
Joined: 05.10.2012 22:10

Post by Alexey »

You posted Form1 unit but please post zip with - .pas .lpr .lfm .lpi files.
ruma1974
Posts: 11
Joined: 23.08.2019 16:12

Post by ruma1974 »

The program can be downloaded from below link:

https://drive.google.com/open?id=1rX-Y7 ... fpn6lRk9T4

Thanks,

Rune
Alexey
Posts: 1633
Joined: 05.10.2012 22:10

Post by Alexey »

I see that after MenuItem click file is not colored, you activate lexer wrong.
In MenuItem click you must do this:

Code: Select all

  ed.AdapterForHilite:= nil;
  ed.Text:=codeFilesL[Icurrent].Text;
  Adapter.Lexer:= AppManager.FindLexerByFilename(opendialog1.FileName, nil);
  ed.AdapterForHilite:= Adapter;
  ed.Update; //not sure it's needed
also in ListboxClick detect lexer this way:

Code: Select all

  Adapter.Lexer:=AppManager.FindLexerByFileName(...);
  //if not colored, call also: ed.Update and/or ed.DoEventChange
ruma1974
Posts: 11
Joined: 23.08.2019 16:12

Post by ruma1974 »

Thanks a lot for your comments. I followed your suggestions and changed the two functions to the below code. However, I still get the procedure TFPSList.CheckIndex(AIndex : Integer);" triggered after a few clicks if I have one file above 3000 lines.

Code: Select all

procedure TForm1.ListBox1Click(Sender: TObject);
var
ext:string;
begin
ext:=ExtractFileExt(self.codefiles[listbox1.ItemIndex]);
codeFilesL[Icurrent].Text:=ed.Text;
Icurrent:=listbox1.ItemIndex;
ed.Text:=codeFilesL[Icurrent].Text;
//
Adapter.Lexer:=AppManager.FindLexerByFileName(self.codefiles[listbox1.ItemIndex],nil);
//
ed.SetFocus;
ed.update(true,true);
end;

procedure TForm1.MenuItem2Click(Sender: TObject);
begin
if opendialog1.Execute then
  begin
  if codefiles=nil then
    codefiles:=Tstringlist.Create;
  codefiles.Add(opendialog1.FileName);
  listbox1.Items.Add(ExtractFileName(opendialog1.FileName));
  //
  setlength(codefilesL,length(codefilesL)+1);
  codefilesL[length(codefilesL)-1]:=tstringlist.Create;
  codefilesL[length(codefilesL)-1].LoadFromFile(opendialog1.FileName);
  Icurrent:=codefiles.Count-1;
  ed.AdapterForHilite:= nil;
  ed.Text:=codeFilesL[Icurrent].Text;
  Adapter.Lexer:= AppManager.FindLexerByFilename(opendialog1.FileName, nil);
  ed.AdapterForHilite:= Adapter;
  ed.Update; //not sure it's needed
  listbox1.ItemIndex:=Icurrent;
  end;
end;
Alexey
Posts: 1633
Joined: 05.10.2012 22:10

Post by Alexey »

If you can write GIF or MP4 video, will be good (use eg ScreenToGif app, and turn on indication of mouse clicks and pressed keys).

do you only click in a file and get error? or you click +edit?
ruma1974
Posts: 11
Joined: 23.08.2019 16:12

Post by ruma1974 »

I use linux mint 19, Lazarus 2.1.0 and FPC 3.04. If you do not have the error then it must be related to FPC on linux mint.

I just click once in the listbox and then use arrow keys to go up and down and the error occurs relatively fast (within a few seconds). You can see the video below:

https://drive.google.com/file/d/1KCVXy_ ... tt9hi/view
Alexey
Posts: 1633
Joined: 05.10.2012 22:10

Post by Alexey »

I dont see ListError but see another error (broken lexer hiliting after clicking in listbox). the reason is Timer which is used in EControl adapter: adapter starts timer, timer then hilites text, and we have changed text when timer ticks. To solve this: don't load all N files into 1 ATSynedit, load N files into N ATSynedits, and change edits Visible on clicking listbox items.
Post Reply