wrapped DCD things in a class: allow to save settings and the instance to know docs and projs.

+ fixes the small lag previously happening during first DCD query
This commit is contained in:
Basile Burg 2014-12-11 09:06:16 +01:00
parent d0ca1fbffa
commit 93be81f643
7 changed files with 245 additions and 243 deletions

View File

@ -5,118 +5,206 @@ unit ce_dcd;
interface
uses
Classes, SysUtils, process, forms, strutils, ce_common;
Classes, SysUtils, process, forms, strutils,
ce_common, ce_writableComponent, ce_interfaces, ce_observer, ce_synmemo, ce_project;
(**
* frees the server: e.g: to remove some bugy imports from the libman.
*)
procedure freeServer;
(**
* recreates the server.
*)
procedure createServer;
(**
* Adds a folder of d sources for DCD.
*)
procedure addDcdImport(const aFilename: string);
(**
* gets a list of propositions for the identifier at aPosition in aFilename.
*)
procedure getCompletion(const aFilename: string; aPosition: Integer; const list: TStrings);
(**
* tries to get the DDoc comment for the identifier at aPosition in aFilename.
*)
procedure getHint(const aFilename: string; aPosition: Integer; const list: TStrings);
(**
* tries to get the symbol location of the identifier at aPosition in aFilename.
* after the call aFilename and aPosition contains the location filename and position.
*)
procedure getSymbolLoc(var aFilename: string; var aPosition: Integer);
type
(**
* Wrap the dcd-server and dcd-clients processes.
*
* Projects folder are automatically imported: ICEProjectObserver.
* Completion, hints and declaration finder automatically work on the current
* document: ICEMultiDocObserver.
*)
TCEDcdWrapper = class(TWritableComponent, ICEProjectObserver, ICEMultiDocObserver)
private
fTempLines: TStringList;
//fPortNum: Word;
fClient, fServer: TProcess;
fAvailable: boolean;
fDoc: TCESynMemo;
fProj: TCEProject;
procedure killServer;
//
procedure projNew(aProject: TCEProject);
procedure projChanged(aProject: TCEProject);
procedure projClosing(aProject: TCEProject);
procedure projFocused(aProject: TCEProject);
//
procedure docNew(aDoc: TCESynMemo);
procedure docFocused(aDoc: TCESynMemo);
procedure docChanged(aDoc: TCESynMemo);
procedure docClosing(aDoc: TCESynMemo);
public
constructor create(aOwner: TComponent); override;
destructor destroy; override;
//
procedure restartServer;
procedure addImportFolder(const aFolder: string);
procedure getComplAtCursor(aList: TStrings);
procedure getDdocFromCursor(out aComment: string);
procedure getDeclFromCursor(out aFilename: string; out aPosition: Integer);
//
property available: boolean read fAvailable;
end;
var
dcdOn: boolean;
DcdWrapper: TCEDcdWrapper;
implementation
var
DCD_server: TProcess = nil;
DCD_client: TProcess = nil;
lines: TStringList;
procedure lazyServerStart;
{$REGION Standard Comp/Obj------------------------------------------------------}
constructor TCEDcdWrapper.create(aOwner: TComponent);
const
clientName = 'dcd-client' + exeExt;
serverName = 'dcd-server' + exeExt;
begin
if not DCD_server.Running then
DCD_server.Execute;
end;
procedure freeServer;
begin
while DCD_client.Running do;
DCD_client.Parameters.Clear;
DCD_client.Parameters.Add('--shutdown');
DCD_client.Execute;
if DCD_server <> nil then
FreeAndNil(DCD_server);
end;
procedure createServer;
begin
if DCD_server <> nil then
FreeAndNil(DCD_server);
DCD_server := TProcess.Create(nil);
DCD_server.Executable := 'dcd-server' + exeExt;
DCD_server.Options := [poUsePipes{$IFDEF WINDOWS}, poNewConsole{$ENDIF}];
DCD_server.ShowWindow := swoHIDE;
end;
procedure addDcdImport(const aFilename: string);
begin
if not dcdOn then exit;
inherited;
//
if not DCD_server.Running then
DCD_server.Parameters.Add('-I' + aFilename)
else if DCD_client <> nil then begin
DCD_client.Parameters.Clear;
DCD_client.Parameters.Add('-I' + aFilename);
DCD_client.Execute;
end;
fAvailable := exeInSysPath(clientName) and exeInSysPath(serverName);
if not fAvailable then
exit;
//
fClient := TProcess.Create(self);
fClient.Executable := clientName;
fClient.Options := [poUsePipes{$IFDEF WINDOWS}, poNewConsole{$ENDIF}];
{$IFNDEF DEBUG}
fClient.ShowWindow := swoHIDE;
{$ENDIF}
//
fServer := TProcess.Create(self);
fServer.Executable := serverName;
fServer.Options := [poUsePipes{$IFDEF WINDOWS}, poNewConsole{$ENDIF}];
{$IFNDEF DEBUG}
fServer.ShowWindow := swoHIDE;
{$ENDIF}
fTempLines := TStringList.Create;
fServer.Execute;
//
EntitiesConnector.addObserver(self);
end;
procedure getCompletion(const aFilename: string; aPosition: Integer; const list: TStrings);
destructor TCEDcdWrapper.destroy;
begin
EntitiesConnector.removeObserver(self);
if fTempLines <> nil then
fTempLines.Free;
killServer;
inherited;
end;
{$ENDREGION}
{$REGION ICEProjectObserver ----------------------------------------------------}
procedure TCEDcdWrapper.projNew(aProject: TCEProject);
begin
fProj := aProject;
end;
procedure TCEDcdWrapper.projChanged(aProject: TCEProject);
var
i: Integer;
begin
if fProj <> aProject then
exit;
if fProj = nil then
exit;
//
for i:= 0 to fProj.Sources.Count-1 do
addImportFolder(extractFilePath(fProj.Sources.Strings[i]));
end;
procedure TCEDcdWrapper.projClosing(aProject: TCEProject);
begin
if fProj <> aProject then exit;
fProj := nil;
end;
procedure TCEDcdWrapper.projFocused(aProject: TCEProject);
begin
fProj := aProject;
end;
{$ENDREGION}
{$REGION ICEMultiDocObserver ---------------------------------------------------}
procedure TCEDcdWrapper.docNew(aDoc: TCESynMemo);
begin
fDoc := aDoc;
end;
procedure TCEDcdWrapper.docFocused(aDoc: TCESynMemo);
begin
fDoc := aDoc;
end;
procedure TCEDcdWrapper.docChanged(aDoc: TCESynMemo);
begin
if fDoc <> aDoc then exit;
end;
procedure TCEDcdWrapper.docClosing(aDoc: TCESynMemo);
begin
if fDoc <> aDoc then exit;
fDoc := nil;
end;
{$ENDREGION}
{$REGION DCD things ------------------------------------------------------------}
procedure TCEDcdWrapper.killServer;
begin
if not fAvailable then exit;
//
while fClient.Running do;
fClient.Parameters.Clear;
fClient.Parameters.Add('--shutdown');
fClient.Execute;
end;
procedure TCEDcdWrapper.restartServer;
begin
if not fAvailable then exit;
//
end;
procedure TCEDcdWrapper.addImportFolder(const aFolder: string);
begin
if not fAvailable then exit;
//
fClient.Parameters.Clear;
fClient.Parameters.Add('-I' + aFolder);
fClient.Execute;
end;
procedure TCEDcdWrapper.getComplAtCursor(aList: TStrings);
var
i, j: NativeInt;
kind: Char;
item: string;
asComp, asTips: boolean;
begin
if not dcdOn then
exit;
lazyServerStart;
if not fAvailable then exit;
if fDoc = nil then exit;
//
DCD_client.Parameters.Clear;
DCD_client.Parameters.Add('-c');
DCD_client.Parameters.Add(intToStr(aPosition));
DCD_client.Parameters.Add(aFilename);
DCD_client.Execute;
fTempLines.Assign(fDoc.Lines);
fTempLines.SaveToFile(fDoc.tempFilename);
//
fClient.Parameters.Clear;
fClient.Parameters.Add('-c');
fClient.Parameters.Add(intToStr(fDoc.SelStart - 1));
fClient.Parameters.Add(fDoc.tempFilename);
fClient.Execute;
//
fTempLines.LoadFromStream(fClient.Output);
if fTempLines.Count = 0 then exit;
//
asComp := fTempLines.Strings[0] = 'identifiers';
asTips := fTempLines.Strings[0] = 'calltips';
if asTips then exit;
//
lines.LoadFromStream(DCD_client.Output);
if lines.Count = 0 then
exit;
asComp := lines.Strings[0] = 'identifiers';
asTips := lines.Strings[0] = 'calltips';
if asTips then
exit;
if asComp then j := 1 else j := 0;
list.Clear;
for i := j to lines.Count-1 do
aList.Clear;
for i := j to fTempLines.Count-1 do
begin
item := lines.Strings[i];
item := fTempLines.Strings[i];
kind := item[length(item)];
setLength(item, length(item)-2);
case kind of
@ -138,57 +226,55 @@ begin
't': item += ' (template) ';
'T': item += ' (mixin) ';
end;
list.Add(item);
aList.Add(item);
end;
end;
procedure getHint(const aFilename: string; aPosition: Integer; const list: TStrings);
procedure TCEDcdWrapper.getDdocFromCursor(out aComment: string);
var
i: Integer;
str: string;
begin
if not dcdOn then
exit;
lazyServerStart;
if DCD_client.Running then
exit;
if not fAvailable then exit;
if fDoc = nil then exit;
//
DCD_client.Parameters.Clear;
DCD_client.Parameters.Add('-c');
DCD_client.Parameters.Add(intToStr(aPosition));
DCD_client.Parameters.Add('-d');
DCD_client.Parameters.Add(aFilename);
DCD_client.Execute;
fTempLines.Assign(fDoc.Lines);
fTempLines.SaveToFile(fDoc.tempFilename);
//
list.LoadFromStream(DCD_client.Output);
for i := 0 to list.Count-1 do
begin
str := list.Strings[i];
list.Strings[i] := ReplaceStr(str, '\n', '');
end;
fClient.Parameters.Clear;
fClient.Parameters.Add('-d');
fClient.Parameters.Add('-c');
fClient.Parameters.Add(intToStr(fDoc.SelStart-1));
fClient.Parameters.Add(fDoc.tempFilename);
fClient.Execute;
//
aComment := '';
fTempLines.LoadFromStream(fClient.Output);
for i := 0 to fTempLines.Count-1 do
aComment += ReplaceStr(fTempLines.Strings[i], '\n', LineEnding);
end;
procedure getSymbolLoc(var aFilename: string; var aPosition: Integer);
procedure TCEDcdWrapper.getDeclFromCursor(out aFilename: string; out aPosition: Integer);
var
i: Integer;
str, loc: string;
i: Integer;
str, loc: string;
begin
if not dcdOn then
exit;
lazyServerStart;
if DCD_client.Running then
exit;
if not fAvailable then exit;
if fDoc = nil then exit;
//
DCD_client.Parameters.Clear;
DCD_client.Parameters.Add('-l');
DCD_client.Parameters.Add('-c');
DCD_client.Parameters.Add(intToStr(aPosition));
DCD_client.Parameters.Add(aFilename);
DCD_client.Execute;
fTempLines.Assign(fDoc.Lines);
fTempLines.SaveToFile(fDoc.tempFilename);
//
fClient.Parameters.Clear;
fClient.Parameters.Add('-l');
fClient.Parameters.Add('-c');
fClient.Parameters.Add(intToStr(fDoc.SelStart - 1));
fClient.Parameters.Add(fDoc.tempFilename);
fClient.Execute;
//
str := 'a';
setlength(str, 256);
i := DCD_client.Output.Read(str[1], 256);
i := fClient.Output.Read(str[1], 256);
setLength(str, i);
if str <> '' then
begin
@ -196,25 +282,15 @@ begin
if i = -1 then
exit;
loc := str[i+1..length(str)];
str := str[1..i-1];
aFilename := str;
aFilename := str[1..i-1];
loc := ReplaceStr(loc, LineEnding, '');
aPosition := strToIntDef(loc, -1);
end;
end;
{$ENDREGION}
initialization
createServer;
DCD_client := TProcess.Create(nil);
DCD_client.Executable := 'dcd-client' + exeExt;
DCD_client.Options := [poUsePipes{$IFDEF WINDOWS}, poNewConsole{$ENDIF}];
DCD_client.ShowWindow := swoHIDE;
dcdOn := exeInSysPath(DCD_server.Executable) and exeInSysPath(DCD_client.Executable);
lines := TStringList.Create;
DcdWrapper := TCEDcdWrapper.create(nil);
finalization
DCD_server.Active := false;
DCD_client.Active := false;
DCD_server.Free;
DCD_client.Free;
lines.Free;
DcdWrapper.Free;
end.

View File

@ -52,7 +52,6 @@ type
procedure addEditor;
procedure removeEditor(const aIndex: NativeInt);
procedure focusedEditorChanged;
function getEditorHint: string;
//
procedure docNew(aDoc: TCESynMemo);
procedure docClosing(aDoc: TCESynMemo);
@ -249,85 +248,27 @@ end;
procedure TCEEditorWidget.getSymbolLoc;
var
str: TMemoryStream;
srcpos: Integer;
ftempname, fname: string;
fname: string;
begin
if not dcdOn then exit;
if fDoc = nil then exit;
if not DcdWrapper.available then exit;
//
str := TMemoryStream.Create;
try
ftempname := fDoc.tempFilename;
fDoc.Lines.SaveToStream(str);
str.SaveToFile(ftempname);
fname := ftempname;
srcpos := fDoc.SelStart;
if srcpos > 0 then srcpos += -1;
if fDoc.GetWordAtRowCol(fDoc.LogicalCaretXY) <> '' then
ce_dcd.getSymbolLoc(fname, srcpos);
if fname <> ftempname then if fileExists(fname) then
CEMainForm.openFile(fname);
if srcpos <> -1 then
begin
fDoc.SelStart := srcpos;
fDoc.SelectWord;
end;
finally
str.Free;
DcdWrapper.getDeclFromCursor(fname, srcpos);
if fname <> fDoc.fileName then if fileExists(fname) then
CEMainForm.openFile(fname);
if srcpos <> -1 then begin
fDoc.SelStart := srcpos;
fDoc.SelectWord;
end;
end;
procedure TCEEditorWidget.getCompletionList;
var
str: TMemoryStream;
srcpos: NativeInt;
fname: string;
begin
if not dcdOn then exit;
if fDoc = nil then exit;
if not DcdWrapper.available then exit;
//
str := TMemoryStream.Create;
try
completion.Position := 0; // previous index could cause an error here.
fname := fDoc.tempFilename;
fDoc.Lines.SaveToStream(str);
str.SaveToFile(fname);
srcpos := fDoc.SelStart;
if srcpos > 0 then srcpos += -1;
completion.ItemList.Clear;
ce_dcd.getCompletion(fname, srcpos, completion.ItemList);
finally
str.Free;
end;
end;
function TCEEditorWidget.getEditorHint: string;
var
str: TMemoryStream;
lst: TStringList;
srcpos: NativeInt;
fname: string;
begin
result := '';
if not dcdOn then exit;
if fDoc = nil then exit;
//
str := TMemoryStream.Create;
lst := TStringList.Create;
try
fname := fDoc.tempFilename;
fDoc.Lines.SaveToStream(str);
str.SaveToFile(fname);
srcpos := fDoc.SelStart;
if srcpos > 0 then srcpos += -1;
if fDoc.GetWordAtRowCol(fDoc.LogicalCaretXY) <> '' then
ce_dcd.getHint(fname, srcpos, lst);
result := lst.Text;
finally
str.Free;
lst.Free;
end;
completion.Position := 0;
completion.ItemList.Clear;
DcdWrapper.getComplAtCursor(completion.ItemList);
end;
procedure TCEEditorWidget.UpdateByEvent;

View File

@ -81,14 +81,12 @@ var
itm: TLibraryItem;
i: NativeInt;
begin
if not dcdOn then exit;
//
ce_dcd.freeServer;
ce_dcd.createServer;
if not DcdWrapper.available then exit;
// note: new items are directly handled but removed ones still in cache until next cession.
for i := 0 to fCol.Count-1 do
begin
itm := TLibraryItem(fCol.Items[i]);
ce_dcd.addDcdImport(itm.libSourcePath);
DcdWrapper.addImportFolder(itm.libSourcePath);
end;
end;

View File

@ -929,12 +929,11 @@ procedure TCEMainForm.ApplicationProperties1ShowHint(var HintStr: string;
var CanShow: Boolean; var HintInfo: THintInfo);
begin
CanShow := true;
{if fDoc <> nil then
if fDoc.Focused then
begin
HintStr := fEditWidg.getEditorHint;
CanShow := HintStr <> '';
end;}
//if fDoc <> nil then if fDoc.Focused then
//begin
// DcdWrapper.getDdocFromCursor(HintStr);
// CanShow := HintStr <> '';
//end;
end;
{$ENDREGION}

View File

@ -39,7 +39,6 @@ type
fCanBeRun: boolean;
procedure updateOutFilename;
procedure doChanged;
procedure updateDcd;
procedure setLibAliases(const aValue: TStringList);
procedure subMemberChanged(sender : TObject);
procedure setOptsColl(const aValue: TCollection);
@ -144,14 +143,6 @@ begin
Configuration[i].onChanged := @subMemberChanged;
end;
procedure TCEProject.updateDcd;
var
fname: string;
begin
for fname in fSrcs do
ce_dcd.addDcdImport(extractfilePath(getAbsoluteFilename(fname)));
end;
procedure TCEProject.addSource(const aFilename: string);
var
relSrc, absSrc: string;
@ -162,7 +153,6 @@ begin
if aFilename = absSrc then exit;
end;
fSrcs.Add(ExtractRelativepath(fBasePath,aFilename));
updateDcd;
end;
procedure TCEProject.setRoot(const aValue: string);
@ -208,7 +198,6 @@ begin
beforeChanged;
fSrcs.Assign(aValue);
patchPlateformPaths(fSrcs);
updateDcd;
afterChanged;
end;
@ -363,7 +352,6 @@ var
hasPatched: Boolean;
begin
patchPlateformPaths(fSrcs);
updateDcd;
doChanged;
fModified := false;
hasPatched := false;

View File

@ -54,12 +54,12 @@ const
macFname = 'staticMacros.txt';
defMacros: array[0..5] of string = (
'$a=auto',
'$c=class {}',
'$s=struct {}',
'$ut=unittest{}',
'$fo=for(auto i = 0; ; )',
'$fe=foreach(elem; )'
'$a=auto',
'$c=class {}',
'$s=struct {}',
'$ut=unittest{}',
'$fo=for(auto i = 0; ; )',
'$fe=foreach(elem; )'
);
var

View File

@ -7,7 +7,7 @@ interface
uses
Classes, SysUtils, SynEdit, ce_d2syn, ce_txtsyn ,SynEditHighlighter,
controls, lcltype, LazSynEditText, SynEditKeyCmds, SynHighlighterLFM, SynEditMouseCmds,
ce_common, ce_observer, ce_dcd;
ce_common, ce_observer;
type
@ -76,7 +76,7 @@ var
implementation
uses
graphics, ce_interfaces, ce_staticmacro;
graphics, ce_interfaces, ce_staticmacro, ce_dcd;
{$REGION TCESynMemoPositions ---------------------------------------------------}
constructor TCESynMemoPositions.create(aMemo: TCustomSynEdit);