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.