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