1 #include "SmartIndentHDL.h"
2 
3 #include <sdk.h> // Code::Blocks SDK
4 
5 #ifndef CB_PRECOMP
6     #include <cbeditor.h>
7     #include <configmanager.h>
8     #include <editormanager.h>
9     #include <editorcolourset.h>
10     #include <manager.h>
11 #endif
12 
13 #include <cbstyledtextctrl.h>
14 
15 // Register the plugin with Code::Blocks.
16 // We are using an anonymous namespace so we don't litter the global one.
17 namespace
18 {
19     PluginRegistrant<SmartIndentHDL> reg(wxT("SmartIndentHDL"));
20 }
21 
OnEditorHook(cbEditor * ed,wxScintillaEvent & event) const22 void SmartIndentHDL::OnEditorHook(cbEditor* ed, wxScintillaEvent& event) const
23 {
24     // check if smart indent is enabled
25     // check the event type and the currently set language
26     // if it is not a CharAdded event or the language is neither VHDL nor Verilog return
27 
28     if (!ed)
29         return;
30 
31     if ( !SmartIndentEnabled() )
32         return;
33 
34     wxEventType type = event.GetEventType();
35     if ( type != wxEVT_SCI_CHARADDED )
36         return;
37 
38     cbStyledTextCtrl *stc = ed->GetControl();
39     if (!stc)
40         return;
41 
42     wxString langname = Manager::Get()->GetEditorManager()->GetColourSet()->GetLanguageName(ed->GetLanguage());
43     if ( (langname != wxT("VHDL") ) && (langname != wxT("Verilog") ) )
44         return;
45 
46     ed->AutoIndentDone(); // we are responsible.
47 
48     wxChar ch = event.GetKey();
49     if ( (ch == wxT('\n')) || ( (stc->GetEOLMode() == wxSCI_EOL_CR) && (ch == wxT('\r')) ) )
50         DoIndent(ed, langname);   // indent because \n added
51     else if ( ch != wxT(' ') )
52         DoUnIndent(ed, langname); // un-indent because not a newline added
53 
54     bool braceCompleted = false;
55     if ( SelectionBraceCompletionEnabled() || stc->IsBraceShortcutActive() )
56         braceCompleted = stc->DoSelectionBraceCompletion(ch);
57     if (!braceCompleted && BraceCompletionEnabled())
58         stc->DoBraceCompletion(ch);
59 }
OnCCDone(cbEditor * ed)60 void SmartIndentHDL::OnCCDone(cbEditor *ed)
61 {
62 
63     if (!ed)
64         return;
65 
66     if ( !SmartIndentEnabled() )
67         return;
68 
69     cbStyledTextCtrl *stc = ed->GetControl();
70     if (!stc)
71         return;
72 
73     wxString langname = Manager::Get()->GetEditorManager()->GetColourSet()->GetLanguageName(ed->GetLanguage());
74     if ( langname != wxT("VHDL") )
75         return;
76 
77     DoUnIndent(ed, langname);
78 }
79 
FindBlockStartVHDL(cbEditor * ed,int position,wxString block) const80 int SmartIndentHDL::FindBlockStartVHDL(cbEditor* ed, int position, wxString block) const
81 {
82     cbStyledTextCtrl* stc = ed->GetControl();
83     if (!stc) return -1;
84 
85     int lvl = 0;
86     int pos = position;
87 
88     do
89     {
90         pos = stc->FindText(pos, 0, block, wxSCI_FIND_WHOLEWORD);
91         if ( pos != -1 )
92         {
93             if (  GetLastNonCommentWord(ed, pos, 1).Lower().IsSameAs( wxT("end") ) )
94                 ++lvl;
95             else
96             {
97                 if ( lvl == 0 )
98                     return pos;
99                 --lvl;
100                 continue;
101             }
102         }
103     }
104     while( pos != -1 );
105 
106     return -1;
107 }
108 
DoIndent(cbEditor * ed,const wxString & langname) const109 void SmartIndentHDL::DoIndent(cbEditor* ed, const wxString& langname) const
110 {
111     cbStyledTextCtrl* stc = ed->GetControl();
112 
113     int pos = stc->GetCurrentPos();
114     const int currLine = stc->LineFromPosition(pos);
115     if (currLine == 0)
116         return;
117 
118     stc->BeginUndoAction();
119 
120     // always do auto indent if smart indent is enabled
121     wxString indent = ed->GetLineIndentString(currLine - 1);
122     stc->InsertText(pos, indent);
123     pos += indent.Length();
124     stc->GotoPos(pos);
125     stc->ChooseCaretX();
126 
127     wxChar b = GetLastNonWhitespaceChar(ed);
128     bool newlineindent = false;
129 
130     if ( langname == wxT("VHDL") )
131     {
132         if (b == wxT('('))// generic/port list/map of entity/component
133             newlineindent = true;
134         else
135         {
136             wxString lw = GetLastNonCommentWord(ed).Lower();
137             wxString l2w = GetLastNonCommentWord(ed, -1, 2).Lower();
138             //Manager::Get()->GetLogManager()->Log(wxT("l2w: ") + l2w);
139             l2w =  l2w.Mid(0,3);
140             bool IsEnd = l2w.IsSameAs(wxT("end"));
141 
142 
143             wxString lc = GetLastNonWhitespaceChars(ed, -1, 2);
144             if (
145                 lw == wxT("is")                  || // architecture/entity ... is
146                                                    // case ... is
147                                                    // block ... is
148                 lw == wxT("block")               ||
149                 lw == wxT("begin")               || // body of architecture
150                (lw == wxT("if") && !IsEnd)       || // indent the following condition
151                 lw == wxT("elsif")               || // indent the following condition
152                 //lw == wxT("case")                 || // indent the following condition
153                 lw == wxT("then")                || // if/elsif ... then
154                 lw == wxT("else")                || // indent body
155                (lw == wxT("loop") && !IsEnd)     || // indent body of loop
156                 lw == wxT("for")                 || // indent condition
157                 lw == wxT("while")               || // indent condition
158 
159                 lw == wxT("with")                || // indent expression
160                 lw == wxT("select")              || // indent target <= waveform
161                (lw == wxT("generate") && !IsEnd) ||
162                (lw == wxT("process") && !IsEnd)  ||
163 
164                 lw == wxT("block")               ||
165                 lw == wxT("entity")              ||
166                 lw == wxT("architecture")        ||
167                 lw == wxT("component")           ||
168                 lw == wxT("package")             ||
169                 lw == wxT("configuration")       ||
170                 lw == wxT("procedure")           ||
171                 lw == wxT("function")            ||
172                 lw == wxT("record")              ||
173                 lc == wxT("=>")
174             ) // if
175             {
176                 newlineindent = true;
177             }
178         }
179     }
180 
181     if ( langname == wxT("Verilog") )
182     {
183         wxString lw = GetLastNonCommentWord(ed);
184         if ( lw.IsSameAs( wxT("begin") ) )
185             newlineindent = true;
186     }
187 
188     if ( newlineindent )
189     {
190         wxString nl_indent;
191         Indent(stc, nl_indent);
192         stc->InsertText(pos, nl_indent);
193         stc->GotoPos(pos + nl_indent.Length());
194         stc->ChooseCaretX();
195     }
196     stc->EndUndoAction();
197 }
198 
DoUnIndent(cbEditor * ed,const wxString & langname) const199 void SmartIndentHDL::DoUnIndent(cbEditor* ed, const wxString& langname) const
200 {
201     cbStyledTextCtrl* stc = ed->GetControl();
202 
203     if ( langname == wxT("VHDL") )
204     {
205         wxString strWithSpaces = stc->GetLine(stc->GetCurrentLine());
206         strWithSpaces.Trim(false).Trim(true);
207         wxString str(strWithSpaces.Lower());
208         str.Replace(wxT("\t"), wxT(" "));
209         // to be sure that there are only single spaces:
210         size_t r;
211         do
212             r = str.Replace(wxT("  "), wxT(" "), true);
213         while (r);
214         int pos = stc->GetLineEndPosition(stc->GetCurrentLine())-2;
215         if (
216             str.IsSameAs(wxT("then"))   ||
217             str.IsSameAs(wxT("elsif"))  ||
218             str.IsSameAs(wxT("else"))   ||
219             str.IsSameAs(wxT("end if"))
220         )
221             pos = FindBlockStartVHDL(ed, pos-1, wxT("if") );
222         else if (
223             str.IsSameAs(wxT("when"))     ||
224             str.IsSameAs(wxT("end case"))
225         )
226             pos = FindBlockStartVHDL(ed, pos-3, wxT("case") );
227         else if ( str.IsSameAs(wxT("end process")) )
228             pos = FindBlockStartVHDL(ed, pos-6, wxT("process") );
229         else if ( str.IsSameAs(wxT("end entity") ) )
230             pos = FindBlockStartVHDL(ed, pos-5, wxT("entity") );
231         else if ( str.IsSameAs(wxT("end architecture") ) )
232             pos = FindBlockStartVHDL(ed, pos-5, wxT("architecture") );
233         else if ( str.IsSameAs(wxT("end configuration") ) )
234             pos = FindBlockStartVHDL(ed, pos-5, wxT("configuration") );
235         else if ( str.IsSameAs(wxT("end record") ) )
236             pos = FindBlockStartVHDL(ed, pos-5, wxT("record") );
237         else if ( str.IsSameAs( wxT("end for") ) )
238             // is it possible to have a "wait for ..." or
239             // "for ... generate" or "for ... loop"
240             // in a block configuration? i believe not
241             // so this should be enough:
242             pos = FindBlockStartVHDL(ed, pos-2, wxT("for") );
243         else if ( str.IsSameAs( wxT("end component") ) )
244             pos = FindBlockStartVHDL(ed, pos-8, wxT("component") );
245         else if ( str.IsSameAs( wxT(")") ) )
246             pos = FindBlockStart(stc, pos, wxT('('), wxT(')'));
247         else if ( str.IsSameAs(wxT("end loop")) )
248             // assuming that "for/while" and "loop" are on the same line:
249             pos = FindBlockStartVHDL(ed, pos-3, wxT("loop"));
250         else if ( str.IsSameAs( wxT("end generate") ) )
251             // assuming that "if/for ..." and "generate" are on the same line:
252             pos = FindBlockStartVHDL(ed, pos-7, wxT("generate") );
253         else if ( str.IsSameAs(_T("end block")) )
254             pos = FindBlockStartVHDL(ed, pos-4, _T("block") );
255         else if ( str.IsSameAs(_T("end procedure")) )
256             pos = FindBlockStartVHDL(ed, pos-9, _T("procedure") );
257         else if ( str.IsSameAs(_T("end function")) )
258             pos = FindBlockStartVHDL(ed, pos-8, _T("function") );
259         else if ( str.IsSameAs( wxT("begin") ) )
260         {
261             pos = -1;
262         }
263         else
264             pos = -1;
265 
266         if (pos != -1)
267         {
268             wxString indent = ed->GetLineIndentString(stc->LineFromPosition(pos));
269             indent += strWithSpaces;
270             stc->BeginUndoAction();
271             stc->DelLineLeft();
272             stc->DelLineRight();
273             pos = stc->GetCurrentPos();
274             stc->InsertText(pos, indent);
275             stc->GotoPos(pos + indent.Length());
276             stc->ChooseCaretX();
277             stc->EndUndoAction();
278         }
279     }
280     if ( langname == wxT("Verilog") )
281     {
282         wxString str = stc->GetLine(stc->GetCurrentLine());
283         str.Trim(false).Trim(true);
284 
285         int pos = stc->GetCurrentPos() - 4;
286         if ( str.Matches( wxT("end")) )
287             pos = FindBlockStart(stc, pos, wxT("begin"), wxT("end") );
288         else
289             pos = -1;
290         if (pos != -1)
291         {
292             wxString indent = ed->GetLineIndentString(stc->LineFromPosition(pos));
293             indent += str;
294             stc->BeginUndoAction();
295             stc->DelLineLeft();
296             stc->DelLineRight();
297             pos = stc->GetCurrentPos();
298             stc->InsertText(pos, indent);
299             stc->GotoPos(pos + indent.Length());
300             stc->ChooseCaretX();
301             stc->EndUndoAction();
302         }
303     }
304 }
305