1 module dlangide.ui.dsourceedit;
2 
3 /// DIDE source file editor
4 class DSourceEdit : SourceEdit {
5 	this(string ID) {
6 		super(ID);
7 		styleId = null;
8 		backgroundColor = 0xFFFFFF;
9         setTokenHightlightColor(TokenCategory.Comment, 0x008000); // green
10         setTokenHightlightColor(TokenCategory.Keyword, 0x0000FF); // blue
11         setTokenHightlightColor(TokenCategory.String, 0xA31515);  // brown
12         setTokenHightlightColor(TokenCategory.Character, 0xA31515);  // brown
13         setTokenHightlightColor(TokenCategory.Error, 0xFF0000);  // red
14         setTokenHightlightColor(TokenCategory.Comment_Documentation, 0x206000);
15         //setTokenHightlightColor(TokenCategory.Identifier, 0x206000);  // no colors
16 	}
17 	this() {
18 		this("SRCEDIT");
19 	}
20     protected ProjectSourceFile _projectSourceFile;
21     @property ProjectSourceFile projectSourceFile() { return _projectSourceFile; }
22     /// load by filename
23     override bool load(string fn) {
24         _projectSourceFile = null;
25         bool res = super.load(fn);
26         setHighlighter();
27         return res;
28     }
29 
30     void setHighlighter() {
31         if (filename.endsWith(".d") || filename.endsWith(".dd") || filename.endsWith(".dh") || filename.endsWith(".ddoc")) {
32             content.syntaxHighlighter = new SimpleDSyntaxHighlighter(filename);
33         } else {
34             content.syntaxHighlighter = null;
35         }
36     }
37 
38     /// load by project item
39     bool load(ProjectSourceFile f) {
40         if (!load(f.filename)) {
41             _projectSourceFile = null;
42             return false;
43         }
44         _projectSourceFile = f;
45         setHighlighter();
46         return true;
47     }
48 }
49 
50 
51 
52 class SimpleDSyntaxHighlighter : SyntaxHighlighter {
53 
54     SourceFile _file;
55     ArraySourceLines _lines;
56     Tokenizer _tokenizer;
57     this (string filename) {
58         _file = new SourceFile(filename);
59         _lines = new ArraySourceLines();
60         _tokenizer = new Tokenizer(_lines);
61         _tokenizer.errorTolerant = true;
62     }
63 
64     TokenPropString[] _props;
65 
66     /// categorize characters in content by token types
67     void updateHighlight(dstring[] lines, TokenPropString[] props, int changeStartLine, int changeEndLine) {
68         Log.d("updateHighlight");
69         long ms0 = currentTimeMillis();
70         _props = props;
71         changeStartLine = 0;
72         changeEndLine = lines.length;
73         _lines.init(lines[changeStartLine..$], _file, changeStartLine);
74         _tokenizer.init(_lines);
75         int tokenPos = 0;
76         int tokenLine = 0;
77         ubyte category = 0;
78         try {
79             for (;;) {
80                 Token token = _tokenizer.nextToken();
81                 if (token is null) {
82                     //Log.d("Null token returned");
83                     break;
84                 }
85                 if (token.type == TokenType.EOF) {
86                     //Log.d("EOF token");
87                     break;
88                 }
89                 uint newPos = token.pos - 1;
90                 uint newLine = token.line - 1;
91 
92                 //Log.d("", token.line, ":", token.pos, "\t", tokenLine + 1, ":", tokenPos + 1, "\t", token.toString);
93 
94                 // fill with category
95                 for (int i = tokenLine; i <= newLine; i++) {
96                     int start = i > tokenLine ? 0 : tokenPos;
97                     int end = i < newLine ? lines[i].length : newPos;
98                     for (int j = start; j < end; j++)
99                         _props[i][j] = category;
100                 }
101 
102                 // handle token - convert to category
103                 switch(token.type) {
104                     case TokenType.COMMENT:
105                         category = token.isDocumentationComment ? TokenCategory.Comment_Documentation : TokenCategory.Comment;
106                         break;
107                     case TokenType.KEYWORD:
108                         category = TokenCategory.Keyword;
109                         break;
110                     case TokenType.IDENTIFIER:
111                         category = TokenCategory.Identifier;
112                         break;
113                     case TokenType.STRING:
114                         category = TokenCategory.String;
115                         break;
116                     case TokenType.CHARACTER:
117                         category = TokenCategory.Character;
118                         break;
119                     case TokenType.INTEGER:
120                         category = TokenCategory.Integer;
121                         break;
122                     case TokenType.FLOAT:
123                         category = TokenCategory.Float;
124                         break;
125                     case TokenType.INVALID:
126                         switch (token.invalidTokenType) {
127                             case TokenType.IDENTIFIER:
128                                 category = TokenCategory.Error_InvalidIdentifier;
129                                 break;
130                             case TokenType.STRING:
131                                 category = TokenCategory.Error_InvalidString;
132                                 break;
133                             case TokenType.COMMENT:
134                                 category = TokenCategory.Error_InvalidComment;
135                                 break;
136                             case TokenType.FLOAT:
137                             case TokenType.INTEGER:
138                                 category = TokenCategory.Error_InvalidNumber;
139                                 break;
140                             default:
141                                 category = TokenCategory.Error;
142                                 break;
143                         }
144                         break;
145                     default:
146                         category = 0;
147                         break;
148                 }
149                 tokenPos = newPos;
150                 tokenLine= newLine;
151 
152             }
153         } catch (Exception e) {
154             Log.e("exception while trying to parse D source", e);
155         }
156         _lines.close();
157         _props = null;
158         Log.d("updateHighlight took ", currentTimeMillis() - ms0, "ms");
159     }
160 }
161