fix #280 - auto detection of indent mode + add convert dialog

This commit is contained in:
Basile Burg 2018-04-07 21:55:57 +02:00
parent 1b8a879291
commit 5936b5d760
2 changed files with 186 additions and 54 deletions

View File

@ -26,7 +26,7 @@ const
type
TIndentationMode = (imSpaces, imTabs);
TIndentationMode = (imNone, imSpaces, imTabs, imMixed);
THasMain = (mainNo, mainYes, mainDefaultBehavior);
@ -299,16 +299,6 @@ type
*)
function globToReg(const glob: string ): string;
(**
* Detects the main indentation mode used in a file
*)
function indentationMode(strings: TStrings): TIndentationMode;
(**
* Detects the main indentation mode used in a file
*)
function indentationMode(const fname: string): TIndentationMode;
(**
* Removes duplicate items in strings
*)
@ -1323,41 +1313,6 @@ begin
end;
end;
function indentationMode(strings: TStrings): TIndentationMode;
var
i: integer;
s: string;
tabs: integer = 0;
spcs: integer = 0;
begin
for s in strings do
for i := 1 to s.length do
begin
case s[i] of
#9: tabs += 1;
' ': spcs += 1;
else break;
end;
end;
if spcs >= tabs then
result := imSpaces
else
result := imTabs;
end;
function indentationMode(const fname: string): TIndentationMode;
var
str: TStringList;
begin
str := TStringList.Create;
try
str.LoadFromFile(fname);
result := indentationMode(str);
finally
str.Free;
end;
end;
function openUrl(const value: string): boolean;
{$IFDEF WINDOWS}
function GetDefaultBrowserForCurrentUser: String;

View File

@ -9,7 +9,7 @@ uses
SynEdit, SynPluginSyncroEdit, SynCompletion, SynEditKeyCmds, LazSynEditText,
SynHighlighterLFM, SynEditHighlighter, SynEditMouseCmds, SynEditFoldedView,
SynEditMarks, SynEditTypes, SynHighlighterJScript, SynBeautifier, dialogs,
md5,
md5, Spin,
//SynEditMarkupFoldColoring,
Clipbrd, fpjson, jsonparser, LazUTF8, LazUTF8Classes, Buttons, StdCtrls,
ce_common, ce_writableComponent, ce_d2syn, ce_txtsyn, ce_dialogs, ce_dastworx,
@ -316,6 +316,8 @@ type
procedure save;
procedure saveTempFile;
//
function indentationMode: TIndentationMode;
procedure forceIndentation(m: TIndentationMode; w: integer);
procedure addBreakPoint(line: integer);
procedure removeBreakPoint(line: integer);
procedure curlyBraceCloseAndIndent;
@ -396,6 +398,14 @@ type
constructor construct(editor: TCESynMemo);
end;
TMixedIndetationDialog = class(TForm)
private
class var fSpacesPerTab: integer;
procedure spinSpacesPerTabChange(sender: TObject);
public
constructor construct();
end;
procedure SetDefaultCoeditKeystrokes(ed: TSynEdit);
function CustomStringToCommand(const Ident: string; var Int: Longint): Boolean;
@ -470,13 +480,13 @@ begin
Font.Size:= FontSize;
result := inherited CalcHintRect(MaxWidth, AHint, AData);
end;
{$REGION TSortDialog -----------------------------------------------------------}
constructor TSortDialog.construct(editor: TCESynMemo);
var
pnl: TPanel;
begin
inherited Create(nil);
BorderStyle:= bsToolWindow;
fEditor := editor;
width := 150;
@ -560,6 +570,64 @@ begin
end;
{$ENDREGION}
{$REGION TMixedIndetationDialog}
constructor TMixedIndetationDialog.construct();
begin
inherited create(nil);
BorderStyle:= bsToolWindow;
caption := 'Indentation converter';
Position:= TPosition.poMainFormCenter;
fSpacesPerTab := 4;
with TButton.Create(self) do
begin
Align:= alBottom;
parent := self;
caption := 'Do nothing';
AutoSize:= true;
ModalResult:= 1;
BorderSpacing.Around:=4;
end;
with TSpinEdit.Create(self) do
begin
value := fSpacesPerTab;
Align:= alTop;
parent := self;
Caption := 'Spaces per TAB';
MinValue:= 1;
MaxValue:= 8;
BorderSpacing.Around:=4;
OnChange:= @spinSpacesPerTabChange;
hint := 'defines how many spaces per TAB will be used';
ShowHint:=true;
end;
with TButton.Create(self) do
begin
Align:= alTop;
parent := self;
caption := 'Always use tabs';
AutoSize:= true;
ModalResult:= 10;
BorderSpacing.Around:=4;
end;
with TButton.Create(self) do
begin
Align:= alTop;
parent := self;
caption := 'Always use spaces';
AutoSize:= true;
ModalResult:= 11;
BorderSpacing.Around:=4;
end;
width := ScaleX(280, 96);
height := ScaleY(150, 96);
end;
procedure TMixedIndetationDialog.spinSpacesPerTabChange(sender: TObject);
begin
self.fSpacesPerTab:= TSpinEdit(sender).Value;
end;
{$ENDREGION}
{$REGION TCESynMemoCache -------------------------------------------------------}
constructor TCESynMemoCache.create(aComponent: TComponent);
begin
@ -1374,6 +1442,93 @@ begin
end;
end;
function TCESynMemo.indentationMode: TIndentationMode;
function checkLine(index: integer): TIndentationMode;
var
u: string;
begin
result := imNone;
u := Lines[index];
if (u.length > 0) and (u[1] = #9) then
result := imTabs
else if (u.length > 1) and (u[1..2] = ' ') then
result := imSpaces;
end;
var
i: integer;
t: integer = 0;
s: integer = 0;
begin
for i:= 0 to lines.count-1 do
begin
result := checkLine(i);
t += byte(result = imTabs);
s += byte(result = imSpaces);
end;
if (t <> 0) and (s <> 0) then
result := imMixed
else if t = 0 then
result := imSpaces
else if s = 0 then
result := imTabs
else
result := imNone;
end;
procedure TCESynMemo.forceIndentation(m: TIndentationMode; w: integer);
var
s: string;
i: integer;
p: integer;
c: integer;
b: string;
begin
for i:= 0 to lines.Count-1 do
begin
c := 0;
p := 1;
s := lines.Strings[i];
case m of
imTabs:
begin
while p <= s.length do
begin
if s[p] = ' ' then
c+=1
else break;
p += 1;
end;
if c >= w then
begin
setLength(b, c div w);
FillChar(b[1], b.length, #9);
s := b + s[c+1 .. s.length];
lines[i] := s;
fModified:=true;
end;
end;
imSpaces:
begin
while p <= s.length do
begin
if s[p] = #9 then
c+=1
else break;
p += 1;
end;
if c > 0 then
begin
setLength(b, c * w);
FillChar(b[1], b.length, ' ');
s := b + s[c+1 .. s.length];
lines[i] := s;
fModified:=true;
end;
end;
end;
end;
end;
procedure TCESynMemo.insertLeadingDDocSymbol(c: char);
begin
BeginUndoBlock;
@ -2853,12 +3008,34 @@ begin
loadCache;
fCacheLoaded := true;
end;
if detectIndentMode then
begin
case indentationMode(lines) of
imTabs: Options:= Options - [eoTabsToSpaces];
imSpaces: Options:= Options + [eoTabsToSpaces];
end;
case indentationMode() of
imTabs:
if detectIndentMode then
Options:= Options - [eoTabsToSpaces];
imSpaces:
if detectIndentMode then
Options:= Options + [eoTabsToSpaces];
imMixed:
if (isDSource or alwaysAdvancedFeatures) and
(dlgYesNo('Mixed indentation style detected, ' +
'do you wish to convert to a single mode ?') = mrYes) then
with TMixedIndetationDialog.construct() do
try
case ShowModal of
10:
begin
forceIndentation(imTabs, TMixedIndetationDialog.fSpacesPerTab);
Options:= Options - [eoTabsToSpaces];
end;
11:
begin
forceIndentation(imSpaces, TMixedIndetationDialog.fSpacesPerTab);
Options:= Options + [eoTabsToSpaces];
end;
end;
finally
free;
end;
end;
subjDocChanged(TCEMultiDocSubject(fMultiDocSubject), self);
fCanDscan := true;