unit ce_symstring; {$I ce_defines.inc} interface uses ce_observer, sysutils, ce_interfaces, ce_ceproject, 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, CFR, CI, CL, CPF, CPP, CPO, CPOP, CPR, CPN, CPFS, CPCD, CPV, CS); const FirstVariableSymbol = CFF; type (** * TCESymbolExpander is designed to expand Coedit symbolic strings, * using the information collected from several observer interfaces. *) TCESymbolExpander = class(ICEDocumentObserver, ICEProjectObserver, ICESymStringExpander) private fProj: TCENativeProject; fProjInterface: ICECommonProject; fDoc: TCESynMemo; fNeedUpdate: boolean; fSymbols: array[TCESymbol] of string; procedure updateSymbols; // procedure projNew(project: ICECommonProject); procedure projClosing(project: ICECommonProject); procedure projFocused(project: ICECommonProject); procedure projChanged(project: ICECommonProject); procedure projCompiling(project: ICECommonProject); procedure projCompiled(project: ICECommonProject; success: boolean); // procedure docNew(document: TCESynMemo); procedure docClosing(document: TCESynMemo); procedure docFocused(document: TCESynMemo); procedure docChanged(document: TCESynMemo); // function singleServiceName: string; function expand(const value: string): string; public constructor Create; destructor Destroy; override; end; implementation uses Forms, 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(project: ICECommonProject); begin fProjInterface := project; case project.getFormat of pfCE: fProj := TCENativeProject(project.getProject); pfDUB: fProj := nil; end; fNeedUpdate := true; end; procedure TCESymbolExpander.projClosing(project: ICECommonProject); begin fProjInterface := nil; if fProj <> project.getProject then exit; fProj := nil; fNeedUpdate := true; end; procedure TCESymbolExpander.projFocused(project: ICECommonProject); begin fProjInterface := project; case project.getFormat of pfCE: fProj := TCENativeProject(project.getProject); pfDUB: fProj := nil; end; fNeedUpdate := true; end; procedure TCESymbolExpander.projChanged(project: ICECommonProject); begin fNeedUpdate := true; end; procedure TCESymbolExpander.projCompiling(project: ICECommonProject); begin end; procedure TCESymbolExpander.projCompiled(project: ICECommonProject; success: boolean); begin end; {$ENDREGION} {$REGION ICEDocumentObserver ---------------------------------------------------} procedure TCESymbolExpander.docNew(document: TCESynMemo); begin fDoc := document; fNeedUpdate := true; end; procedure TCESymbolExpander.docClosing(document: TCESynMemo); begin if document <> fDoc then exit; fDoc := nil; fNeedUpdate := true; end; procedure TCESymbolExpander.docFocused(document: TCESynMemo); begin if (document.isNotNil) and (fDoc = document) then exit; fDoc := document; fNeedUpdate := true; end; procedure TCESymbolExpander.docChanged(document: TCESynMemo); begin if document <> 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[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 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 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(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 ; 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]; // 'CAF', 'CoeditApplicationFile': Result += fSymbols[CAF]; 'CAP', 'CoeditApplicationPath': Result += fSymbols[CAP]; // '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 := TCESymbolExpander.Create; finalization symbolExpander.Free; end.