diff --git a/common/dsnlexer.cpp b/common/dsnlexer.cpp index d9ed3e719b..769e278797 100644 --- a/common/dsnlexer.cpp +++ b/common/dsnlexer.cpp @@ -50,7 +50,7 @@ static int compare( const void* a1, const void* a2 ) //------------------------------------------------------------------ -void DSNLEXER::init() +inline void DSNLEXER::init() { curTok = DSN_NONE; prevTok = DSN_NONE; @@ -502,10 +502,16 @@ L_read: { if( commentsAreTokens ) { - // save the entire line, including new line as the current token. - // the '#' character may not be at offset zero. - curText = start; // entire line is the token - cur = start; // ensure a good curOffset below + // Grab the entire current line [excluding end of line char(s)] as the + // current token. The '#' character may not be at offset zero. + + while( limit[-1] == '\n' || limit[-1] == '\r' ) + --limit; + + curText.clear(); + curText.append( start, limit ); + + cur = start; // ensure a good curOffset below curTok = DSN_COMMENT; head = limit; // do a readLine() on next call in here. goto exit; @@ -739,3 +745,23 @@ exit: // single point of exit, no returns elsewhere please. return curTok; } + +wxArrayString* DSNLEXER::ReadCommentLines() throw( IO_ERROR ) +{ + wxArrayString* ret = 0; + bool cmt_setting = SetCommentsAreTokens( true ); + int tok = NextTok(); + + if( tok == DSN_COMMENT ) + { + ret = new wxArrayString(); + + do + ret->Add( FromUTF8() ); + while( ( tok = NextTok() ) == DSN_COMMENT ); + } + + SetCommentsAreTokens( cmt_setting ); + + return ret; +} diff --git a/include/dsnlexer.h b/include/dsnlexer.h index 35da414b6a..32721226fb 100644 --- a/include/dsnlexer.h +++ b/include/dsnlexer.h @@ -101,12 +101,12 @@ protected: bool commentsAreTokens; ///< true if should return comments as tokens - int prevTok; ///< curTok from previous NextTok() call. - int curOffset; ///< offset within current line of the current token + int prevTok; ///< curTok from previous NextTok() call. + int curOffset; ///< offset within current line of the current token - int curTok; ///< the current token obtained on last NextTok() - std::string curText; ///< the text of the current token - std::string lowercase; ///< a scratch buf holding token in lowercase + int curTok; ///< the current token obtained on last NextTok() + std::string curText; ///< the text of the current token + std::string lowercase; ///< a scratch buf holding token in lowercase const KEYWORD* keywords; unsigned keywordCount; @@ -368,6 +368,19 @@ public: return old; } + /** + * Function ReadCommentLines + * checks the next sequence of tokens and reads them into a wxArrayString + * if they are comments. Reading continues until a non-comment token is + * encountered, and such last read token remains as CurTok() and as CurText(). + * No push back or "un get" mechanism is used for this support. Upon return + * you simply avoid calling NextTok() for the next token, but rather CurTok(). + * + * @return wxArrayString* - heap allocated block of comments, or NULL if none; + * caller owns the allocation and must delete if not NULL. + */ + wxArrayString* ReadCommentLines() throw( IO_ERROR ); + /** * Function IsSymbol * tests a token to see if it is a symbol. This means it cannot be a diff --git a/pcbnew/class_module.cpp b/pcbnew/class_module.cpp index b0115bb119..07284f83f8 100644 --- a/pcbnew/class_module.cpp +++ b/pcbnew/class_module.cpp @@ -53,7 +53,8 @@ MODULE::MODULE( BOARD* parent ) : - BOARD_ITEM( (BOARD_ITEM*) parent, PCB_MODULE_T ) + BOARD_ITEM( (BOARD_ITEM*) parent, PCB_MODULE_T ), + m_initial_comments( 0 ) { m_Attributs = MOD_DEFAULT; m_Layer = LAYER_N_FRONT; @@ -82,7 +83,8 @@ MODULE::MODULE( BOARD* parent ) : MODULE::MODULE( const MODULE& aModule ) : - BOARD_ITEM( aModule ) + BOARD_ITEM( aModule ), + m_initial_comments( 0 ) { m_Pos = aModule.m_Pos; m_LibRef = aModule.m_LibRef; @@ -171,6 +173,7 @@ MODULE::~MODULE() { delete m_Reference; delete m_Value; + delete m_initial_comments; } @@ -185,7 +188,7 @@ void MODULE::DrawAncre( EDA_DRAW_PANEL* panel, wxDC* DC, const wxPoint& offset, if( GetBoard()->IsElementVisible( ANCHOR_VISIBLE ) ) { - GRDrawAnchor( panel->GetClipBox(), DC, m_Pos.x, m_Pos.y, + GRDrawAnchor( panel->GetClipBox(), DC, m_Pos.x, m_Pos.y, dim_ancre, g_ColorsSettings.GetItemColor( ANCHOR_VISIBLE ) ); } diff --git a/pcbnew/class_module.h b/pcbnew/class_module.h index cc68cab05c..e78f214bc4 100644 --- a/pcbnew/class_module.h +++ b/pcbnew/class_module.h @@ -473,6 +473,30 @@ public: */ static const wxChar* ReturnStringLibNameInvalidChars( bool aUserReadable ); + /** + * Function SetInitialComments + * takes ownership of caller's heap allocated aInitialComments block. The comments + * are single line strings already containing the s-expression comments with + * optional leading whitespace and then a '#' character followed by optional + * single line text (text with no line endings, not even one). + * This block of single line comments will be output upfront of any generated + * s-expression text in the PCBIO::Format() function. + *

+ * Note that a block of single line comments constitutes a multiline block of + * single line comments. That is, the block is made of consecutive single line + * comments. + * @param aInitialComments is a heap allocated wxArrayString or NULL, which the caller + * gives up ownership of over to this MODULE. + */ + void SetInitialComments( wxArrayString* aInitialComments ) + { + delete m_initial_comments; + m_initial_comments = aInitialComments; + } + + /// Return the initial comments block or NULL if none, without transfer of ownership. + const wxArrayString* GetInitialComments() { return m_initial_comments; } + #if defined(DEBUG) virtual void Show( int nestLevel, std::ostream& os ) const { ShowDummy( os ); } // override #endif @@ -512,6 +536,9 @@ private: int m_LocalSolderPasteMargin; ///< Solder paste margin absolute value double m_LocalSolderPasteMarginRatio; ///< Solder mask margin ratio ///< value of pad size + + wxArrayString* m_initial_comments; ///< leading s-expression comments in the module, + ///< lazily allocated only if needed for speed }; #endif // MODULE_H_ diff --git a/pcbnew/kicad_plugin.cpp b/pcbnew/kicad_plugin.cpp index 181da820b7..33cd1a0bdf 100644 --- a/pcbnew/kicad_plugin.cpp +++ b/pcbnew/kicad_plugin.cpp @@ -881,6 +881,14 @@ void PCB_IO::format( PCB_TARGET* aTarget, int aNestLevel ) const void PCB_IO::format( MODULE* aModule, int aNestLevel ) const throw( IO_ERROR ) { + const wxArrayString* initial_comments = aModule->GetInitialComments(); + + if( initial_comments ) + { + for( unsigned i=0; iGetCount(); ++i ) + m_out->Print( aNestLevel, "%s\n", TO_UTF8( (*initial_comments)[i] ) ); + } + m_out->Print( aNestLevel, "(module %s", m_out->Quotew( aModule->GetLibRef() ).c_str() ); if( aModule->IsLocked() ) diff --git a/pcbnew/pcb_parser.cpp b/pcbnew/pcb_parser.cpp index efcb0f49ad..6de537f87e 100644 --- a/pcbnew/pcb_parser.cpp +++ b/pcbnew/pcb_parser.cpp @@ -328,11 +328,16 @@ S3D_MASTER* PCB_PARSER::parse3DModel() throw( PARSE_ERROR ) BOARD_ITEM* PCB_PARSER::Parse() throw( IO_ERROR, PARSE_ERROR ) { - T token; - BOARD_ITEM* item; - LOCALE_IO toggle; // toggles on, then off, the C locale. + T token; + BOARD_ITEM* item; + LOCALE_IO toggle; - token = NextTok(); + // MODULEs can be prefixed with an initial block of single line comments and these + // are kept for Format() so they round trip in s-expression form. BOARDs might + // eventually do the same, but currently do not. + std::auto_ptr initial_comments( ReadCommentLines() ); + + token = CurTok(); if( token != T_LEFT ) Expecting( T_LEFT ); @@ -347,7 +352,7 @@ BOARD_ITEM* PCB_PARSER::Parse() throw( IO_ERROR, PARSE_ERROR ) break; case T_module: - item = (BOARD_ITEM*) parseMODULE(); + item = (BOARD_ITEM*) parseMODULE( initial_comments.release() ); break; default: @@ -1514,7 +1519,7 @@ DIMENSION* PCB_PARSER::parseDIMENSION() throw( IO_ERROR, PARSE_ERROR ) } -MODULE* PCB_PARSER::parseMODULE() throw( IO_ERROR, PARSE_ERROR ) +MODULE* PCB_PARSER::parseMODULE( wxArrayString* aInitialComments ) throw( IO_ERROR, PARSE_ERROR ) { wxCHECK_MSG( CurTok() == T_module, NULL, wxT( "Cannot parse " ) + GetTokenString( CurTok() ) + wxT( " as MODULE." ) ); @@ -1524,6 +1529,8 @@ MODULE* PCB_PARSER::parseMODULE() throw( IO_ERROR, PARSE_ERROR ) auto_ptr< MODULE > module( new MODULE( m_board ) ); + module->SetInitialComments( aInitialComments ); + NeedSYMBOLorNUMBER(); module->SetLibRef( FromUTF8() ); diff --git a/pcbnew/pcb_parser.h b/pcbnew/pcb_parser.h index 92574af4e4..e679a356f3 100644 --- a/pcbnew/pcb_parser.h +++ b/pcbnew/pcb_parser.h @@ -82,18 +82,26 @@ class PCB_PARSER : public PCB_LEXER void parseSetup() throw( IO_ERROR, PARSE_ERROR ); void parseNETINFO_ITEM() throw( IO_ERROR, PARSE_ERROR ); void parseNETCLASS() throw( IO_ERROR, PARSE_ERROR ); - DRAWSEGMENT* parseDRAWSEGMENT() throw( IO_ERROR, PARSE_ERROR ); - TEXTE_PCB* parseTEXTE_PCB() throw( IO_ERROR, PARSE_ERROR ); - DIMENSION* parseDIMENSION() throw( IO_ERROR, PARSE_ERROR ); - MODULE* parseMODULE() throw( IO_ERROR, PARSE_ERROR ); - TEXTE_MODULE* parseTEXTE_MODULE() throw( IO_ERROR, PARSE_ERROR ); - EDGE_MODULE* parseEDGE_MODULE() throw( IO_ERROR, PARSE_ERROR ); - D_PAD* parseD_PAD() throw( IO_ERROR, PARSE_ERROR ); - TRACK* parseTRACK() throw( IO_ERROR, PARSE_ERROR ); - SEGVIA* parseSEGVIA() throw( IO_ERROR, PARSE_ERROR ); + + DRAWSEGMENT* parseDRAWSEGMENT() throw( IO_ERROR, PARSE_ERROR ); + TEXTE_PCB* parseTEXTE_PCB() throw( IO_ERROR, PARSE_ERROR ); + DIMENSION* parseDIMENSION() throw( IO_ERROR, PARSE_ERROR ); + + /** + * Function parseModule + * @param aInitialComments may be a pointer to a heap allocated initial comment block + * or NULL. If not NULL, then caller has given ownership of a wxArrayString to + * this function and care must be taken to delete it even on exception. + */ + MODULE* parseMODULE( wxArrayString* aInitialComments = 0 ) throw( IO_ERROR, PARSE_ERROR ); + TEXTE_MODULE* parseTEXTE_MODULE() throw( IO_ERROR, PARSE_ERROR ); + EDGE_MODULE* parseEDGE_MODULE() throw( IO_ERROR, PARSE_ERROR ); + D_PAD* parseD_PAD() throw( IO_ERROR, PARSE_ERROR ); + TRACK* parseTRACK() throw( IO_ERROR, PARSE_ERROR ); + SEGVIA* parseSEGVIA() throw( IO_ERROR, PARSE_ERROR ); ZONE_CONTAINER* parseZONE_CONTAINER() throw( IO_ERROR, PARSE_ERROR ); - PCB_TARGET* parsePCB_TARGET() throw( IO_ERROR, PARSE_ERROR ); - BOARD* parseBOARD() throw( IO_ERROR, PARSE_ERROR ); + PCB_TARGET* parsePCB_TARGET() throw( IO_ERROR, PARSE_ERROR ); + BOARD* parseBOARD() throw( IO_ERROR, PARSE_ERROR ); /**