JediCodeFormat: Add option to format only selected text. Issue #37652, patch from Domingo Galmés.

git-svn-id: trunk@63850 -
This commit is contained in:
juha 2020-08-31 07:10:22 +00:00
parent 080e21106f
commit eff8a039ba
3 changed files with 166 additions and 59 deletions

View File

@ -36,11 +36,16 @@ See http://www.gnu.org/licenses/gpl.html
interface interface
uses uses
{ freepascal }SysUtils, Classes, SysUtils, Classes,
{ lazarus design time } // BuildIntf
LazIDEIntf, SrcEditorIntf, IDEMsgIntf, ProjectIntf, IDEExternToolIntf, ProjectIntf, IDEExternToolIntf,
{ local} // IdeIntf
EditorConverter, FileConverter, ConvertTypes, jcfuiconsts; LazIDEIntf, SrcEditorIntf, IDEMsgIntf,
// LCL
Menus, Dialogs, Controls,
// local
EditorConverter, FileConverter, Converter, ConvertTypes,
JcfUIConsts, JcfStringUtils, JcfSettings, fAbout, frFiles;
type type
@ -67,6 +72,7 @@ type
constructor Create; constructor Create;
destructor Destroy; override; destructor Destroy; override;
procedure DoFormatSelection(Sender: TObject);
procedure DoFormatCurrentIDEWindow(Sender: TObject); procedure DoFormatCurrentIDEWindow(Sender: TObject);
procedure DoFormatProject(Sender: TObject); procedure DoFormatProject(Sender: TObject);
procedure DoFormatOpen(Sender: TObject); procedure DoFormatOpen(Sender: TObject);
@ -78,15 +84,6 @@ type
implementation implementation
uses
{ lazarus }
Menus, Dialogs, Controls,
{ jcf }
JcfStringUtils,
{ local }
fAbout, frFiles, JcfSettings;
function FileIsAllowedType(const psFileName: string): boolean; function FileIsAllowedType(const psFileName: string): boolean;
const const
ALLOWED_FILE_TYPES: array[1..5] of string = ('.pas', '.pp', '.dpr', '.lpr', '.dpk'); ALLOWED_FILE_TYPES: array[1..5] of string = ('.pas', '.pp', '.dpr', '.lpr', '.dpk');
@ -229,6 +226,108 @@ begin
LazarusIDE.DoOpenIDEOptions(TfFiles); LazarusIDE.DoOpenIDEOptions(TfFiles);
end; end;
//offset in bytes of first char of the lines. 1 based.
procedure FindLineOffsets(const aStr: string; aLineStart, aLineEnd: integer;
out aLineStartOffset: integer; out aLineEndOffset:integer);
var
lineCount:integer;
len:integer;
pC:PChar;
offset:integer;
begin
len:=length(aStr);
pC:=@aStr[1];
lineCount:=1;
offset:=1;
aLineStartOffset:=0;
aLineEndOffset:=0;
if len<1 then
exit;
if aLineStart=1 then
aLineStartOffset:=offset;
if (aLineEnd=1) then
aLineEndOffset:=offset;
while (offset<=len) and (lineCount<=aLineEnd) do
begin
while (offset<=len) and (pC^<>#10) do
begin
inc(offset);
inc(pC);
end;
if (pC^=#10) and (offset<len) then
begin
inc(pC);
inc(offset);
inc(lineCount);
if lineCount=aLineStart then
aLineStartOffset:=offset;
if lineCount=aLineEnd then
begin
aLineEndOffset:=offset;
exit;
end;
end
else
exit;
end;
end;
procedure TJcfIdeMain.DoFormatSelection(Sender: TObject);
var
srcEditor: TSourceEditorInterface;
procedure GetSelectedBlockFullLines(out p1: TPoint; out p2: TPoint);
begin
p1 := srcEditor.BlockBegin;
p2 := srcEditor.BlockEnd;
if (p1.y > p2.y) then
begin
p1 := srcEditor.BlockEnd;
p2 := srcEditor.BlockBegin;
end;
if p2.x<=1 then
begin
if p2.y>1 then
p2.y:=p2.y-1;
end;
p2.x:=Length(srcEditor.Lines[p2.y-1])+1; //last char
p1.x:=1;
end;
var
sourceCode: string;
BlockBegin, BlockEnd: TPoint;
fcConverter: TConverter;
lineStartOffset,lineEndOffset: integer;
wi: integer;
outputstr: string;
begin
if (SourceEditorManagerIntf = nil) or (SourceEditorManagerIntf.ActiveEditor = nil) then
begin
LogIdeMessage('', 'No current window', mtInputError, -1, -1);
exit;
end;
srcEditor := SourceEditorManagerIntf.ActiveEditor;
if (srcEditor.SelectionAvailable=false) or srcEditor.ReadOnly then
Exit;
sourceCode := srcEditor.GetText(False); //get ALL editor text.
GetSelectedBlockFullLines(BlockBegin,BlockEnd);
fcConverter := TConverter.Create;
try
fcConverter.OnStatusMessage := LogIDEMessage;
fcConverter.InputCode := sourceCode;
fcConverter.GuiMessages:=true;
FindLineOffsets(sourceCode,BlockBegin.Y,BlockEnd.Y,lineStartOffset,lineEndOffset);
fcConverter.ConvertPart(lineStartOffset,lineEndOffset,true);
wI:=length(fcConverter.OutputCode);
outputstr:=Copy(fcConverter.OutputCode,1,wI-4); // converter adds 2 line ends 0d0a 0d0a
if fcConverter.ConvertError=false then
srcEditor.ReplaceText(BlockBegin, BlockEnd, outputstr);
finally
fcConverter.Free;
end;
end;
procedure TJcfIdeMain.DoAbout(Sender: TObject); procedure TJcfIdeMain.DoAbout(Sender: TObject);
var var
lcAbout: TfrmAboutBox; lcAbout: TfrmAboutBox;

View File

@ -51,27 +51,30 @@ uses
JcfIdeMain, JcfRegistrySettings; JcfIdeMain, JcfRegistrySettings;
const const
FORMAT_MENU_NAME = 'jcfJEDICodeFormat'; FORMAT_MENU_NAME = 'jcfJEDICodeFormat';
FORMAT_CURRENT_NAME = 'jcfCurrentEditorWindow'; FORMAT_SELECTION_NAME = 'jcfSelectionText';
FORMAT_PROJECT_MENU_NAME = 'jcfAllFilesinProject'; FORMAT_CURRENT_NAME = 'jcfCurrentEditorWindow';
FORMAT_OPEN_MENU_NAME = 'jcfAllOpenWindows'; FORMAT_PROJECT_MENU_NAME = 'jcfAllFilesinProject';
FORMAT_OPEN_MENU_NAME = 'jcfAllOpenWindows';
//FORMAT_REG_SETTINGS_MENU_NAME = 'jcfRegistrySettings'; //FORMAT_REG_SETTINGS_MENU_NAME = 'jcfRegistrySettings';
FORMAT_SETTINGS_MENU_NAME = 'jcfFormatSettings'; FORMAT_SETTINGS_MENU_NAME = 'jcfFormatSettings';
FORMAT_ABOUT_MENU_NAME = 'jcfAbout'; FORMAT_ABOUT_MENU_NAME = 'jcfAbout';
FORMAT_CATEGORY_IDECMD_NAME = 'jcfFormat'; FORMAT_CATEGORY_IDECMD_NAME = 'jcfFormat';
FORMAT_MENU_SECTION1 = 'jcfSection1'; FORMAT_MENU_SECTION1 = 'jcfSection1';
FORMAT_MENU_SECTION2 = 'jcfSection2'; FORMAT_MENU_SECTION2 = 'jcfSection2';
resourcestring resourcestring
FORMAT_MENU = 'JEDI Code &Format'; FORMAT_MENU = 'JEDI Code &Format';
FORMAT_CURRENT_MENU = '&Current Editor Window'; FORMAT_SELECTION_MENU = 'Selection';
FORMAT_CURRENT_IDECMD = 'Format code in current editor window'; FORMAT_CURRENT_MENU = '&Current Editor Window';
FORMAT_PROJECT_MENU = '&All Files in Project'; FORMAT_SELECTION_IDECMD = 'Format code in Selection';
FORMAT_OPEN_MENU = 'All &Open Windows'; FORMAT_CURRENT_IDECMD = 'Format code in current editor window';
FORMAT_PROJECT_MENU = '&All Files in Project';
FORMAT_OPEN_MENU = 'All &Open Windows';
//FORMAT_REG_SETTINGS_MENU = '&Registry Settings'; //FORMAT_REG_SETTINGS_MENU = '&Registry Settings';
FORMAT_SETTINGS_MENU = '&Format Settings'; FORMAT_SETTINGS_MENU = '&Format Settings';
FORMAT_ABOUT_MENU = '&About'; FORMAT_ABOUT_MENU = '&About';
FORMAT_CATEGORY_IDECMD = 'JEDI Code Format'; FORMAT_CATEGORY_IDECMD = 'JEDI Code Format';
const const
DefaultJCFOptsFile = 'jcfsettings.cfg'; DefaultJCFOptsFile = 'jcfsettings.cfg';
@ -109,24 +112,32 @@ end;
procedure Register; procedure Register;
var var
Cat: TIDECommandCategory; Cat: TIDECommandCategory;
Key: TIDEShortCut;
fcMainMenu, SubSection: TIDEMenuSection; fcMainMenu, SubSection: TIDEMenuSection;
CmdFormatFile: TIDECommand; KeySelect, KeyUnit: TIDEShortCut;
CmdSelect, CmdUnit: TIDECommand;
begin begin
SetLazarusDefaultFileName; SetLazarusDefaultFileName;
GetDefaultSettingsFileName := IDEGetDefaultSettingsFileName; GetDefaultSettingsFileName := IDEGetDefaultSettingsFileName;
Cat := IDECommandList.CreateCategory(nil, FORMAT_CATEGORY_IDECMD_NAME, Cat := IDECommandList.CreateCategory(nil, FORMAT_CATEGORY_IDECMD_NAME,
FORMAT_CATEGORY_IDECMD, IDECmdScopeSrcEditOnly); FORMAT_CATEGORY_IDECMD, IDECmdScopeSrcEditOnly);
// Ctrl + D ?
Key := IDEShortCut(VK_D, [SSctrl], VK_UNKNOWN, []);
CmdFormatFile := RegisterIDECommand(Cat, FORMAT_CURRENT_NAME, FORMAT_CURRENT_IDECMD, Key,
lcJCFIDE.DoFormatCurrentIDEWindow);
fcMainMenu := RegisterIDESubMenu(itmSourceTools, FORMAT_MENU_NAME, FORMAT_MENU); fcMainMenu := RegisterIDESubMenu(itmSourceTools, FORMAT_MENU_NAME, FORMAT_MENU);
KeySelect := IDEShortCut(VK_UNKNOWN, []);
// We are running out of free shortcut combinations. Ctrl+Shift+Alt+D is free.
//KeySelect := IDEShortCut(VK_D, [ssShift,ssAlt,SSctrl]);
CmdSelect := RegisterIDECommand(Cat, FORMAT_SELECTION_NAME, FORMAT_SELECTION_IDECMD,
KeySelect, lcJCFIDE.DoFormatSelection);
RegisterIDEMenuCommand(fcMainMenu, FORMAT_SELECTION_NAME, FORMAT_SELECTION_MENU,
lcJCFIDE.DoFormatSelection, nil, CmdSelect);
// Ctrl + D
KeyUnit := IDEShortCut(VK_D, [SSctrl]);
CmdUnit := RegisterIDECommand(Cat, FORMAT_CURRENT_NAME, FORMAT_CURRENT_IDECMD,
KeyUnit, lcJCFIDE.DoFormatCurrentIDEWindow);
RegisterIDEMenuCommand(fcMainMenu, FORMAT_CURRENT_NAME, FORMAT_CURRENT_MENU, RegisterIDEMenuCommand(fcMainMenu, FORMAT_CURRENT_NAME, FORMAT_CURRENT_MENU,
lcJCFIDE.DoFormatCurrentIDEWindow, nil, CmdFormatFile); lcJCFIDE.DoFormatCurrentIDEWindow, nil, CmdUnit);
RegisterIDEMenuCommand(fcMainMenu, FORMAT_PROJECT_MENU_NAME, FORMAT_PROJECT_MENU, RegisterIDEMenuCommand(fcMainMenu, FORMAT_PROJECT_MENU_NAME, FORMAT_PROJECT_MENU,
lcJCFIDE.DoFormatProject); lcJCFIDE.DoFormatProject);

View File

@ -37,10 +37,11 @@ unit Converter;
interface interface
uses uses
{ delphi } SysUtils, Controls, Forms, SysUtils, strutils,
{ local } ConvertTypes, ParseTreeNode, // LCL
BuildTokenList, Controls, Forms,
BuildParseTree, BaseVisitor; // local
ConvertTypes, ParseTreeNode, BuildTokenList, BuildParseTree, BaseVisitor;
type type
@ -89,7 +90,8 @@ type
procedure Clear; procedure Clear;
procedure Convert; procedure Convert;
procedure ConvertPart(const piStartIndex, piEndIndex: Integer); procedure ConvertPart(const piStartIndex, piEndIndex: Integer;
aOnlyOutputSelection: boolean=false);
procedure CollectOutput(const pcRoot: TParseTreeNode); procedure CollectOutput(const pcRoot: TParseTreeNode);
@ -298,17 +300,10 @@ begin
// is it a leaf with source? // is it a leaf with source?
if (pcRoot is TSourceToken) then if (pcRoot is TSourceToken) then
begin fsOutputCode := fsOutputCode + TSourceToken(pcRoot).SourceCode
fsOutputCode := fsOutputCode + TSourceToken(pcRoot).SourceCode; else // recurse, write out all child nodes
end
else
begin
// recurse, write out all child nodes
for liLoop := 0 to pcRoot.ChildNodeCount - 1 do for liLoop := 0 to pcRoot.ChildNodeCount - 1 do
begin
CollectOutput(pcRoot.ChildNodes[liLoop]); CollectOutput(pcRoot.ChildNodes[liLoop]);
end;
end;
end; end;
function TConverter.GetOnStatusMessage: TStatusMessageProc; function TConverter.GetOnStatusMessage: TStatusMessageProc;
@ -365,7 +360,8 @@ begin
fShowParseTree.ShowParseTree(fcBuildParseTree.Root); fShowParseTree.ShowParseTree(fcBuildParseTree.Root);
end; end;
procedure TConverter.ConvertPart(const piStartIndex, piEndIndex: Integer); procedure TConverter.ConvertPart(const piStartIndex, piEndIndex: Integer;
aOnlyOutputSelection: boolean);
const const
FORMAT_START = '{<JCF_!*$>}'; FORMAT_START = '{<JCF_!*$>}';
FORMAT_END = '{</JCF_!*$>}'; FORMAT_END = '{</JCF_!*$>}';
@ -398,17 +394,18 @@ begin
Convert; Convert;
{ locate the markers in the output, { locate the markers in the output, and replace before and after }
and replace before and after }
liOutputStart := Pos(FORMAT_START, fsOutputCode) + Length(FORMAT_START); liOutputStart := Pos(FORMAT_START, fsOutputCode) + Length(FORMAT_START);
liOutputEnd := Pos(FORMAT_END, fsOutputCode); liOutputEnd := PosEx(FORMAT_END, fsOutputCode,liOutputStart);
{ splice } { splice }
lsNewOutput := StrLeft(fsInputCode, liRealInputStart - 1); if aOnlyOutputSelection then
lsNewOutput := lsNewOutput + Copy(fsOutputCode, liOutputStart, (liOutputEnd - liOutputStart)); lsNewOutput := Copy(fsOutputCode, liOutputStart, (liOutputEnd - liOutputStart))
lsNewOutput := lsNewOutput + StrRestOf(fsInputCode, liRealInputEnd + Length(FORMAT_START) + Length(FORMAT_END)); else begin
lsNewOutput := StrLeft(fsInputCode, liRealInputStart - 1);
lsNewOutput := lsNewOutput + Copy(fsOutputCode, liOutputStart, (liOutputEnd - liOutputStart));
lsNewOutput := lsNewOutput + StrRestOf(fsInputCode, liRealInputEnd + Length(FORMAT_START) + Length(FORMAT_END));
end;
fsOutputCode := lsNewOutput; fsOutputCode := lsNewOutput;
end; end;