ShaderEditor.cpp 17.5 KB
Newer Older
1
#include <cstring>
2
3
4
#include "ShaderEditor.h"
#include "Renderer.h"
#include "PropSetSimple.h"
Gargaj's avatar
Gargaj committed
5
#include "Clipboard.h"
6

7
8
9
10
11
12
13
14
const int statementLookback = 20;
const int statementIndentStyleNumber = 5;
const int blockAndStatementEndStyleNumber = 10;
std::string statementIndent[] = { "case", "default", "do", "else", "for", "if", "while" };
const char statementEnd = ';';
const char blockStart = '{';
const char blockEnd = '}';

15
16
ShaderEditor::ShaderEditor( Scintilla::Surface *s )
{
Gargaj's avatar
Gargaj committed
17
  bReadOnly = false;
18
  surfaceWindow = s;
Gargaj's avatar
Gargaj committed
19
20
  sFontFile = "";
  nFontSize = 16;
21
  bHasMouseCapture = false;
22
  nOpacity = 0xC0;
23
24
25
26
27
28
29
30
31
32
33
34
}

void ShaderEditor::SetAStyle(int style, Scintilla::ColourDesired fore, Scintilla::ColourDesired back, int size, const char *face)
{
  WndProc(SCI_STYLESETFORE, style, (sptr_t)fore.AsLong());
  WndProc(SCI_STYLESETBACK, style, (sptr_t)back.AsLong());
  if (size >= 1)
    WndProc(SCI_STYLESETSIZE, style, size);
  if (face) 
    WndProc(SCI_STYLESETFONT, style, reinterpret_cast<sptr_t>(face));
}

35
#define BACKGROUND(x) ( (x) | (nOpacity << 24) )
36

Gargaj's avatar
Gargaj committed
37
38
39
40
41
42
43
44
45
46
const size_t NB_FOLDER_STATE = 7;
const size_t FOLDER_TYPE = 0;

const int markersArray[][NB_FOLDER_STATE] = {
  {SC_MARKNUM_FOLDEROPEN, SC_MARKNUM_FOLDER, SC_MARKNUM_FOLDERSUB, SC_MARKNUM_FOLDERTAIL, SC_MARKNUM_FOLDEREND,        SC_MARKNUM_FOLDEROPENMID,     SC_MARKNUM_FOLDERMIDTAIL},
  {SC_MARK_MINUS,         SC_MARK_PLUS,      SC_MARK_EMPTY,        SC_MARK_EMPTY,         SC_MARK_EMPTY,               SC_MARK_EMPTY,                SC_MARK_EMPTY},
  {SC_MARK_ARROWDOWN,     SC_MARK_ARROW,     SC_MARK_EMPTY,        SC_MARK_EMPTY,         SC_MARK_EMPTY,               SC_MARK_EMPTY,                SC_MARK_EMPTY},
  {SC_MARK_CIRCLEMINUS,   SC_MARK_CIRCLEPLUS,SC_MARK_VLINE,        SC_MARK_LCORNERCURVE,  SC_MARK_CIRCLEPLUSCONNECTED, SC_MARK_CIRCLEMINUSCONNECTED, SC_MARK_TCORNERCURVE},
  {SC_MARK_BOXMINUS,      SC_MARK_BOXPLUS,   SC_MARK_VLINE,        SC_MARK_LCORNER,       SC_MARK_BOXPLUSCONNECTED,    SC_MARK_BOXMINUSCONNECTED,    SC_MARK_TCORNER}
};
47
48
49
extern const char * shaderKeyword;
extern const char * shaderType;
extern const char * shaderBuiltin;
Gargaj's avatar
Gargaj committed
50

Gargaj's avatar
Gargaj committed
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
using namespace Scintilla;
class Scintilla::LexState : public LexInterface {
  const LexerModule *lexCurrent;
  void SetLexerModule(const LexerModule *lex);
  PropSetSimple props;
  int interfaceVersion;
public:
  int lexLanguage;

  explicit LexState(Document *pdoc_);
  virtual ~LexState();
  void SetLexer(uptr_t wParam);
  void SetLexerLanguage(const char *languageName);
  const char *DescribeWordListSets();
  void SetWordList(int n, const char *wl);
  const char *GetName() const;
  void *PrivateCall(int operation, void *pointer);
  const char *PropertyNames();
  int PropertyType(const char *name);
  const char *DescribeProperty(const char *name);
  void PropSet(const char *key, const char *val);
  const char *PropGet(const char *key) const;
  int PropGetInt(const char *key, int defaultValue=0) const;
  int PropGetExpanded(const char *key, char *result) const;

  int LineEndTypesSupported();
  int AllocateSubStyles(int styleBase, int numberStyles);
  int SubStylesStart(int styleBase);
  int SubStylesLength(int styleBase);
  int StyleFromSubStyle(int subStyle);
  int PrimaryStyleFromStyle(int style);
  void FreeSubStyles();
  void SetIdentifiers(int style, const char *identifiers);
  int DistanceToSecondaryStyles();
  const char *GetSubStyleBases();
};

Gargaj's avatar
Gargaj committed
88
static unsigned int wndID = 1;
89
90
void ShaderEditor::Initialise()
{
91
  wMain = (Scintilla::WindowID)(unsigned long)(wndID++);
Gargaj's avatar
Gargaj committed
92

Gargaj's avatar
Gargaj committed
93
94
  lexState = new Scintilla::LexState( pdoc );

Klemens Nanni's avatar
Klemens Nanni committed
95
96
  WndProc( SCI_SETBUFFEREDDRAW, 0, 0 );
  WndProc( SCI_SETCODEPAGE, SC_CP_UTF8, 0 );
97

Klemens Nanni's avatar
Klemens Nanni committed
98
  WndProc( SCI_SETWRAPMODE, SC_WRAP_WORD, 0 );
Gargaj's avatar
Gargaj committed
99

Klemens Nanni's avatar
Klemens Nanni committed
100
  //WndProc( SCI_SETLEXERLANGUAGE, SCLEX_CPP, 0 );
101

Gargaj's avatar
Gargaj committed
102
  SetAStyle( STYLE_DEFAULT,     0xFFFFFFFF, BACKGROUND( 0x000000 ), nFontSize, sFontFile.c_str() );
Klemens Nanni's avatar
Klemens Nanni committed
103
  WndProc( SCI_STYLECLEARALL, 0, 0 );
Gargaj's avatar
Gargaj committed
104
105
106
107
  SetAStyle( STYLE_LINENUMBER,  0xFFC0C0C0, BACKGROUND( 0x000000 ), nFontSize, sFontFile.c_str() );
  SetAStyle( STYLE_BRACELIGHT,  0xFF00FF00, BACKGROUND( 0x000000 ), nFontSize, sFontFile.c_str() );
  SetAStyle( STYLE_BRACEBAD,    0xFF0000FF, BACKGROUND( 0x000000 ), nFontSize, sFontFile.c_str() );
  SetAStyle( STYLE_INDENTGUIDE, 0xFFC0C0C0, BACKGROUND( 0x000000 ), nFontSize, sFontFile.c_str() );
108
109
110
111
112

  WndProc(SCI_SETFOLDMARGINCOLOUR,   1, BACKGROUND( 0x1A1A1A ));
  WndProc(SCI_SETFOLDMARGINHICOLOUR, 1, BACKGROUND( 0x1A1A1A ));
  WndProc(SCI_SETSELBACK,            1, BACKGROUND( 0xCC9966 ));

Gargaj's avatar
Gargaj committed
113
  SetReadOnly(false);
114

Gargaj's avatar
Gargaj committed
115
116
  for (int i = 0 ; i < NB_FOLDER_STATE ; i++)
  {
Gargaj's avatar
Gargaj committed
117
    WndProc(SCI_MARKERDEFINE,  markersArray[FOLDER_TYPE][i], markersArray[4][i]);
Gargaj's avatar
Gargaj committed
118
119
120
    WndProc(SCI_MARKERSETBACK, markersArray[FOLDER_TYPE][i], 0xFF6A6A6A);
    WndProc(SCI_MARKERSETFORE, markersArray[FOLDER_TYPE][i], 0xFF333333);
  }
Klemens Nanni's avatar
Klemens Nanni committed
121
122
123
124
  WndProc(SCI_SETUSETABS, bUseSpacesForTabs ? 0 : 1, 0);
  WndProc(SCI_SETTABWIDTH, nTabSize, 0);
  WndProc(SCI_SETINDENT, nTabSize, 0);
  WndProc(SCI_SETINDENTATIONGUIDES, SC_IV_REAL, 0);
Gargaj's avatar
Gargaj committed
125

126
127
  if (bVisibleWhitespace)
  {
Klemens Nanni's avatar
Klemens Nanni committed
128
    WndProc(SCI_SETVIEWWS, SCWS_VISIBLEALWAYS, 0);
129
    WndProc(SCI_SETWHITESPACEFORE, 1, 0x30FFFFFF);
Klemens Nanni's avatar
Klemens Nanni committed
130
    WndProc(SCI_SETWHITESPACESIZE, 2, 0 );
131
132
  }
  
133
  lexState->SetLexer( SCLEX_CPP );
134
135
  lexState->SetWordList(0, shaderKeyword);
  lexState->SetWordList(1, shaderType);
136
  lexState->SetWordList(3, shaderBuiltin);
Gargaj's avatar
Gargaj committed
137

Gargaj's avatar
Gargaj committed
138
  SetAStyle(SCE_C_DEFAULT,      0xFFFFFFFF, BACKGROUND( 0x000000 ), nFontSize, sFontFile.c_str() );
139
140
  SetAStyle(SCE_C_WORD,         0xFF0066FF, BACKGROUND( 0x000000 ));
  SetAStyle(SCE_C_WORD2,        0xFFFFFF00, BACKGROUND( 0x000000 ));
141
  SetAStyle(SCE_C_GLOBALCLASS,  0xFF88FF44, BACKGROUND( 0x000000 ));  
142
143
144
145
146
  SetAStyle(SCE_C_PREPROCESSOR, 0xFFC0C0C0, BACKGROUND( 0x000000 ));
  SetAStyle(SCE_C_NUMBER,       0xFF0080FF, BACKGROUND( 0x000000 ));
  SetAStyle(SCE_C_OPERATOR,     0xFF00CCFF, BACKGROUND( 0x000000 ));
  SetAStyle(SCE_C_COMMENT,      0xFF00FF00, BACKGROUND( 0x000000 ));
  SetAStyle(SCE_C_COMMENTLINE,  0xFF00FF00, BACKGROUND( 0x000000 ));
147
  
Gargaj's avatar
Gargaj committed
148
149
  lexState->Colourise( 0, -1 );

Klemens Nanni's avatar
Klemens Nanni committed
150
  //WndProc( SCI_COLOURISE, 0, 0 );
151
152
153
154

  vs.Refresh( *surfaceWindow, 4 );
}

Gargaj's avatar
Gargaj committed
155
void ShaderEditor::Initialise( SHADEREDITOR_OPTIONS &options )
Gargaj's avatar
Gargaj committed
156
{
Gargaj's avatar
Gargaj committed
157
158
  nFontSize = options.nFontSize;
  sFontFile = options.sFontPath;
159
  nOpacity = options.nOpacity;
160
161
162
  bUseSpacesForTabs = options.bUseSpacesForTabs;
  nTabSize = options.nTabSize;
  bVisibleWhitespace = options.bVisibleWhitespace;
163
  eAutoIndent = options.eAutoIndent;
164

Gargaj's avatar
Gargaj committed
165
  Initialise();
Gargaj's avatar
Gargaj committed
166
  SetPosition( options.rect );
Gargaj's avatar
Gargaj committed
167
168
}

169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
void ShaderEditor::SetVerticalScrollPos()
{

}

void ShaderEditor::SetHorizontalScrollPos()
{

}

bool ShaderEditor::ModifyScrollBars( int nMax, int nPage )
{
  return true;
}

void ShaderEditor::Copy()
{
Gargaj's avatar
Gargaj committed
186
187
188
189
190
  if (!sel.Empty()) {
    SelectionText selectedText;
    CopySelectionRange(&selectedText);
    CopyToClipboard(selectedText);
  }
191
192
193
194
}

void ShaderEditor::Paste()
{
Gargaj's avatar
Gargaj committed
195
  int n = Clipboard::GetContentsLength();
196
197
  if (n == 0) return;

Gargaj's avatar
Gargaj committed
198
199
200
  char * p = new char[n + 1];
  memset(p,0,n+1);
  Clipboard::GetContents( p, n );
201

202
203
  ClearSelection();
  InsertPaste(p, n);
Gargaj's avatar
Gargaj committed
204
205

  delete[] p;
206
207
208
209
210
211
212
213
214
215
216
217
218
219
}

void ShaderEditor::ClaimSelection()
{

}

void ShaderEditor::NotifyChange()
{

}

void ShaderEditor::NotifyParent( Scintilla::SCNotification scn )
{
220
221
222
  switch (scn.nmhdr.code) {
    case SCN_CHARADDED:
      char ch = static_cast<char>(scn.ch);
223
224
225
226
227
      if(eAutoIndent == aitPreserve) {
        PreserveIndentation(ch);
      } else if (aitSmart) {
        AutomaticIndentation(ch);
      }
228
229
      break;
    }
230
231
232
233
}

void ShaderEditor::CopyToClipboard( const Scintilla::SelectionText &selectedText )
{
234
  Clipboard::Copy( selectedText.Data(), (int)selectedText.Length() );
235
236
237
238
}

void ShaderEditor::SetMouseCapture( bool on )
{
239
  bHasMouseCapture = on;
240
241
242
243
}

bool ShaderEditor::HaveMouseCapture()
{
244
  return bHasMouseCapture;
245
246
247
248
249
250
251
252
253
}

sptr_t ShaderEditor::DefWndProc( unsigned int iMessage, uptr_t wParam, sptr_t lParam )
{
  return 0;
}

void ShaderEditor::Paint()
{
Gargaj's avatar
Gargaj committed
254
  Renderer::SetTextRenderingViewport( wMain.GetPosition() );
Gargaj's avatar
Gargaj committed
255
  Scintilla::Editor::Paint( surfaceWindow, GetClientRectangle() );
256
257
}

Marco Foco's avatar
Marco Foco committed
258
void ShaderEditor::SetText( const char * buf )
259
{
Klemens Nanni's avatar
Klemens Nanni committed
260
261
262
  WndProc( SCI_SETREADONLY, false, 0 );
  WndProc( SCI_CLEARALL, false, 0 );
  WndProc( SCI_SETUNDOCOLLECTION, 0, 0);
263
  WndProc( SCI_ADDTEXT, strlen(buf), (sptr_t)buf );
Klemens Nanni's avatar
Klemens Nanni committed
264
265
266
  WndProc( SCI_SETUNDOCOLLECTION, 1, 0);
  WndProc( SCI_SETREADONLY, bReadOnly, 0 );
  WndProc( SCI_GOTOPOS, 0, 0 );
Gargaj's avatar
Gargaj committed
267
268
  if (!bReadOnly)
    SetFocusState( true );
269
270
271
272
273
274
275
276
277
278
279
}

void ShaderEditor::Tick()
{
  Scintilla::Editor::Tick();
}

void ShaderEditor::SetTicking( bool on )
{

}
Gargaj's avatar
Gargaj committed
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294

int ShaderEditor::KeyDown( int key, bool shift, bool ctrl, bool alt, bool *consumed )
{
  return Scintilla::Editor::KeyDown( key, shift, ctrl, alt, consumed );
}

void ShaderEditor::AddCharUTF( const char *s, unsigned int len, bool treatAsDBCS )
{
  Scintilla::Editor::AddCharUTF( s, len, treatAsDBCS );
}

void ShaderEditor::GetText( char * buf, int len )
{
  memset( buf, 0, len );

Klemens Nanni's avatar
Klemens Nanni committed
295
  int lengthDoc = (int)WndProc( SCI_GETLENGTH, 0, 0 );
Gargaj's avatar
Gargaj committed
296
297
298
299
300
301
302
303

  Scintilla::TextRange tr;
  tr.chrg.cpMin = 0;
  tr.chrg.cpMax = Scintilla::Platform::Minimum( len - 1, lengthDoc );
  tr.lpstrText  = buf;

  WndProc(SCI_GETTEXTRANGE, 0, reinterpret_cast<sptr_t>(&tr));
}
Gargaj's avatar
Gargaj committed
304
305
306
307
308
309
310
311
312
313
314

void ShaderEditor::NotifyStyleToNeeded(int endStyleNeeded) {
#ifdef SCI_LEXER
  if (lexState->lexLanguage != SCLEX_CONTAINER) {
    int lineEndStyled = pdoc->LineFromPosition(pdoc->GetEndStyled());
    int endStyled = pdoc->LineStart(lineEndStyled);
    lexState->Colourise(endStyled, endStyleNeeded);
    return;
  }
#endif
  Scintilla::Editor::NotifyStyleToNeeded(endStyleNeeded);
Gargaj's avatar
Gargaj committed
315
316
317
318
319
}

void ShaderEditor::SetReadOnly( bool b )
{
  bReadOnly = b;
Klemens Nanni's avatar
Klemens Nanni committed
320
  WndProc( SCI_SETREADONLY, bReadOnly, 0 );
Gargaj's avatar
Gargaj committed
321
322
  if (bReadOnly)
  {
Klemens Nanni's avatar
Klemens Nanni committed
323
    WndProc(SCI_SETVIEWWS, SCWS_INVISIBLE, 0);
Gargaj's avatar
Gargaj committed
324
325
    WndProc(SCI_SETMARGINWIDTHN, 0, 0);
    WndProc(SCI_SETMARGINWIDTHN, 1, 0);
Klemens Nanni's avatar
Klemens Nanni committed
326
    WndProc( SCI_SETCARETLINEVISIBLE,   0, 0);
Gargaj's avatar
Gargaj committed
327
328
329
330
331
332
333
334
335
    WndProc( SCI_SETCARETFORE,          0, 0);
  }
  else
  {
    WndProc(SCI_SETMARGINWIDTHN, 0, 44);//Calculate correct width
    WndProc(SCI_SETMARGINWIDTHN, 1, 20);//Calculate correct width
    WndProc(SCI_SETMARGINMASKN, 1, SC_MASK_FOLDERS);//Calculate correct width

    WndProc( SCI_SETCARETFORE,          0xFFFFFFFF, 0);
Klemens Nanni's avatar
Klemens Nanni committed
336
337
338
    WndProc( SCI_SETCARETLINEVISIBLE,   1, 0);
    WndProc( SCI_SETCARETLINEBACK,      0xFFFFFFFF, 0);
    WndProc( SCI_SETCARETLINEBACKALPHA, 0x20, 0);
Gargaj's avatar
Gargaj committed
339
340
  }
}
Gargaj's avatar
Gargaj committed
341
342
343
344
345
346
347
348

void ShaderEditor::ButtonDown( Scintilla::Point pt, unsigned int curTime, bool shift, bool ctrl, bool alt )
{
  Scintilla::PRectangle rect = wMain.GetPosition();
  pt.x -= rect.left;
  pt.y -= rect.top;
  Scintilla::Editor::ButtonDown( pt, curTime, shift, ctrl, alt );
}
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364

void ShaderEditor::ButtonMovePublic( Scintilla::Point pt )
{
  Scintilla::PRectangle rect = wMain.GetPosition();
  pt.x -= rect.left;
  pt.y -= rect.top;
  ButtonMove(pt);
}

void ShaderEditor::ButtonUp( Scintilla::Point pt, unsigned int curTime, bool ctrl )
{
  Scintilla::PRectangle rect = wMain.GetPosition();
  pt.x -= rect.left;
  pt.y -= rect.top;
  Scintilla::Editor::ButtonUp( pt, curTime, ctrl );
}
Gargaj's avatar
Gargaj committed
365
366
367
368

Font * ShaderEditor::GetTextFont()
{
  return &vs.styles[ STYLE_DEFAULT ].font;
Gargaj's avatar
Gargaj committed
369
370
371
372
373
374
}

void ShaderEditor::SetPosition( Scintilla::PRectangle rect )
{
  wMain.SetPosition(rect);
}
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389

bool ShaderEditor::FineTickerRunning( TickReason )
{
  return false;
}

void ShaderEditor::FineTickerStart( TickReason, int, int )
{

}

void ShaderEditor::FineTickerCancel( TickReason )
{

}
390

391
392
393
394

/////////////////////////////////////////////////////////////////////////////////////////////
// Indentation handling

395
int ShaderEditor::GetLineLength(int line) {
Klemens Nanni's avatar
Klemens Nanni committed
396
  return WndProc(SCI_GETLINEENDPOSITION, line, 0) - WndProc(SCI_POSITIONFROMLINE, line, 0);
397
398
399
}

int ShaderEditor::GetCurrentLineNumber() {
Klemens Nanni's avatar
Klemens Nanni committed
400
  return WndProc(SCI_LINEFROMPOSITION, WndProc(SCI_GETCURRENTPOS, 0, 0), 0);
401
402
403
404
}

Sci_CharacterRange ShaderEditor::GetSelection() {
  Sci_CharacterRange chrange;
Klemens Nanni's avatar
Klemens Nanni committed
405
406
  chrange.cpMin = WndProc(SCI_GETSELECTIONSTART, 0, 0);
  chrange.cpMax = WndProc(SCI_GETSELECTIONEND, 0, 0);
407
408
409
410
  return chrange;
}

int ShaderEditor::GetLineIndentation(int line) {
Klemens Nanni's avatar
Klemens Nanni committed
411
  return WndProc(SCI_GETLINEINDENTATION, line, 0);
412
413
414
}

int ShaderEditor::GetLineIndentPosition(int line) {
Klemens Nanni's avatar
Klemens Nanni committed
415
  return WndProc(SCI_GETLINEINDENTPOSITION, line, 0);
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
}

void ShaderEditor::SetLineIndentation(int line, int indent) {
  if (indent < 0)
    return;
  Sci_CharacterRange crange = GetSelection();
  int posBefore = GetLineIndentPosition(line);
  WndProc(SCI_SETLINEINDENTATION, line, indent);
  int posAfter = GetLineIndentPosition(line);
  int posDifference = posAfter - posBefore;
  if (posAfter > posBefore) {
    // Move selection on
    if (crange.cpMin >= posBefore) {
      crange.cpMin += posDifference;
    }
    if (crange.cpMax >= posBefore) {
      crange.cpMax += posDifference;
    }
  } else if (posAfter < posBefore) {
    // Move selection back
    if (crange.cpMin >= posAfter) {
      if (crange.cpMin >= posBefore) {
        crange.cpMin += posDifference;
      } else {
        crange.cpMin = posAfter;
      }
    }
    if (crange.cpMax >= posAfter) {
      if (crange.cpMax >= posBefore) {
        crange.cpMax += posDifference;
      } else {
        crange.cpMax = posAfter;
      }
    }
  }
  SetSelection(static_cast<int>(crange.cpMin), static_cast<int>(crange.cpMax));
}

void ShaderEditor::PreserveIndentation(char ch) {
Klemens Nanni's avatar
Klemens Nanni committed
455
  int eolMode = WndProc(SCI_GETEOLMODE, 0, 0);
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
  int curLine = GetCurrentLineNumber();
  int lastLine = curLine - 1;

  if (((eolMode == SC_EOL_CRLF || eolMode == SC_EOL_LF) && ch == '\n') || (eolMode == SC_EOL_CR && ch == '\r')) {
    while (lastLine >= 0 && GetLineLength(lastLine) == 0) {
      lastLine--;
    }
    int indentAmount = 0;
    if (lastLine >= 0) {
      indentAmount = GetLineIndentation(lastLine);
    }
    if (indentAmount > 0) {
      SetLineIndentation(curLine, indentAmount);
    }
  }
}
472
473
474
475

std::vector<std::string> ShaderEditor::GetLinePartsInStyle(int line, int style) {
  std::vector<std::string> out;
  std::string s;
Klemens Nanni's avatar
Klemens Nanni committed
476
477
  int thisLineStart = WndProc(SCI_POSITIONFROMLINE, line, 0);
  int nextLineStart = WndProc(SCI_POSITIONFROMLINE, line + 1, 0);
478
479
  for (int pos = thisLineStart; pos < nextLineStart; pos++) {
    if (static_cast<char>(WndProc(SCI_GETSTYLEAT, pos, 0)) == style) {
Klemens Nanni's avatar
Klemens Nanni committed
480
      char c = WndProc(SCI_GETCHARAT, pos, 0);
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
      // special handling for statements and blocks characters as they should be dissociated to other strings
      if (c == statementEnd || c == blockStart || c == blockEnd) {
        out.push_back(std::string(1, c));
        s = "";
      } else {
        s += c;
      }
    } else if (s.size() > 0) {
      out.push_back(s);
      s = "";
    }
  }
  if (s.size() > 0) {
    out.push_back(s);
  }
  
  return out;
}

bool ShaderEditor::isAStatementIndent(std::string &word) {
  for (size_t i=0; i<(sizeof(statementIndent)/sizeof(statementIndent[0])); i++) {
    std::string &statementElem = statementIndent[i];
    if (statementElem == word) {
      return true;
    }
  }
  return false;
}

ShaderEditor::IndentationStatus ShaderEditor::GetIndentState(int line) {
  size_t i;
  IndentationStatus indentState = isNone;
  std::vector<std::string> controlWords = GetLinePartsInStyle(line, statementIndentStyleNumber);
  for (i=0; i<controlWords.size(); i++) {
    std::string &controlWord = controlWords[i];
    if (isAStatementIndent(controlWord)) {
      indentState = isKeyWordStart;
    }
  }
  
  controlWords = GetLinePartsInStyle(line, blockAndStatementEndStyleNumber);
  for (i=0; i<controlWords.size(); i++) {
    std::string &controlWord = controlWords[i];
    if (controlWord.size() < 1) continue;
    if (statementEnd == controlWord[0]) {
      indentState = isNone;
    }
  }
  
  for (i=0; i<controlWords.size(); i++) {
    std::string &controlWord = controlWords[i];
    if (controlWord.size() < 1) continue;
    if (blockEnd == controlWord[0]) {
      indentState = isBlockEnd;
    }
    if (blockStart == controlWord[0]) {
      indentState = isBlockStart;
    }
  }
  
  return indentState;
}

int ShaderEditor::IndentOfBlock(int line) {
  if (line < 0)
    return 0;
Klemens Nanni's avatar
Klemens Nanni committed
547
  int indentSize = WndProc(SCI_GETINDENT, 0, 0);
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
  int indentBlock = GetLineIndentation(line);
  int backLine = line;
  IndentationStatus indentState = isNone;

  int lineLimit = line - statementLookback;
  if (lineLimit < 0) lineLimit = 0;

  while ((backLine >= lineLimit) && (indentState == isNone)) {
    indentState = GetIndentState(backLine);
    if (indentState != isNone) {
      indentBlock = GetLineIndentation(backLine);
      if (indentState == isBlockStart) {
          indentBlock += indentSize;
      }
      if (indentState == isBlockEnd) {
        if (indentBlock < 0)
          indentBlock = 0;
      }
      if ((indentState == isKeyWordStart) && (backLine == line))
        indentBlock += indentSize;
    }
    backLine--;
  }
  
  return indentBlock;
}

bool ShaderEditor::RangeIsAllWhitespace(int start, int end) {
  for (int i = start; i < end; i++) {
Klemens Nanni's avatar
Klemens Nanni committed
577
    if ((WndProc(SCI_GETCHARAT, i, 0) != ' ') && (WndProc(SCI_GETCHARAT, i, 0) != '\t'))
578
579
580
581
582
583
584
585
586
      return false;
  }
  return true;
}

void ShaderEditor::AutomaticIndentation(char ch) {
  Sci_CharacterRange crange = GetSelection();
  int selStart = static_cast<int>(crange.cpMin);
  int curLine = GetCurrentLineNumber();
Klemens Nanni's avatar
Klemens Nanni committed
587
588
  int thisLineStart = WndProc(SCI_POSITIONFROMLINE, curLine, 0);
  int indentSize = WndProc(SCI_GETINDENT, 0, 0);
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
  int indentBlock = IndentOfBlock(curLine - 1);
 
  if (ch == blockEnd) {
    if (RangeIsAllWhitespace(thisLineStart, selStart - 1)) {
      SetLineIndentation(curLine, indentBlock - indentSize);
    }
  } else if (ch == blockStart) {
    if (GetIndentState(curLine - 1) == isKeyWordStart) {
      if (RangeIsAllWhitespace(thisLineStart, selStart - 1)) {
        SetLineIndentation(curLine, indentBlock - indentSize);
      }
    }
  } else if ((ch == '\r' || ch == '\n') && (selStart == thisLineStart)) {
    SetLineIndentation(curLine, indentBlock);
  }
}