unit UnitCodeUtils; interface uses SysUtils, Classes, Forms, Controls, Windows, ScintillaLanguageManager, RichEdit, ComCtrls, JvInspector; function PosBack(eSubStr, eStr: String): Integer; function IsAtStart(eSubStr, eStr: String; AllowFunctions: Boolean = True): Boolean; function GetIndents(Line: Integer = -1): String; function GetStyleAt(ePos: Integer): TSciStyle; function LineFromPos(ePos: Integer): Integer; function RemoveSemicolon(eStr: String): String; procedure IndentCode; procedure UnindentCode; function Between(eText, eFirst, eSecond: String; eSecondBack: Boolean = False): String; procedure Delay(eTime: Integer); function CountChars(eIn: String; eChar: Char): Integer; function RemoveStringsAndComments(eLine: String; eRemoveStrings: Boolean; eRemoveComments: Boolean): String; function GetMatchingBrace(eString: String): Integer; function GetColoredLine(eLine: Integer): String; function GetFunctionPos: Integer; function GetCurrFunc: String; function GetRTFText(ARichEdit: TRichedit): string; procedure SetRTFText(ARichEdit: TRichedit; ARTFText: String); implementation uses UnitfrmMain, UnitMainTools, UnitLanguages, UnitfrmIRCPaster; function PosBack(eSubStr, eStr: String): Integer; begin Result := 0; if Pos(eSubStr, eStr) <> 0 then begin while Pos(eSubStr, Copy(eStr, Result +1, Length(eStr))) <> 0 do Inc(Result, 1); end; end; function IsAtStart(eSubStr, eStr: String; AllowFunctions: Boolean = True): Boolean; begin eStr := LowerCase(Trim(StringReplace(eStr, #9, #32, [rfReplaceAll]))); eSubStr := LowerCase(eSubStr); if Pos(eSubStr + #32, eStr) = 1 then Result := True else if (Pos(eSubStr + '(', eStr) = 1) and (AllowFunctions) then Result := True else Result := False; end; function GetIndents(Line: Integer = -1): String; var i: integer; begin Result := ''; if Line = -1 then Line := frmMain.sciEditor.GetCurrentLineNumber; if Length(frmMain.sciEditor.Lines[Line]) <> 0 then begin for i := 1 to Length(frmMain.sciEditor.Lines[Line]) do begin if (frmMain.sciEditor.Lines[Line][i] <> #32) and (frmMain.sciEditor.Lines[Line][i] <> #9) then break else Result := Result + frmMain.sciEditor.Lines[Line][i]; end; end; end; function GetStyleAt(ePos: Integer): TSciStyle; var eBits: Integer; eStyleNo: Byte; i: integer; begin Result := nil; eStyleNo := Byte(frmMain.sciEditor.GetStyleAt(ePos)); eBits := frmMain.sciEditor.GetStyleBits; if eBits = 5 then eStyleNo := eStyleNo and $1f //Strip away the indicators (3 bits) else if eBits = 7 then eStyleNo := eStyleNo and $7f //Strip away the indicators (1 bit) else if eBits = 6 then eStyleNo := eStyleNo and $3f; //Strip away the indicators (2 bits) with frmMain.sciEditor.LanguageManager.LanguageList.Find(ActiveDoc.Highlighter).Styles do begin for i := 0 to Count -1 do begin if TSciStyle(Items[i]).StyleNumber = eStyleNo then Result := TSciStyle(Items[i]); end; end; end; function LineFromPos(ePos: Integer): Integer; var i: integer; eLength: Integer; begin Result := -1; eLength := 0; for i := 0 to frmMain.sciEditor.Lines.Count -1 do begin eLength := eLength + Length(frmMain.sciEditor.Lines[i]) + 2; if eLength >= ePos then begin Result := i; break; end; end; end; function RemoveSemicolon(eStr: String): String; begin if Length(eStr) <> 0 then begin if eStr[Length(eStr)] = ';' then Result := Copy(eStr, 1, Length(eStr) -1) else Result := eStr; end else Result := eStr; end; procedure IndentCode; var eStr: TStringList; i, k: integer; eIndent, eTempIndent: Integer; eString: String; begin Screen.Cursor := crHourGlass; frmMain.sciEditor.Enabled := False; eStr := TStringList.Create; eIndent := 0; eTempIndent := 0; Cancel := False; ShowProgress(False); frmMain.pbLoading.Max := frmMain.sciEditor.Lines.Count *2 -2; for i := 0 to frmMain.sciEditor.Lines.Count -1 do begin if Cancel then begin Cancel := False; exit; end; eStr.Add(TrimLeft(frmMain.sciEditor.Lines[i])); // Remove strings and comments virtually because they could include brackets frmMain.pbLoading.Position := i; SetProgressStatus('Indenting Code...'); eStr[i] := RemoveStringsAndComments(eStr[i], True, True); eStr[i] := LowerCase(Trim(eStr[i])); end; for i := 0 to eStr.Count -1 do begin if CountChars(eStr[i], '{') <> CountChars(eStr[i], '}') then eIndent := eIndent - CountChars(eStr[i], '}'); frmMain.sciEditor.Lines[i] := TrimLeft(frmMain.sciEditor.Lines[i]); for k := 1 to eIndent + eTempIndent do frmMain.sciEditor.Lines[i] := ' ' + frmMain.sciEditor.Lines[i]; if eTempIndent <> 0 then eTempIndent := eTempIndent -1; if (IsAtStart('if', eStr[i], True)) and (Pos('{', eStr[i]) = 0) and (Length(eStr[i]) > 3) then begin eString := eStr[i]; Delete(eString, 1, 2); if (eString[1] <> Trim(eString)[1]) or (eString[1] = '(') then begin eString := Trim(eString); if GetMatchingBrace(eString) = Length(eString) then eTempIndent := eTempIndent +1; end; end else if (IsAtStart('for', eStr[i], True)) and (Pos('{', eStr[i]) = 0) and (Length(eStr[i]) > 4) then begin eString := eStr[i]; Delete(eString, 1, 3); if (eString[1] <> Trim(eString)[1]) or (eString[1] = '(') then begin eString := Trim(eString); if GetMatchingBrace(eString) = Length(eString) then eTempIndent := eTempIndent +1; end; end else if (eStr[i] = 'else') or (Pos('else if', eStr[i]) = 1) and (Pos('{', eStr[i]) = 0) then eTempIndent := eTempIndent +1 else if (Pos('{', eStr[i]) = 0) and (Length(eStr[i]) > 6) then begin if (IsAtStart('stock', eStr[i], False)) or (IsAtStart('while', eStr[i], True)) then begin eString := eStr[i]; Delete(eString, 1, 5); if (eString[1] <> Trim(eString)[1]) or (eString[1] = '(') then begin eString := Trim(eString); if GetMatchingBrace(eString) = Length(eString) then eTempIndent := eTempIndent +1; end; end; end; if (Pos('{', eStr[i]) = 0) and (Length(eStr[i]) > 7) then begin if (Pos('public', eStr[i]) = 1) or (Pos('native', eStr[i]) = 1) then begin eString := eStr[i]; Delete(eString, 1, 6); if eString[1] <> Trim(eString)[1] then begin eString := Trim(eString); if GetMatchingBrace(eString) = Length(eString) then eTempIndent := eTempIndent +1; end; end; end else if (IsAtStart('forward', eStr[i], False)) and (Pos('{', eStr[i]) = 0) and (Length(eStr[i]) > 8) then begin eString := eStr[i]; Delete(eString, 1, 7); if eString[1] <> Trim(eString)[1] then begin eString := Trim(eString); if GetMatchingBrace(eString) = Length(eString) then eTempIndent := eTempIndent +1; end; end; if CountChars(eStr[i], '{') <> CountChars(eStr[i], '}') then eIndent := eIndent + CountChars(eStr[i], '{'); frmMain.pbLoading.Position := frmMain.sciEditor.Lines.Count + i -1; SetProgressStatus('Indenting Code...'); frmMain.pnlLoading.Repaint; end; ActiveDoc.Modified := True; frmMain.mnuModified.Caption := lModified; HideProgress; frmMain.sciEditor.Enabled := True; Screen.Cursor := crDefault; end; procedure UnindentCode; var i: integer; begin Screen.Cursor := crHourGlass; frmMain.sciEditor.Enabled := False; Cancel := False; ShowProgress(False); frmMain.pbLoading.Max := frmMain.sciEditor.Lines.Count -1; for i := 0 to frmMain.sciEditor.Lines.Count -1 do begin if Cancel then begin Cancel := False; exit; end; frmMain.sciEditor.Lines[i] := TrimLeft(frmMain.sciEditor.Lines[i]); frmMain.pbLoading.Position := i; SetProgressStatus('Unintending Code...'); frmMain.pnlLoading.Repaint; end; HideProgress; frmMain.sciEditor.Enabled := True; Screen.Cursor := crDefault; end; function RemoveStringsAndComments(eLine: String; eRemoveStrings: Boolean; eRemoveComments: Boolean): String; begin // Remove comments if eRemoveComments then begin if (Pos(GetCurrLang.CommentBoxStart, eLine) = 1) or (Pos(GetCurrLang.CommentBoxMiddle, eLine) = 1) or (Pos(GetCurrLang.CommentBoxEnd, eLine) = 1) or (Pos(GetCurrLang.CommentBlock, eLine) = 1) then eLine := ''; if Pos(GetCurrLang.CommentBlock, eLine) <> 0 then eLine := Copy(eLine, 1, Pos('//', eLine) -2); if (Pos(GetCurrLang.CommentStreamStart, eLine) < Pos(GetCurrLang.CommentStreamEnd, eLine)) and (Pos(GetCurrLang.CommentStreamStart, eLine) <> 0) then eLine := StringReplace(eLine, GetCurrLang.CommentStreamStart + Between(eLine, GetCurrLang.CommentStreamStart, GetCurrLang.CommentStreamEnd) + GetCurrLang.CommentStreamEnd, '', [rfReplaceAll]); // maybe not the best method, but simple and quite easy end; // Remove quotes if eRemoveStrings then begin while Between(eLine, '"', '"') <> '' do eLine := StringReplace(eLine, '"' + Between(eLine, '"', '"') + '"', '', [rfReplaceAll]); end; Result := eLine; end; procedure Delay(eTime: Integer); var i: integer; begin for i := 1 to eTime do begin Sleep(1); Application.ProcessMessages; if Application.Terminated then exit; end; end; function CountChars(eIn: String; eChar: Char): Integer; var i: integer; begin Result := 0; if Length(eIn) <> 0 then begin for i := 1 to Length(eIn) do begin if eIn[i] = eChar then Inc(Result, 1); end; end; end; function Between(eText, eFirst, eSecond: String; eSecondBack: Boolean = False): String; var eTemp: String; begin Result := ''; if Pos(eFirst, eText) = PosBack(eSecond, eText) then exit; if Pos(eFirst, eText) = 0 then exit; if PosBack(eSecond, eText) < Pos(eFirst, eText) then exit; eTemp := eText; Delete(eTemp, 1, Pos(eFirst, eText) + Length(eFirst) - 1); if eSecondBack then Delete(eTemp, PosBack(eSecond, eTemp), Length(eTemp)) else Delete(eTemp, Pos(eSecond, eTemp), Length(eTemp)); Result := eTemp; end; function GetMatchingBrace(eString: String): Integer; var a, b,c : integer; begin Result := 0; if Length(eString) < 1 then exit; b := 0; c := 0; for a := 1 to Length(eString) do begin if eString[a] = '(' then begin b := b +1; c := 1; end else if eString[a] = ')' then begin b := b -1; c := 1; end; if (b = 0) and (c = 1) then begin Result := a; exit; end; end; end; function GetColoredLine(eLine: Integer): String; var eCurrStyle: String; eChars: Integer; i: integer; begin eChars := 0; if eLine <> 0 then begin for i := 0 to eLine -1 do eChars := eChars + Length(frmMain.sciEditor.Lines[i]) + 2; // +2 for #13#10 end; eCurrStyle := ''; if frmIRCPaster.chkLineNumbers.Checked then Result := IntToStr(eLine +1) + '] ' else Result := ''; if Trim(frmMain.sciEditor.Lines[eLine]) = '' then exit; for i := 0 to Length(frmMain.sciEditor.Lines[eLine]) -1 do begin if eCurrStyle <> GetStyleAt(eChars + i).Name then begin eCurrStyle := GetStyleAt(eChars + i).Name; if (eCurrStyle = 'White Space') and (Length(Result) <> Length(IntToStr(eLine +1) + '] ')) then Result := Result + ''; if eCurrStyle = 'Ok Braces' then Result := Result + '02'; if eCurrStyle = 'Bad Braces' then Result := Result + '04'; if eCurrStyle = 'White Space' then Result := Result + '12'; if eCurrStyle = 'Comment' then Result := Result + '07'; if eCurrStyle = 'Line Comment' then Result := Result + '07'; if eCurrStyle = 'Doc Comment' then Result := Result + '07'; if eCurrStyle = 'Number' then Result := Result + '12'; if eCurrStyle = 'Keyword' then Result := Result + '03'; if eCurrStyle = 'Double quoted string' then Result := Result + '04'; if eCurrStyle = 'Single quoted string' then Result := Result + '04'; if eCurrStyle = 'Symbols/UUID' then Result := Result + '04'; if eCurrStyle = 'Preprocessor' then Result := Result + '07'; if eCurrStyle = 'Operators' then Result := Result + '12'; if eCurrStyle = 'Identifier' then Result := Result + '12'; if eCurrStyle = 'Regular expressions' then Result := Result + '10'; if eCurrStyle = 'Doc Comment Line' then Result := Result + '07'; if eCurrStyle = 'User-defined keywords' then Result := Result + '04'; end; Result := Result + frmMain.sciEditor.Lines[eLine][i +1]; end; Result := StringReplace(Result, ' ', ' ', [rfReplaceAll]); end; { ------------------ NOTES ------------------ } function GetRTFText(ARichEdit: TRichedit): string; var ss: TStringStream; emptystr: string; eStr: TStringList; i: integer; begin Result := ''; emptystr := ''; ss := TStringStream.Create(emptystr); eStr := TStringList.Create; try ARichEdit.PlainText := False; ARichEdit.Lines.SaveToStream(ss); eStr.Text := StringReplace(ss.DataString, '\', '\\ ', [rfReplaceAll]); for i := 0 to eStr.Count -1 do Result := Result + '\n' + eStr[i]; Delete(Result, 1, 2); finally ss.Free; eStr.Free; end; end; procedure SetRTFText(ARichEdit: TRichedit; ARTFText: String); var ss: TStringStream; begin ARTFText := StringReplace(ARTFText, '\n', #13#10, [rfReplaceAll]); ARTFText := StringReplace(ARTFText, '\\ ', '\', [rfReplaceAll]); ss := TStringStream.Create(ARTFText); try ARichEdit.PlainText := False; ARichEdit.Lines.LoadFromStream(ss); finally ss.Free; end; end; function GetFunctionPos: Integer; var eStr: String; i: integer; begin Result := 0; eStr := Copy(eStr, 1, frmMain.sciEditor.GetCaretInLine); eStr := StringReplace(frmMain.sciEditor.Lines[frmMain.sciEditor.GetCurrentLineNumber], '^"', '', [rfReplaceAll]); if (Length(eStr) = 0) then exit; while Between(eStr, '"', '"') <> '' do eStr := StringReplace(eStr, Between(eStr, '"', '"'), '', [rfReplaceAll]); while Between(eStr, '{', '}') <> '' do eStr := StringReplace(eStr, Between(eStr, '{', '}'), '', [rfReplaceAll]); for i := Length(eStr) -1 downto 1 do begin if eStr[i] = ',' then Result := Result +1 else if eStr[i] = '(' then exit; end; end; function GetCurrFunc: String; var eStr: String; i: integer; eStart, eEnd: integer; eInString, eInArray: Boolean; begin Result := ''; eStart := 1; eEnd := -1; eInString := False; eInArray := False; eStr := frmMain.sciEditor.Lines[frmMain.sciEditor.GetCurrentLineNumber]; eStr := StringReplace(eStr, '^"', 'AB', [rfReplaceAll]); // we don't need those if (Length(eStr) = 0) then exit; for i := frmMain.sciEditor.GetCaretInLine downto 1 do begin if eStr[i] = '"' then eInString := not eInString else if (eStr[i] = '{') and (not eInString) then eInArray := True else if (eStr[i] = '}') and (not eInString) then eInArray := False else if (not eInArray) and (not eInString) and (eStr[i] = '(') then begin eEnd := i-1; break; end; end; if eEnd <> -1 then begin for i := eEnd downto 1 do begin if eStr[i] = '"' then eInString := not eInString else if (eStr[i] = '{') and (not eInString) then eInArray := True else if (eStr[i] = '}') and (not eInString) then eInArray := False else if (not eInArray) and (not eInString) and (eStr[i] = '(') then begin eStart := i+1; break; end; end; end else exit; Result := Trim(Copy(eStr, eStart, eEnd - eStart + 1)); end; end.