Author Topic: For your consideration: AStyle patch  (Read 5939 times)

Offline DrewBoo

  • Multiple posting newcomer
  • *
  • Posts: 110
For your consideration: AStyle patch
« on: March 13, 2008, 01:03:20 am »
I just uploaded patch 2405 to BerliOS.  This patch adds features to the AStyle plugin.

Comments, questions and complaints from anyone are welcome.

https://developer.berlios.de/patch/index.php?func=detailpatch&patch_id=2405&group_id=5358

Changes to AStyle:
  • New context menu in Code::Blocks editor to format the currently active file
  • New context menu in Code::Blocks Project Manager to format any source file
  • New context menu in Code::Blocks Project Manager to format entire project  :D
  • If formatting doesn't change the file, the contents are not modified
  • If formatting only changes the spacing at the end of lines, the contents are not modified

Notes:
  • When formatting an entire project, I strived to create familiar behavior, so -- modeling the "Replace in Files" dialog -- files that are changed are automatically opened and modified in the editor, but not saved.  Files that are not changed are not opened.
  • Formatting a project uses a progress dialog, with a working cancel button
  • Formatting a project won't touch files that aren't recognized as source or header files
  • As it turns out, AStyle can at times add space to the end of a line.  This could have created unexpected behavior when used in conjunction with Code::Blocks' integrated "Strip Trailing Blanks" feature.  This is why the plugin now, when formatting a file, will not touch the file if the only changes it makes involve trailing blanks.  Without this feature, you could format an entire project, save all the files (stripping the blanks), and if you tried formatting the project again, all project files would get opened and modified again.

Affected Files:
  • astyleplugin.h
  • astyleplugin.cpp

The Patch: (it's small enough)
Code
Index: astyleplugin.cpp
===================================================================
--- astyleplugin.cpp (revision 4944)
+++ astyleplugin.cpp (working copy)
@@ -24,12 +24,25 @@
 #include <wx/xrc/xmlres.h>
 #include <wx/fs_zip.h>
 #include <wx/strconv.h>
+#include <wx/progdlg.h>
 #include "asstreamiterator.h"
 #include "cbstyledtextctrl.h"
 
 using std::istringstream;
 using std::string;
 
+namespace
+{
+ const int idCodeFormatterFile = wxNewId();
+ const int idCodeFormatterActiveFile = wxNewId();
+ const int idCodeFormatterProject = wxNewId();
+}
+
+BEGIN_EVENT_TABLE( AStylePlugin, cbPlugin )
+ EVT_MENU( idCodeFormatterActiveFile, AStylePlugin::OnFormatActiveFile )
+ EVT_MENU( idCodeFormatterProject, AStylePlugin::OnFormatProject )
+END_EVENT_TABLE()
+
 // this auto-registers the plugin
 
 namespace
@@ -78,6 +91,97 @@
     return dlg;
 }
 
+void AStylePlugin::BuildModuleMenu( const ModuleType type, wxMenu* menu, const FileTreeData* data )
+{
+ if ( !menu || !IsAttached() )
+ return;
+
+ switch ( type )
+ {
+ case mtEditorManager:
+ menu->AppendSeparator();
+ menu->Append( idCodeFormatterActiveFile, _( "Format This File (AStyle)" ), _( "Format the source code in the current file" ) );
+ break;
+
+ case mtProjectManager:
+ if ( data ) switch ( data->GetKind() )
+ {
+ case FileTreeData::ftdkProject:
+ menu->AppendSeparator();
+ menu->Append( idCodeFormatterProject, _( "Format This Project (AStyle)" ), _( "Format the source code in this project" ) );
+ break;
+
+ case FileTreeData::ftdkFile:
+ menu->AppendSeparator();
+ menu->Append( idCodeFormatterProject, _( "Format This File (AStyle)" ), _( "Format the source code in this file" ) );
+ break;
+
+ default:
+ // Do nothing.
+ break;
+ }
+ break;
+
+ default:
+ break;
+ }
+}
+
+void AStylePlugin::OnFormatProject( wxCommandEvent& event )
+{
+ wxTreeCtrl *tree = Manager::Get()->GetProjectManager()->GetTree();
+
+ if ( NULL == tree )
+ return;
+
+ wxTreeItemId treeItem =  tree->GetSelection();
+
+ if ( false == treeItem.IsOk() )
+ return;
+
+ const FileTreeData *data = static_cast<FileTreeData*>( tree->GetItemData( treeItem ) );
+
+ if ( NULL == data )
+ return;
+
+ switch ( data->GetKind() )
+ {
+ case FileTreeData::ftdkProject:
+ {
+ cbProject* prj = data->GetProject();
+ wxProgressDialog progressDlg(_("Please wait"), _("Formatting..."), prj->GetFilesCount(), NULL, wxPD_CAN_ABORT|wxPD_AUTO_HIDE|wxPD_SMOOTH );
+ progressDlg.Show();
+ for ( int i = 0; i < prj->GetFilesCount(); ++i )
+ {
+ ProjectFile* pf = prj->GetFile( i );
+ wxString filename = pf->file.GetFullPath();
+
+ FileType fileType = FileTypeOf( filename );
+ if ( fileType == ftSource || fileType == ftHeader )
+ {
+ FormatFile( filename );
+ if ( false == progressDlg.Update( i, wxString("Formatting ") + pf->relativeFilename ) )
+ break;
+ }
+ }
+ }
+ break;
+
+ case FileTreeData::ftdkFile:
+ {
+ ProjectFile* f = data->GetProject()->GetFile( data->GetFileIndex() );
+ if ( f )
+ FormatFile( f->file.GetFullPath() );
+ }
+ break;
+ }
+}
+
+void AStylePlugin::OnFormatActiveFile( wxCommandEvent& event )
+{
+ Execute();
+}
+
 int AStylePlugin::Execute()
 {
     if (!IsAttached())
@@ -92,10 +196,96 @@
         return 0;
     }
 
+    FormatEditor( ed );
+}
+
+void AStylePlugin::FormatFile( const wxString &filename )
+{
+ cbEditor* ed = Manager::Get()->GetEditorManager()->IsBuiltinOpen( filename );
+
+ if ( ed )
+ {
+ // File is already open
+ FormatEditor( ed );
+ }
+ else
+ {
+ // File is not open.  We must open it.
+ ed = Manager::Get()->GetEditorManager()->Open( filename );
+
+ if ( ed )
+ {
+ bool changed = FormatEditor( ed );
+
+ if ( !changed )
+ {
+ // We opened a file and it didn't change.  Close it.
+ Manager::Get()->GetEditorManager()->Close( filename );
+ }
+ }
+ }
+}
+
+// Special code to compare strings which doesn't care
+// about spaces leading up to the EOL.
+static bool BuffersDiffer( const wxString &a, const wxString &b )
+{
+ const wxChar *aCurrent = a.c_str();
+ const wxChar *bCurrent = b.c_str();
+ const wxChar * const aEnd = aCurrent + a.Len();
+ const wxChar * const bEnd = bCurrent + b.Len();
+
+ while ( aCurrent != aEnd && bCurrent != bEnd )
+ {
+ if ( *aCurrent != *bCurrent )
+ {
+ // Check for varying space at EOL
+ while ( *aCurrent == ' ' || *aCurrent == '\t' )
+ {
+ if ( ++aCurrent == aEnd )
+ break;
+ }
+ while ( *bCurrent == ' ' || *bCurrent == '\t' )
+ {
+ if ( ++bCurrent == bEnd )
+ break;
+ }
+
+ // Make sure it was at EOL
+ if ( ( *aCurrent != '\r' && *aCurrent != '\n' ) || ( *bCurrent != '\r' && *bCurrent != '\n' ) )
+ return true;
+ }
+
+ ++aCurrent;
+ ++bCurrent;
+ }
+
+ while ( aCurrent != aEnd )
+ {
+ if ( *aCurrent != ' ' && *aCurrent != '\t' )
+ {
+ return true;
+ }
+
+ }
+
+ while ( bCurrent != bEnd )
+ {
+ if ( *bCurrent != ' ' && *bCurrent != '\t' )
+ {
+ return true;
+ }
+ }
+
+ return false;
+}
+
+bool AStylePlugin::FormatEditor( cbEditor *ed )
+{
     if (ed->GetControl()->GetReadOnly())
     {
         cbMessageBox(_("The file is read-only"), _("Error"), wxICON_ERROR);
-        return 0;
+        return false;
     }
 
     wxString edText(ed->GetControl()->GetText());
@@ -167,20 +357,24 @@
 
     int pos = ed->GetControl()->GetCurrentPos();
 
-    ed->GetControl()->BeginUndoAction();
-    ed->GetControl()->SetText(formattedText);
+    bool changed = BuffersDiffer( formattedText, edText );
 
-    for (std::vector<int>::const_iterator i = new_bookmark.begin(); i != new_bookmark.end(); ++i)
-    {
-        ed->ToggleBookmark(*i);
-    }
+ if ( changed )
+ {
+ ed->GetControl()->BeginUndoAction();
+ ed->GetControl()->SetText(formattedText);
 
-    ed->GetControl()->EndUndoAction();
+ for (std::vector<int>::const_iterator i = new_bookmark.begin(); i != new_bookmark.end(); ++i)
+ {
+ ed->ToggleBookmark(*i);
+ }
 
-    ed->GetControl()->GotoPos(pos);
-    ed->SetModified(true);
+ ed->GetControl()->EndUndoAction();
+ ed->GetControl()->GotoPos(pos);
+ ed->SetModified(true);
+ }
 
     wxSetCursor(wxNullCursor);
 
-    return 0;
+ return changed;
 }
Index: astyleplugin.h
===================================================================
--- astyleplugin.h (revision 4944)
+++ astyleplugin.h (working copy)
@@ -28,9 +28,18 @@
     int Configure();
     int GetConfigurationGroup() const { return cgEditor; }
     cbConfigurationPanel* GetConfigurationPanel(wxWindow* parent);
+ void BuildModuleMenu( const ModuleType type, wxMenu* menu, const FileTreeData* data = 0 );
     int Execute();
+
+ void OnFormatActiveFile( wxCommandEvent& event );
+ void OnFormatProject( wxCommandEvent& event );
+ void FormatFile( const wxString &filename );
+ bool FormatEditor( cbEditor *ed );
+
     void OnAttach(); // fires when the plugin is attached to the application
     void OnRelease(bool appShutDown); // fires when the plugin is released from the application
+
+ DECLARE_EVENT_TABLE()
 };
 
 #endif // ASTYLEPLUGIN_H

« Last Edit: March 13, 2008, 05:18:04 am by DrewBoo »

Offline JGM

  • Lives here!
  • ****
  • Posts: 518
  • Got to practice :)
Re: For your consideration: AStyle patch
« Reply #1 on: March 13, 2008, 01:23:53 am »
nice work! :D

Offline Ceniza

  • Developer
  • Lives here!
  • *****
  • Posts: 1441
    • CenizaSOFT
Re: For your consideration: AStyle patch
« Reply #2 on: March 13, 2008, 12:53:43 pm »
Looks good to me, but a bit cruel for huge projects. Just imagine running that on the Code::Blocks project file (or try it if you prefer :P).

I made a few minor changes to the patch (NULL/false comparisons to !, NULL to 0, removal of warnings), but the functionality remains untouched. Nice job, and thank you for it.

Patch applied.

Offline DrewBoo

  • Multiple posting newcomer
  • *
  • Posts: 110
Re: For your consideration: AStyle patch
« Reply #3 on: March 13, 2008, 04:09:36 pm »
Looks good to me, but a bit cruel for huge projects. Just imagine running that on the Code::Blocks project file (or try it if you prefer :P).

Yeah, I didn't have any huge projects to test it on, but a tiny buzzing in the back of my brain told me to add the progress dialog with the cancel button.

I made a few minor changes to the patch (NULL/false comparisons to !, NULL to 0, removal of warnings), but the functionality remains untouched. Nice job, and thank you for it.

Patch applied.

Absolutely my pleasure, Ceniza.

What compiler and warning level were you using when you got compile warnings?  I could try to match that next time I'm feeling ambitious.

Is there a coding style guideline that I could look at so my code was more consistent with the rest of the codebase?


Offline JGM

  • Lives here!
  • ****
  • Posts: 518
  • Got to practice :)
Re: For your consideration: AStyle patch
« Reply #4 on: March 13, 2008, 04:39:17 pm »
Is there a coding style guideline that I could look at so my code was more consistent with the rest of the codebase?

http://wiki.codeblocks.org/index.php?title=Coding_style

Offline Ceniza

  • Developer
  • Lives here!
  • *****
  • Posts: 1441
    • CenizaSOFT
Re: For your consideration: AStyle patch
« Reply #5 on: March 13, 2008, 06:04:27 pm »
What compiler and warning level were you using when you got compile warnings?  I could try to match that next time I'm feeling ambitious.

g++ 4.2.1-dw2 (mingw32-2). I haven't checked, but the warning level must be -Wall. I just compiled it using the Code::Blocks project file, so it's whatever it uses.

Is there a coding style guideline that I could look at so my code was more consistent with the rest of the codebase?

JGM gave you the link already. If you can follow it, the better. I haven't even checked that link.