458 lines
16 KiB
ObjectPascal
Executable File
458 lines
16 KiB
ObjectPascal
Executable File
unit UnitTextAnalyze;
|
|
|
|
interface
|
|
|
|
uses SysUtils, Classes, Windows, Forms;
|
|
|
|
type TPawnParseResult = class
|
|
public
|
|
Constants: TStringList;
|
|
Defined: TStringList;
|
|
CVars: TStringList;
|
|
Included: TStringList;
|
|
MethodsDefault: TStringList;
|
|
Events: TStringList;
|
|
Stocks: TStringList;
|
|
Natives: TStringList;
|
|
Forwards: TStringList;
|
|
Variables: TStringList;
|
|
|
|
CallTips: TStringList;
|
|
AutoComplete: TStringList;
|
|
HighlightKeywords: TStringList;
|
|
|
|
constructor Create; reintroduce;
|
|
procedure DestroyResult;
|
|
end;
|
|
|
|
function ParseCodePawn(eCode: TStringList; FileName: string; IsRecursive: Boolean = False): TPawnParseResult;
|
|
function UpdateIncPath(eInput: string): string;
|
|
|
|
var eCPUSpeed: Integer = 1;
|
|
|
|
implementation
|
|
|
|
uses UnitCodeExplorerUpdater, UnitCodeUtils, UnitfrmSettings,
|
|
UnitMainTools, UnitfrmMain;
|
|
|
|
var eLookedUpIncluded: TStringList;
|
|
|
|
function UpdateIncPath(eInput: string): string;
|
|
begin
|
|
eInput := StringReplace(Trim(eInput), '/', '\', [rfReplaceAll]);
|
|
if ExtractFileExt(eInput) = '' then
|
|
eInput := eInput + '.inc';
|
|
|
|
if FileExists(ExtractFilePath(frmSettings.txtPawnCompilerPath.Text) + eInput) then
|
|
Result := ExtractFilePath(frmSettings.txtPawnCompilerPath.Text) + eInput
|
|
else if FileExists(ExtractFilePath(frmSettings.txtPawnCompilerPath.Text) + 'include\' + eInput) then
|
|
Result := ExtractFilePath(frmSettings.txtPawnCompilerPath.Text) + 'include\' + eInput
|
|
else if (FileExists(ExtractFilePath(ActiveDoc.FileName) + eInput)) then
|
|
Result := ExtractFilePath(ActiveDoc.FileName) + eInput
|
|
else
|
|
Result := '';
|
|
end;
|
|
|
|
function ParseCodePawn(eCode: TStringList; FileName: string; IsRecursive: Boolean = False): TPawnParseResult;
|
|
var i, k: integer;
|
|
eString, eTemp, eBackup: string;
|
|
eStr, ePreEvents: TStringList;
|
|
eStartLine, eBracesOpen: Integer;
|
|
eTimeToSleep: Integer;
|
|
eAddingEnum: Integer;
|
|
eTempResult: TPawnParseResult;
|
|
eProcedureAdded: Boolean;
|
|
eCActive: Boolean;
|
|
eTempBool: Boolean;
|
|
begin
|
|
Result := TPawnParseResult.Create;
|
|
if not IsRecursive then
|
|
eLookedUpIncluded.Clear;
|
|
|
|
eStr := TStringList.Create;
|
|
ePreEvents := TStringList.Create;
|
|
eBracesOpen := 0;
|
|
eStartLine := -1;
|
|
eTimeToSleep := 0;
|
|
eAddingEnum := 0;
|
|
eCActive := False;
|
|
|
|
for i := 0 to eCode.Count - 1 do begin
|
|
if (Application.Terminated) or (not Started) or (frmMain.pnlLoading.Visible) or (not frmMain.trvExplorer.Visible) then exit;
|
|
|
|
eBackup := Trim(eCode[i]);
|
|
eString := RemoveStringsAndComments(Trim(eCode[i]), True, True);
|
|
if (Pos('/*', eBackup) = 1) or (Pos('*/', eBackup) <> 0) then begin
|
|
eCActive := (Pos('/*', eBackup) = 1);
|
|
if (eCActive) and (Pos('*/', eBackup) <> 0) then begin
|
|
eCActive := False;
|
|
continue
|
|
end;
|
|
end;
|
|
if (eBackup = '') or (Pos('//', eBackup) = 1) or (eCActive) then
|
|
continue;
|
|
|
|
|
|
eProcedureAdded := False;
|
|
Inc(eTimeToSleep, 1);
|
|
|
|
if eTimeToSleep = eCPUSpeed then begin
|
|
Sleep(1);
|
|
eTimeToSleep := 0;
|
|
end;
|
|
|
|
{ Constants and Variables }
|
|
if (IsAtStart('new', eString, False)) or (IsAtStart('const', eString, False)) or (IsAtStart('stock', eString, False)) then begin // const or variable
|
|
if (eBracesOpen = 0) and (not IsRecursive) and (Pos('(', eString) = Pos(')', eString)) then begin
|
|
// we don't need braces so delete them...
|
|
while (CountChars(eString, '{') <> 0) and (CountChars(eString, '}') <> 0) and (Pos('{', eString) < Pos('}', eString)) do
|
|
eString := StringReplace(eString, '{' + Between(eString, '{', '}') + '}', '', [rfReplaceAll]);
|
|
while (CountChars(eString, '[') <> 0) and (CountChars(eString, ']') <> 0) and (Pos('[', eString) < Pos(']', eString)) do
|
|
eString := StringReplace(eString, '[' + Between(eString, '[', ']') + ']', '', [rfReplaceAll]);
|
|
// done? okay, split all items if there are more than one; and if not, it's okay...
|
|
eStr.Text := StringReplace(Copy(eString, Pos(#32, eString)+1, Length(eString)), ',', #13, [rfReplaceAll]);
|
|
for k := 0 to eStr.Count - 1 do begin
|
|
if (Trim(eStr[k]) <> '') and (eStr[k] <> '}') then begin
|
|
eTemp := Trim(RemoveSemicolon(eStr[k]));
|
|
|
|
if (IsAtStart('const', eTemp, False)) then begin
|
|
Delete(eTemp, 1, 5);
|
|
eTemp := Trim(eTemp);
|
|
eTempBool := True;
|
|
end
|
|
else
|
|
eTempBool := (IsAtStart('const', eString, False));
|
|
|
|
if (Pos(':', eTemp) <> 0) then
|
|
eTemp := Copy(eTemp, Pos(':', eTemp) + 1, Length(eTemp));
|
|
|
|
if (Pos('=', eTemp) <> 0) then begin // constant
|
|
Result.Constants.AddObject(Copy(eTemp, 1, Pos('=', eTemp) - 1), TObject(i));
|
|
Result.AutoComplete.Add(Copy(eTemp, 1, Pos('=', eTemp) - 1));
|
|
end
|
|
else begin // variable
|
|
if (eTempBool) then
|
|
Result.Constants.AddObject(eTemp, TObject(i))
|
|
else
|
|
Result.Variables.AddObject(eTemp, TObject(i));
|
|
Result.AutoComplete.Add(eTemp);
|
|
end;
|
|
end;
|
|
end;
|
|
eString := RemoveStringsAndComments(Trim(eCode[i]), True, True);
|
|
continue;
|
|
end;
|
|
end;
|
|
{ Included }
|
|
if (IsAtStart('#include', eBackup)) then begin
|
|
eString := StringReplace(eBackup, '/', '\', [rfReplaceAll]);
|
|
if Between(eString, '<', '>') <> '' then
|
|
eString := Between(eString, '<', '>')
|
|
else if Between(eString, '"', '"') <> '' then
|
|
eString := Between(eString, '"', '"');
|
|
eString := Trim(eString);
|
|
Result.Included.AddObject(eString, TObject(i));
|
|
|
|
// Recursive update
|
|
if (eLookedUpIncluded.IndexOf(eString) = -1) then begin
|
|
eLookedUpIncluded.Add(eString);
|
|
eTemp := UpdateIncPath(eString);
|
|
|
|
if (eString <> '') and (FileExists(eTemp)) then begin
|
|
// Load code and parse
|
|
eTempResult := nil;
|
|
try
|
|
eStr.LoadFromFile(eTemp);
|
|
if Application.Terminated then exit;
|
|
eTempResult := ParseCodePawn(eStr, ExtractFileName(eTemp), True);
|
|
// Assign parsed values
|
|
Result.AutoComplete.AddStrings(eTempResult.AutoComplete);
|
|
Result.CallTips.AddStrings(eTempResult.CallTips);
|
|
Result.HighlightKeywords.AddStrings(eTempResult.HighlightKeywords);
|
|
// free
|
|
except
|
|
// mmmm.. burger
|
|
end;
|
|
if Assigned(eTempResult) then
|
|
eTempResult.DestroyResult;
|
|
// wait
|
|
Sleep(20);
|
|
end;
|
|
end;
|
|
continue;
|
|
end;
|
|
{ CVars }
|
|
if (IsAtStart('register_cvar', eString)) and (not IsRecursive) then begin
|
|
if Between(eString, '"', '"') <> '' then
|
|
Result.CVars.AddObject(Between(eBackup, '"', '"'), TObject(i));
|
|
continue;
|
|
end;
|
|
{ Defined }
|
|
if (IsAtStart('#define', eString)) then begin
|
|
eString := Copy(eString, 8, Length(eString));
|
|
eString := Trim(eString);
|
|
Result.CallTips.Add(eString + '-> ' + FileName + ', defined constant');
|
|
if Pos(#32, eString) <> 0 then
|
|
eString := Copy(eString, 1, Pos(#32, eString) - 1);
|
|
if Pos(' ', eString) <> 0 then
|
|
eString := Copy(eString, 1, Pos(' ', eString) - 1);
|
|
Result.Defined.AddObject(eString, TObject(i));
|
|
Result.AutoComplete.Add(eString);
|
|
continue;
|
|
end;
|
|
{ Events (Part 1) }
|
|
if (IsAtStart('register_event(', eString)) and (not IsRecursive) then begin
|
|
if CountChars(eBackup, '"') >= 4 then begin
|
|
eTemp := StringReplace(eBackup, '"' + Between(eBackup, '"', '"') + '"', '', []);
|
|
ePreEvents.Add(Between(eBackup, '"', '"'));
|
|
end;
|
|
continue;
|
|
end;
|
|
|
|
{ Functions (1), this is adapted from AMXX-Edit v2 [see TextAnalyze.pas] }
|
|
eBracesOpen := eBracesOpen + CountChars(eString, '{');
|
|
eBracesOpen := eBracesOpen - CountChars(eString, '}');
|
|
|
|
if Pos('{', eString) <> 0 then begin
|
|
{ Enums -> }
|
|
if eAddingEnum = 1 then begin
|
|
eAddingEnum := 2;
|
|
Delete(eString, 1, Pos('{', eString) + 1);
|
|
end
|
|
else begin
|
|
if eStartLine = -1 then begin
|
|
eProcedureAdded := True;
|
|
eStartLine := i;
|
|
end;
|
|
end;
|
|
{ <- Enums }
|
|
end;
|
|
if (Pos('}', eString) <> 0) then begin
|
|
{ Enums -> }
|
|
if eAddingEnum <> 0 then
|
|
eAddingEnum := 0;
|
|
|
|
{ <- Enums }
|
|
if (eStartLine <> -1) then begin
|
|
if (eBracesOpen = 0) and (not IsAtStart('new', Trim(eCode[eStartLine]))) then begin
|
|
if Trim(RemoveStringsAndComments(eCode[eStartLine], True, True)) = '{' then
|
|
eStartLine := eStartLine - 1;
|
|
eTemp := Trim(RemoveSemicolon(Trim(eCode[eStartLine])));
|
|
|
|
// Analyze type
|
|
k := 0;
|
|
if IsAtStart('public', eTemp) then
|
|
k := 1
|
|
else if IsAtStart('stock', eTemp) then
|
|
k := 2
|
|
else if IsAtStart('native', eTemp) then
|
|
k := 3
|
|
else if IsAtStart('forward', eTemp) then
|
|
k := 4
|
|
else if Pos('enum', LowerCase(eTemp)) = 1 then // no method
|
|
k := 5;
|
|
|
|
|
|
// Remove type
|
|
if Pos('@', eTemp) = 1 then begin
|
|
eTemp := Copy(eTemp, 2, Length(eTemp));
|
|
k := 1;
|
|
end
|
|
else begin
|
|
if (Pos(#32, eTemp) <> 0) and (Pos(#32, eTemp) < Pos('(', eTemp)) then
|
|
eTemp := Copy(eCode[eStartLine], Pos(#32, eCode[eStartLine]) + 1, Length(eCode[eStartLine]))
|
|
else if (Pos(#9, eTemp) <> 0) and (Pos(#9, eTemp) < Pos('(', eTemp)) then
|
|
eTemp := Copy(eTemp, Pos(#9, eTemp) + 1, Length(eTemp));
|
|
end;
|
|
|
|
if eTemp[Length(eTemp)] = '{' then
|
|
eTemp := Trim(Copy(eTemp, 1, Length(eTemp) - 1));
|
|
|
|
// Remove return-type
|
|
if (Pos(':', eTemp) <> 0) and (Pos(':', eTemp) < Pos('(', eTemp)) then
|
|
Delete(eTemp, 1, Pos(':', eTemp));
|
|
|
|
if Pos('operator', eTemp) = 1 then
|
|
k := 6;
|
|
|
|
eTemp := RemoveSemicolon(eTemp);
|
|
if k < 5 then begin
|
|
case k of
|
|
0: Result.CallTips.Add(eTemp + '-> ' + FileName + ', function');
|
|
1: Result.CallTips.Add(eTemp + '-> ' + FileName + ', public function');
|
|
2: Result.CallTips.Add(eTemp + '-> ' + FileName + ', stock');
|
|
3: Result.CallTips.Add(eTemp + '-> ' + FileName + ', native');
|
|
4: Result.CallTips.Add(eTemp + '-> ' + FileName + ', forward');
|
|
end;
|
|
end;
|
|
// Copy function-name
|
|
if Pos('(', eTemp) <> 0 then
|
|
eTemp := Copy(eTemp, 1, Pos('(', eTemp) - 1);
|
|
eTemp := Trim(eTemp);
|
|
|
|
if (Result.AutoComplete.IndexOf(eTemp) <> -1) then begin
|
|
eStartLine := -1;
|
|
eBracesOpen := 0;
|
|
continue;
|
|
end;
|
|
|
|
if k < 5 then begin
|
|
Result.AutoComplete.Add(eTemp);
|
|
Result.HighlightKeywords.Add(eTemp);
|
|
end;
|
|
|
|
if eTemp <> '' then begin
|
|
case k of
|
|
0: begin
|
|
if not IsRecursive then
|
|
Result.MethodsDefault.AddObject(eTemp, TObject(eStartLine)); // Default Method
|
|
end;
|
|
1: begin
|
|
k := ePreEvents.IndexOf(eTemp);
|
|
if k <> -1 then begin
|
|
Result.Events.AddObject(eTemp, ePreEvents.Objects[k]);
|
|
ePreEvents.Delete(k);
|
|
end
|
|
else
|
|
Result.MethodsDefault.AddObject(eTemp, TObject(eStartLine));
|
|
end;
|
|
2: Result.Stocks.AddObject(eTemp, TObject(eStartLine));
|
|
3: Result.Natives.AddObject(eTemp, TObject(eStartLine));
|
|
4: Result.Forwards.AddObject(eTemp, TObject(eStartLine));
|
|
end;
|
|
end;
|
|
eStartLine := -1;
|
|
eBracesOpen := 0;
|
|
end;
|
|
end;
|
|
end
|
|
else if (eAddingEnum = 2) and (Pos('enum', LowerCase(eString)) <> 1) then begin
|
|
if Pos(' ', eString) <> 0 then
|
|
eString := Copy(eString, 1, Pos(' ', eString) - 1);
|
|
if Pos(',', eString) <> 0 then
|
|
eString := Copy(eString, 1, Pos(',', eString) - 1);
|
|
if Pos(' ', eString) <> 0 then
|
|
eString := Copy(eString, 1, Pos(' ', eString) - 1);
|
|
if Pos(':', eString) <> 0 then
|
|
eString := Copy(eString, 1, Pos(':', eString) - 1);
|
|
Result.AutoComplete.Add(eString);
|
|
end;
|
|
|
|
{ Enums }
|
|
if IsAtStart('enum', eString) then begin
|
|
if Pos('{', eString) <> 0 then
|
|
eAddingEnum := 2 // Add values immediately
|
|
else
|
|
eAddingEnum := 1; // Wait for next brace and add then
|
|
end;
|
|
|
|
{ Functions (2) }
|
|
if (IsAtStart('forward', eString)) or (IsAtStart('public', eString)) or (IsAtStart('native', eString)) or (IsAtStart('stock', eString)) then begin
|
|
if (not eProcedureAdded) and (Pos('(', eString) <> 0) and (Pos(')', eString) <> 0) then begin
|
|
eTemp := StringReplace(Trim(eBackup), #9, #32, [rfReplaceAll]);
|
|
eTemp := Trim(RemoveSemicolon(eTemp));
|
|
if eTemp[Length(eTemp)] = '{' then
|
|
eTemp := Trim(Copy(eTemp, 1, Length(eTemp) - 1));
|
|
|
|
// Remove type
|
|
if (Pos(#32, eTemp) <> 0) and (Pos(#32, eTemp) < Pos('(', eTemp)) then
|
|
eTemp := Copy(eTemp, Pos(#32, eTemp) + 1, Length(eTemp));
|
|
if (Pos(#9, eTemp) <> 0) and (Pos(#9, eTemp) < Pos('(', eTemp)) then
|
|
eTemp := Copy(eTemp, Pos(#9, eTemp) + 1, Length(eTemp));
|
|
// Remove return-type
|
|
if (Pos(':', eTemp) <> 0) and (Pos(':', eTemp) < Pos('(', eTemp)) then
|
|
Delete(eTemp, 1, Pos(':', eTemp));
|
|
|
|
eTemp := RemoveSemicolon(eTemp);
|
|
if (Pos('enum', eTemp) = Pos('operator', eTemp)) and (Pos('enum', eTemp) = 0) then
|
|
Result.CallTips.Add(eTemp + '-> ' + FileName + ', ' + Trim(Copy(eString, 1, Pos(#32, eString) -1)));
|
|
|
|
// Copy function-name
|
|
if Pos('(', eTemp) <> 0 then
|
|
eTemp := Copy(eTemp, 1, Pos('(', eTemp) - 1);
|
|
eTemp := Trim(eTemp);
|
|
|
|
if (Pos('enum', eTemp) = Pos('operator', eTemp)) and (Pos('enum', eTemp) = 0) then begin
|
|
Result.AutoComplete.Add(eTemp);
|
|
Result.HighlightKeywords.Add(eTemp);
|
|
end;
|
|
|
|
if eTemp <> '' then begin
|
|
if IsAtStart('forward', eString) then
|
|
Result.Forwards.AddObject(eString, TObject(i))
|
|
else if IsAtStart('public', eString) then begin
|
|
k := ePreEvents.IndexOf(eTemp);
|
|
if k <> -1 then begin
|
|
Result.Events.AddObject(eTemp, ePreEvents.Objects[k]);
|
|
ePreEvents.Delete(k);
|
|
end
|
|
else
|
|
Result.MethodsDefault.AddObject(eTemp, TObject(i));
|
|
end
|
|
else if IsAtStart('native', eString) then
|
|
Result.Natives.AddObject(eTemp, TObject(i))
|
|
else if IsAtStart('stock', eString) then
|
|
Result.Stocks.AddObject(eTemp, TObject(i))
|
|
else if (Pos('enum', eTemp) = Pos('operator', eTemp)) and (Pos('enum', eTemp) = 0) then
|
|
Result.MethodsDefault.AddObject(eTemp, TObject(i));
|
|
end;
|
|
end;
|
|
end;
|
|
end;
|
|
ePreEvents.Free;
|
|
eStr.Free;
|
|
end;
|
|
|
|
{ TPawnParseResult }
|
|
|
|
constructor TPawnParseResult.Create;
|
|
begin
|
|
inherited Create;
|
|
|
|
Constants := TStringList.Create;
|
|
Defined := TStringList.Create;
|
|
CVars := TStringList.Create;
|
|
Included := TStringList.Create;
|
|
MethodsDefault := TStringList.Create;
|
|
Events := TStringList.Create;
|
|
Stocks := TStringList.Create;
|
|
Natives := TStringList.Create;
|
|
Forwards := TStringList.Create;
|
|
Variables := TStringList.Create;
|
|
|
|
CallTips := TStringList.Create;
|
|
AutoComplete := TStringList.Create;
|
|
HighlightKeywords := TStringList.Create;
|
|
end;
|
|
|
|
procedure TPawnParseResult.DestroyResult;
|
|
begin
|
|
AutoComplete.Free;
|
|
CallTips.Free;
|
|
Constants.Free;
|
|
CVars.Free;
|
|
Defined.Free;
|
|
Events.Free;
|
|
Forwards.Free;
|
|
HighlightKeywords.Free;
|
|
Included.Free;
|
|
MethodsDefault.Free;
|
|
Natives.Free;
|
|
Stocks.Free;
|
|
Variables.Free;
|
|
|
|
Free;
|
|
end;
|
|
|
|
initialization
|
|
|
|
eLookedUpIncluded := TStringList.Create;
|
|
|
|
finalization
|
|
|
|
eLookedUpIncluded.Free;
|
|
|
|
|
|
end.
|
|
|