nitwebcrawl: add a simple crawler for nitweb
[nit.git] / src / examples / nitlight_as_a_service.nit
1 # This file is part of NIT ( http://www.nitlanguage.org ).
2 #
3 # Licensed under the Apache License, Version 2.0 (the "License");
4 # you may not use this file except in compliance with the License.
5 # You may obtain a copy of the License at
6 #
7 # http://www.apache.org/licenses/LICENSE-2.0
8 #
9 # Unless required by applicable law or agreed to in writing, software
10 # distributed under the License is distributed on an "AS IS" BASIS,
11 # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 # See the License for the specific language governing permissions and
13 # limitations under the License.
14
15 # This is an example of a client of the frontend without command-line processing.
16 #
17 # It offers a simple nitcorn web server that offers a textarea and nitpick and nitlignt it.
18 module nitlight_as_a_service
19
20 import frontend
21 import highlight
22 import nitcorn
23 import nitcorn::log
24 import template
25
26 # Fully process a content as a nit source file.
27 fun hightlightcode(hl: HighlightVisitor, content: String): HLCode
28 do
29 # Prepare a stand-alone tool context
30 var tc = new ToolContext
31 tc.nit_dir = tc.locate_nit_dir # still use the common lib to have core
32 tc.keep_going = true # no exit, obviously
33 tc.opt_warn.value = -1 # no output, obviously
34
35 # Prepare an stand-alone model and model builder.
36 # Unfortunately, models are enclosing and append-only.
37 # There is no way (yet?) to have a shared module `core` with
38 # isolated and throwable user modules.
39 var model = new Model
40 var mb = new ModelBuilder(model, tc)
41
42 # Parse the code
43 var source = new SourceFile.from_string("", content + "\n")
44 var lexer = new Lexer(source)
45 var parser = new Parser(lexer)
46 var tree = parser.parse
47
48 var hlcode = new HLCode(hl, content, source)
49
50 # Check syntax error
51 var eof = tree.n_eof
52 if eof isa AError then
53 mb.error(eof, eof.message)
54 hl.hightlight_source(source)
55 return hlcode
56 end
57 var amodule = tree.n_base.as(not null)
58
59 # Load the AST as a module in the model
60 # Then process it
61 mb.load_rt_module(null, amodule, "")
62 mb.run_phases
63
64 # Highlight the processed module
65 hl.enter_visit(amodule)
66 return hlcode
67 end
68
69 # A standalone highlighted piece of code
70 class HLCode
71 # The highlighter used
72 var hl: HighlightVisitor
73
74 # The raw code source
75 var content: String
76
77 # The pseudo source-file
78 var source: SourceFile
79
80 # JavaScript code to update an existing codemirror editor.
81 fun code_mirror_update: Template
82 do
83
84 var res = new Template
85 res.add """
86 function nitmessage() {
87 editor.operation(function(){
88 for (var i = 0; i < widgets.length; ++i)
89 editor.removeLineWidget(widgets[i]);
90 widgets.length = 0;
91 """
92
93 for m in source.messages do
94 res.add """
95 var l = document.createElement("div");
96 l.className = "lint-error"
97 l.innerHTML = "<span class='glyphicon glyphicon-warning-sign lint-error-icon'></span> {{{m.text.html_escape}}}";
98 var w = editor.addLineWidget({{{m.location.line_start-1}}}, l);
99 widgets.push(w);
100 """
101 end
102 res.add """});}"""
103 return res
104 end
105 end
106
107 # Nitcorn service to hightlight code
108 #
109 # It's a single stand-alone page that has to form to itself.
110 class HighlightAction
111 super Action
112
113 redef fun answer(http_request, turi)
114 do
115 var hl = new HighlightVisitor
116 var page = new Template
117
118 # There is code? Process it
119 var code = http_request.post_args.get_or_null("code")
120 var hlcode = null
121 if code != null then hlcode = hightlightcode(hl, code)
122
123 if http_request.post_args.get_or_null("ajax") == "true" and hlcode != null then
124 page.add hlcode.code_mirror_update
125 page.add """
126 document.getElementById("lightcode").innerHTML = "{{{hl.html.write_to_string.escape_to_c}}}";
127 nitmessage();
128 """
129
130 var response = new HttpResponse(200)
131 response.header["Content-Type"] = "application/javascript"
132 response.body = page.write_to_string
133 return response
134 end
135
136 page.add """
137 <!doctype html><html><head>{{{hl.head_content}}}
138 <link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/codemirror/5.16.0/codemirror.css">
139 <style>
140 {{{hl.css_content}}}
141 textarea {width:100%;}
142 .lint-error {font-family: arial; font-size: 70%; background: #ffa; color: #a00; padding: 2px 5px 3px; }
143 .lint-error-icon {color: red; padding: 0 3px; margin-right: 7px;}
144 </style></head><body>
145 """
146 # Add the form+textarea
147 page.add """
148 <form action="#light" method=post><textarea id=code name=code rows=10>{{{code or else ""}}}</textarea><br><input type=submit></form>
149 """
150
151 if hlcode != null then
152 # Inject highlight
153 page.add "<pre id=light><code id=lightcode>"
154 page.add hl.html.write_to_string
155 page.add "</code></pre><hr>"
156 page.add "<ul>"
157
158 # List messages
159 for m in hlcode.source.messages do
160 page.add "<li>{m.location.as(not null)}: {m.text}</li>"
161 end
162 page.add "</ul>"
163 else
164 page.add "<pre id=light><code id=lightcode></code></pre>"
165 end
166
167 page.add hl.foot_content
168
169 # Call codemirror
170 page.add """
171 <script type="text/javascript" src="https://cdnjs.cloudflare.com/ajax/libs/codemirror/5.16.0/codemirror.min.js"></script>
172 <script>
173 var editor = CodeMirror.fromTextArea(document.getElementById("code"), {
174 lineNumbers: true
175 });
176 """
177
178 # Callback to update codemirror messages
179 if hlcode != null then
180 page.add hlcode.code_mirror_update
181 else
182 page.add "function nitmessage()\{\}"
183 end
184 page.add """
185 var widgets = [];
186 nitmessage();
187
188 function updatePage() {
189 $.post("", { ajax: true, code: editor.getValue()}, function(data) {
190 eval(data);
191 $(".popupable").popover({html:true, placement:'top'});
192 });
193 }
194
195 var waiting;
196 editor.on("change", function() {
197 clearTimeout(waiting);
198 waiting = setTimeout(updatePage, 500);
199 });
200 waiting = setTimeout(updatePage, 500);
201
202 </script>
203 </body></html>
204 """
205
206 var response = new HttpResponse(200)
207 response.header["Content-Type"] = "text/html"
208 response.body = page.write_to_string
209 return response
210 end
211 end
212
213 var host = "localhost:8080"
214 if args.length > 0 then host = args.first
215
216 var vh = new VirtualHost(host)
217 vh.routes.add new Route("/", new HighlightAction)
218 var factory = new HttpFactory.and_libevent
219 factory.config.virtual_hosts.add vh
220 factory.run