dexed/src/u_symstring.pas

334 lines
9.1 KiB
Plaintext

unit u_symstring;
{$I u_defines.inc}
interface
uses
u_observer, sysutils, u_interfaces, u_ceproject, u_synmemo, u_common,
u_stringrange;
type
(**
* Enumerates the symbol kinds, used to index an associative array.
*)
TExpandableSymbol = ( ENV_USER, ENV_HOME, ENV_TEMP, CAF, CAP, MEP,
CFF, CFP, CFR, CI, CL, CPF, CPP, CPO, CPOP, CPR, CPN, CPFS, CPCD,
CPV, CS);
const
FirstVariableSymbol = CFF;
type
(**
* TSymbolExpander is designed to expand symbolic strings,
* using the information collected from several observer interfaces.
*)
TSymbolExpander = class(IDocumentObserver, IProjectObserver, ISymStringExpander, IMiniExplorerObserver)
private
fProj: TNativeProject;
fProjInterface: ICommonProject;
fDoc: TDexedMemo;
fNeedUpdate: boolean;
fExp: IExplorer;
fSymbols: array[TExpandableSymbol] of string;
procedure updateSymbols;
procedure projNew(project: ICommonProject);
procedure projClosing(project: ICommonProject);
procedure projFocused(project: ICommonProject);
procedure projChanged(project: ICommonProject);
procedure projCompiling(project: ICommonProject);
procedure projCompiled(project: ICommonProject; success: boolean);
procedure docNew(document: TDexedMemo);
procedure docClosing(document: TDexedMemo);
procedure docFocused(document: TDexedMemo);
procedure docChanged(document: TDexedMemo);
procedure mnexDirectoryChanged(const directory: string);
function singleServiceName: string;
function expand(const value: string): string;
public
constructor Create;
destructor Destroy; override;
end;
implementation
uses
Forms, Classes;
var
symbolExpander: TSymbolExpander;
{$REGION Standard Comp/Obj------------------------------------------------------}
constructor TSymbolExpander.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 TSymbolExpander.Destroy;
begin
fNeedUpdate := false;
EntitiesConnector.removeObserver(self);
inherited;
end;
{$ENDREGION}
{$REGION IProjectObserver ----------------------------------------------------}
procedure TSymbolExpander.projNew(project: ICommonProject);
begin
fProjInterface := project;
case project.getFormat of
pfDEXED: fProj := TNativeProject(project.getProject);
pfDUB: fProj := nil;
end;
fNeedUpdate := true;
end;
procedure TSymbolExpander.projClosing(project: ICommonProject);
begin
if fProjInterface = project then
fProjInterface := nil;
if fProj = project.getProject then
fProj := nil;
fNeedUpdate := true;
end;
procedure TSymbolExpander.projFocused(project: ICommonProject);
begin
fProjInterface := project;
case project.getFormat of
pfDEXED: fProj := TNativeProject(project.getProject);
pfDUB: fProj := nil;
end;
fNeedUpdate := true;
end;
procedure TSymbolExpander.projChanged(project: ICommonProject);
begin
fNeedUpdate := true;
end;
procedure TSymbolExpander.projCompiling(project: ICommonProject);
begin
end;
procedure TSymbolExpander.projCompiled(project: ICommonProject; success: boolean);
begin
end;
{$ENDREGION}
{$REGION IDocumentObserver ---------------------------------------------------}
procedure TSymbolExpander.docNew(document: TDexedMemo);
begin
fDoc := document;
fNeedUpdate := true;
end;
procedure TSymbolExpander.docClosing(document: TDexedMemo);
begin
if document <> fDoc then
exit;
fDoc := nil;
fNeedUpdate := true;
end;
procedure TSymbolExpander.docFocused(document: TDexedMemo);
begin
if document.isAssigned and (fDoc = document) then
exit;
fDoc := document;
fNeedUpdate := true;
end;
procedure TSymbolExpander.docChanged(document: TDexedMemo);
begin
if document <> fDoc then
exit;
fNeedUpdate := true;
end;
procedure TSymbolExpander.mnexDirectoryChanged(const directory: string);
begin
fNeedUpdate := true;
end;
{$ENDREGION}
{$REGION Symbol things ---------------------------------------------------------}
procedure TSymbolExpander.updateSymbols;
var
hasNativeProj: boolean;
hasProjItf: boolean;
hasDoc: boolean;
fname: string;
i: Integer;
e: TExpandableSymbol;
str: TStringList;
const
na = '``';
begin
if not fNeedUpdate then
exit;
fNeedUpdate := false;
hasNativeProj := fProj.isAssigned;
hasProjItf := fProjInterface.isAssigned;
hasDoc := fDoc.isAssigned;
if fExp.isNotAssigned then
fExp := getExplorer;
for e := FirstVariableSymbol to high(TExpandableSymbol) do
fSymbols[e] := na;
if fExp.isAssigned then
fSymbols[MEP] := fExp.currentLocation;
// document
if hasDoc then
begin
if not fDoc.fileName.fileExists then
fDoc.saveTempFile;
fSymbols[CFF] := fDoc.fileName;
fSymbols[CFR] := fSymbols[CFF].stripFileExt + exeExt;
fSymbols[CFP] := fSymbols[CFF].extractFilePath;
if fDoc.Identifier.isNotEmpty then
fSymbols[CI] := fDoc.Identifier;
fSymbols[CL] := fDoc.LineText;
fSymbols[CS] := fDoc.SelText;
end;
// project interface
if hasProjItf then
begin
fname := fProjInterface.filename;
fSymbols[CPF] := fname;
fSymbols[CPP] := fSymbols[CPF].extractFilePath;
fSymbols[CPN] := fSymbols[CPF].extractFileName.stripFileExt;
fSymbols[CPO] := fProjInterface.outputFilename;
fSymbols[CPOP]:= fSymbols[CPO].extractFileDir;
fSymbols[CPR] := fSymbols[CPP];
if not fProjInterface.sourcesCount.equals(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
if fProj.version.isNotEmpty then
fSymbols[CPV] := fProj.version;
fSymbols[CPR] := expandFilenameEx(fProj.basePath, fProj.RootFolder);
if fSymbols[CPR].isEmpty then
fSymbols[CPR] := fSymbols[CPP];
end;
end;
//
for e := FirstVariableSymbol to high(TExpandableSymbol) do
if fSymbols[e].isEmpty then
fSymbols[e] := na;
end;
function TSymbolExpander.singleServiceName: string;
begin
exit('ISymStringExpander');
end;
function TSymbolExpander.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
;
sym := rng.popFront^.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];
//
'AF', 'CAF', 'CoeditApplicationFile': Result += fSymbols[CAF];
'AP', 'CAP', 'CoeditApplicationPath': Result += fSymbols[CAP];
'MEP', 'MiniExplorerPath': Result += fSymbols[MEP];
//
'CFF', 'CurrentFileFile' : Result += fSymbols[CFF];
'CFR', 'CurrentFileRunnable' : Result += fSymbols[CFR];
'CFP', 'CurrentFilePath' : Result += fSymbols[CFP];
'CI', 'CurrentIdentifier' : Result += fSymbols[CI];
'CL', 'CurrentLine' : Result += fSymbols[CL];
'CS', 'CurrentSelection' : Result += fSymbols[CS];
//
'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];
'CPV', 'CurrentProjectVersion': Result += fSymbols[CPV];
//
else Result += '<' + sym + '>';
end;
end
else Result += '<' + sym;
end;
end;
end;
{$ENDREGION}
initialization
symbolExpander := TSymbolExpander.Create;
finalization
symbolExpander.Free;
end.