dexed/src/ce_symstring.pas

311 lines
8.3 KiB
Plaintext

unit ce_symstring;
{$I ce_defines.inc}
interface
uses
ce_observer, ce_interfaces, ce_nativeproject, ce_synmemo, ce_common,
ce_stringrange;
type
(**
* Enumerates the symbol kinds, used to index an associative array.
*)
TCESymbol = ( ENV_USER, ENV_HOME, ENV_TEMP, CAF, CAP,
CFF, CFP, CI, CPF, CPP, CPO, CPOP, CPR, CPN, CPFS, CPCD);
const
FirstVariableSymbol = CFF;
type
(**
* TCESymbolExpander is designed to expand Coedit symbolic strings,
* using the information collected from several observer interfaces.
*)
TCESymbolExpander = class(ICEMultiDocObserver, ICEProjectObserver, ICESymStringExpander)
private
fProj: TCENativeProject;
fProjInterface: ICECommonProject;
fDoc: TCESynMemo;
fNeedUpdate: boolean;
fSymbols: array[TCESymbol] of string;
procedure updateSymbols;
//
procedure projNew(aProject: ICECommonProject);
procedure projClosing(aProject: ICECommonProject);
procedure projFocused(aProject: ICECommonProject);
procedure projChanged(aProject: ICECommonProject);
procedure projCompiling(aProject: ICECommonProject);
procedure projCompiled(aProject: ICECommonProject; success: boolean);
//
procedure docNew(aDoc: TCESynMemo);
procedure docClosing(aDoc: TCESynMemo);
procedure docFocused(aDoc: TCESynMemo);
procedure docChanged(aDoc: TCESynMemo);
//
function singleServiceName: string;
function expand(const value: string): string;
public
constructor Create;
destructor Destroy; override;
end;
implementation
uses
Forms, SysUtils, Classes;
var
symbolExpander: TCESymbolExpander;
{$REGION Standard Comp/Obj------------------------------------------------------}
constructor TCESymbolExpander.Create;
begin
EntitiesConnector.addObserver(self);
EntitiesConnector.addSingleService(self);
fNeedUpdate := true;
//
{$IFDEF UNIX}
fSymbols[ENV_USER] := sysutils.GetEnvironmentVariable('USER');
fSymbols[ENV_HOME] := sysutils.GetEnvironmentVariable('HOME');
fSymbols[ENV_TEMP] := sysutils.GetEnvironmentVariable('TMPDIR');
{$ELSE}
fSymbols[ENV_USER] := sysutils.GetEnvironmentVariable('USERNAME');
fSymbols[ENV_HOME] := sysutils.GetEnvironmentVariable('HOMEPATH');
fSymbols[ENV_TEMP] := sysutils.GetEnvironmentVariable('TEMP');
{$ENDIF}
fSymbols[CAF] := Application.ExeName;
fSymbols[CAP] := fSymbols[CAF].extractFilePath;
end;
destructor TCESymbolExpander.Destroy;
begin
fNeedUpdate := false;
EntitiesConnector.removeObserver(self);
inherited;
end;
{$ENDREGION}
{$REGION ICEProjectObserver ----------------------------------------------------}
procedure TCESymbolExpander.projNew(aProject: ICECommonProject);
begin
fProjInterface := aProject;
case aProject.getFormat of
pfNative: fProj := TCENativeProject(aProject.getProject);
pfDub: fProj := nil;
end;
fNeedUpdate := true;
end;
procedure TCESymbolExpander.projClosing(aProject: ICECommonProject);
begin
fProjInterface := nil;
if fProj <> aProject.getProject then
exit;
fProj := nil;
fNeedUpdate := true;
end;
procedure TCESymbolExpander.projFocused(aProject: ICECommonProject);
begin
fProjInterface := aProject;
case aProject.getFormat of
pfNative: fProj := TCENativeProject(aProject.getProject);
pfDub: fProj := nil;
end;
fNeedUpdate := true;
end;
procedure TCESymbolExpander.projChanged(aProject: ICECommonProject);
begin
fProjInterface := aProject;
if fProj <> aProject.getProject then
exit;
fNeedUpdate := true;
end;
procedure TCESymbolExpander.projCompiling(aProject: ICECommonProject);
begin
end;
procedure TCESymbolExpander.projCompiled(aProject: ICECommonProject; success: boolean);
begin
end;
{$ENDREGION}
{$REGION ICEMultiDocObserver ---------------------------------------------------}
procedure TCESymbolExpander.docNew(aDoc: TCESynMemo);
begin
fDoc := aDoc;
fNeedUpdate := true;
end;
procedure TCESymbolExpander.docClosing(aDoc: TCESynMemo);
begin
if aDoc <> fDoc then
exit;
fDoc := nil;
fNeedUpdate := true;
end;
procedure TCESymbolExpander.docFocused(aDoc: TCESynMemo);
begin
if (aDoc.isNotNil) and (fDoc = aDoc) then
exit;
fDoc := aDoc;
fNeedUpdate := true;
end;
procedure TCESymbolExpander.docChanged(aDoc: TCESynMemo);
begin
if aDoc <> fDoc then
exit;
fNeedUpdate := true;
end;
{$ENDREGION}
{$REGION Symbol things ---------------------------------------------------------}
procedure TCESymbolExpander.updateSymbols;
var
hasNativeProj: boolean;
hasProjItf: boolean;
hasDoc: boolean;
fname: string;
i: Integer;
e: TCESymbol;
str: TStringList;
const
na = '``';
begin
if not fNeedUpdate then exit;
fNeedUpdate := false;
//
hasNativeProj := fProj.isNotNil;
hasProjItf := fProjInterface <> nil;
hasDoc := fDoc.isNotNil;
//
for e := FirstVariableSymbol to high(TCESymbol) do
fSymbols[e] := na;
// document
if hasDoc then
begin
if not fDoc.fileName.fileExists then
fDoc.saveTempFile;
fSymbols[CFF] := fDoc.fileName;
fSymbols[CFP] := fDoc.fileName.extractFilePath;
if fDoc.Identifier.isNotEmpty then
fSymbols[CI] := fDoc.Identifier;
end;
// project interface
if hasProjItf then
begin
fname := fProjInterface.filename;
fSymbols[CPF] := fname;
fSymbols[CPP] := fSymbols[CPF].extractFilePath;
fSymbols[CPN] := stripFileExt(fSymbols[CPF].extractFileName);
fSymbols[CPO] := fProjInterface.outputFilename;
fSymbols[CPOP]:= fSymbols[CPO].extractFileDir;
fSymbols[CPR] := fSymbols[CPP];
if fProjInterface.sourcesCount <> 0 then
begin
str := TStringList.Create;
try
for i := 0 to fProjInterface.sourcesCount-1 do
begin
fname := fProjInterface.sourceAbsolute(i);
if not isEditable(fname.extractFileExt) then
continue;
str.Add(fname);
end;
fSymbols[CPFS] := str.Text;
if str.Count = 1 then
fSymbols[CPCD] := str[0].extractFileDir
else
fSymbols[CPCD] := commonFolder(str);
finally
str.Free;
end;
end;
end;
if hasNativeProj then
begin
if fProj.fileName.fileExists then
begin
fSymbols[CPR] := expandFilenameEx(fProj.basePath, fProj.RootFolder);
if fSymbols[CPR].isEmpty then
fSymbols[CPR] := fSymbols[CPP];
end;
end;
//
for e := FirstVariableSymbol to high(TCESymbol) do
if fSymbols[e].isEmpty then
fSymbols[e] := na;
end;
function TCESymbolExpander.singleServiceName: string;
begin
exit('ICESymStringExpander');
end;
function TCESymbolExpander.expand(const value: string): string;
var
rng: TStringRange = (ptr:nil; pos:0; len: 0);
sym: string;
begin
Result := '';
if value.isEmpty then
exit;
//
updateSymbols;
rng.init(value);
while true do
begin
if rng.empty then
break;
Result += rng.takeUntil('<').yield;
if not rng.empty and (rng.front = '<') then
begin
rng.popFront;
sym := rng.takeUntil('>').yield;
if not rng.empty and (rng.front = '>') then
begin
rng.popFront;
case sym of
'ENV_HOME': Result += fSymbols[ENV_HOME];
'ENV_TEMP': Result += fSymbols[ENV_TEMP];
'ENV_USER': Result += fSymbols[ENV_USER];
//
'CAF', 'CoeditApplicationFile': Result += fSymbols[CAF];
'CAP', 'CoeditApplicationPath': Result += fSymbols[CAP];
//
'CFF', 'CurrentFileFile' : Result += fSymbols[CFF];
'CFP', 'CurrentFilePath' : Result += fSymbols[CFP];
'CI', 'CurrentIdentifier' : Result += fSymbols[CI];
//
'CPF', 'CurrentProjectFile' : Result += fSymbols[CPF];
'CPFS','CurrentProjectFiles' : Result += fSymbols[CPFS];
'CPN', 'CurrentProjectName' : Result += fSymbols[CPN];
'CPO', 'CurrentProjectOutput' : Result += fSymbols[CPO];
'CPOP','CurrentProjectOutputPath' : Result += fSymbols[CPOP];
'CPP', 'CurrentProjectPath' : Result += fSymbols[CPP];
'CPR', 'CurrentProjectRoot' : Result += fSymbols[CPR];
'CPCD','CurrentProjectCommonDirectory': Result += fSymbols[CPCD];
//
else Result += '<' + sym + '>';
end;
end
else Result += '<' + sym;
end;
end;
end;
{$ENDREGION}
initialization
symbolExpander := TCESymbolExpander.Create;
finalization
symbolExpander.Free;
end.