Skip to content

Commit dc920ce

Browse files
author
Jean-Baptiste Doderlein
committed
autocomplete + error highlight
1 parent 4ea39b0 commit dc920ce

File tree

4 files changed

+149
-31
lines changed

4 files changed

+149
-31
lines changed

src/css/index.css

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -490,6 +490,9 @@ code, kbd, pre, samp {
490490
background: var(--editor-selection-bg-color);
491491
}
492492

493+
.CodeMirror .syntax-error {
494+
text-decoration: underline red wavy;
495+
}
493496
/* Track */
494497

495498
::-webkit-scrollbar-track {

src/index.html

Lines changed: 9 additions & 30 deletions
Original file line numberDiff line numberDiff line change
@@ -35,6 +35,7 @@
3535
<script src="js/codemirror/show-hint.js"></script>
3636
<script src="js/codemirror/annotatescrollbar.js"></script>
3737
<script src="js/codemirror/matchesonscrollbar.js"></script>
38+
<script src="js/codemirror/python-hint.js"></script>
3839
<script async src="js/resizer.js"></script>
3940
<script async defer src="js/buttons.js"></script>
4041

@@ -277,12 +278,15 @@ <h4>Graphics main window</h4>
277278
<script id="toplevel-terminal" type="mpy" terminal worker name="toplevel" env="main">
278279
import code
279280
import asyncio
280-
# Import pyscript event handler to create file in filesystem
281+
import sys
281282
from pyscript import window
282283
def _execute_current():
283284
current = int(window.current_editor())
284285
value = window.editors[current].getValue()
285-
exec(value)
286+
try:
287+
exec(value)
288+
except Exception as e:
289+
sys.print_exception(e)
286290

287291
code.interact()
288292
</script>
@@ -399,7 +403,7 @@ <h4>Graphics main window</h4>
399403

400404

401405
const Parser = window.TreeSitter;
402-
let parser,query;
406+
let parser,query,error_quey;
403407
Parser.init().then(() => {
404408
parser = new Parser;
405409
Parser.Language.load('js/tree-sitter/tree-sitter-python.wasm').then((language) => {
@@ -412,36 +416,11 @@ <h4>Graphics main window</h4>
412416
(try_statement)@bloc
413417
(import_statement)@expr
414418
(expression_statement)@expr`);
419+
error_query = language.query(`
420+
(ERROR)@error`);
415421
});
416422
});
417423

418-
// Sélectionner l'élément à surveiller
419-
const elementToObserve = document.querySelector('#toplevel-terminal');
420-
421-
// Créer une fonction de callback qui sera exécutée lorsque des mutations sont observées
422-
const callback = (mutationsList, observer) => {
423-
for (const mutation of mutationsList) {
424-
if (mutation.type === 'attributes' && mutation.attributeName === 'terminal') {
425-
const newValue = elementToObserve.getAttribute('terminal');
426-
if (newValue !== undefined) {
427-
console.log('L\'attribut terminal a changé et n\'est plus undefined:', newValue);
428-
// Exécutez votre code ici
429-
}
430-
}
431-
}
432-
};
433-
434-
// Créer une instance de MutationObserver et passer la fonction de callback
435-
const observer = new MutationObserver(callback);
436-
437-
// Configurer les options d'observation
438-
const config = { attributes: true };
439-
440-
// Commencer à observer l'élément
441-
observer.observe(elementToObserve, config);
442-
443-
//change_font_size("toplevel",0);
444-
445424
</script>
446425

447426

src/js/codemirror/python-hint.js

Lines changed: 94 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,94 @@
1+
(function () {
2+
function forEach(arr, f) {
3+
for (var i = 0, e = arr.length; i < e; ++i) f(arr[i]);
4+
}
5+
6+
function arrayContains(arr, item) {
7+
if (!Array.prototype.indexOf) {
8+
var i = arr.length;
9+
while (i--) {
10+
if (arr[i] === item) {
11+
return true;
12+
}
13+
}
14+
return false;
15+
}
16+
return arr.indexOf(item) != -1;
17+
}
18+
19+
function scriptHint(editor, _keywords, getToken) {
20+
// Find the token at the cursor
21+
var cur = editor.getCursor(), token = getToken(editor, cur), tprop = token;
22+
// If it's not a 'word-style' token, ignore the token.
23+
24+
if (!/^[\w$_]*$/.test(token.string)) {
25+
token = tprop = {start: cur.ch, end: cur.ch, string: "", state: token.state,
26+
className: token.string == ":" ? "python-type" : null};
27+
}
28+
29+
if (!context) var context = [];
30+
context.push(tprop);
31+
32+
var completionList = getCompletions(token, context);
33+
completionList = completionList.sort();
34+
//prevent autocomplete for last word, instead show dropdown with one word
35+
if(completionList.length == 1) {
36+
completionList.push(" ");
37+
}
38+
39+
return {list: completionList,
40+
from: CodeMirror.Pos(cur.line, token.start),
41+
to: CodeMirror.Pos(cur.line, token.end)};
42+
}
43+
44+
CodeMirror.pythonHint = function(editor) {
45+
return scriptHint(editor, pythonKeywordsU, function (e, cur) {return e.getTokenAt(cur);});
46+
};
47+
48+
var pythonKeywords = "and del from not while as elif global or with assert else if pass yield"
49+
+ "break except import print class exec in raise continue finally is return def for lambda try";
50+
var pythonKeywordsL = pythonKeywords.split(" ");
51+
var pythonKeywordsU = pythonKeywords.toUpperCase().split(" ");
52+
53+
var pythonBuiltins = "abs divmod input open staticmethod all enumerate int ord str "
54+
+ "any eval isinstance pow sum basestring execfile issubclass print super"
55+
+ "bin file iter property tuple bool filter len range type"
56+
+ "bytearray float list raw_input unichr callable format locals reduce unicode"
57+
+ "chr frozenset long reload vars classmethod getattr map repr xrange"
58+
+ "cmp globals max reversed zip compile hasattr memoryview round __import__"
59+
+ "complex hash min set apply delattr help next setattr buffer"
60+
+ "dict hex object slice coerce dir id oct sorted intern ";
61+
var pythonBuiltinsL = pythonBuiltins.split(" ").join("() ").split(" ");
62+
var pythonBuiltinsU = pythonBuiltins.toUpperCase().split(" ").join("() ").split(" ");
63+
64+
function getCompletions(token, context) {
65+
var found = [], start = token.string;
66+
function maybeAdd(str) {
67+
if (str.indexOf(start) == 0 && !arrayContains(found, str)) found.push(str);
68+
}
69+
70+
function gatherCompletions(_obj) {
71+
forEach(pythonBuiltinsL, maybeAdd);
72+
forEach(pythonBuiltinsU, maybeAdd);
73+
forEach(pythonKeywordsL, maybeAdd);
74+
forEach(pythonKeywordsU, maybeAdd);
75+
}
76+
77+
if (context) {
78+
// If this is a property, see if it belongs to some object we can
79+
// find in the current environment.
80+
var obj = context.pop(), base;
81+
82+
if (obj.type == "variable")
83+
base = obj.string;
84+
else if(obj.type == "variable-3")
85+
base = ":" + obj.string;
86+
87+
while (base != null && context.length)
88+
base = base[context.pop().string];
89+
if (base != null) gatherCompletions(base);
90+
}
91+
return found;
92+
}
93+
})();
94+

src/js/editor_change.js

Lines changed: 43 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -86,6 +86,43 @@ let exec_all = function (instance) {
8686
autoscroll_output();
8787
};
8888

89+
let check_error = function (instance) {
90+
if (error_query == undefined) {
91+
return;
92+
}
93+
instance.syntaxErrorMark.forEach(mark => mark.clear());
94+
instance.syntaxErrorMark = [];
95+
let errors = error_query.matches(parser.parse(instance.getValue()).rootNode);
96+
// If a match is found, the error is displayed
97+
if (errors.length > 0) {
98+
// Clear previous error marks
99+
100+
errors.forEach(capture => {
101+
capture.captures.forEach(element => {
102+
instance.syntaxErrorMark.push(instance.markText({
103+
line: element.node.startPosition.row,
104+
ch: element.node.startPosition.column
105+
},
106+
{
107+
line: element.node.endPosition.row,
108+
ch: element.node.endPosition.column
109+
}, {className: "syntax-error"}));
110+
})
111+
});
112+
return true;
113+
}
114+
return false;
115+
}
116+
117+
let check_error_bloc = function (text) {
118+
if (error_query == undefined) {
119+
return;
120+
}
121+
let errors = error_query.matches(parser.parse(text).rootNode);
122+
return errors.length > 0;
123+
}
124+
125+
89126

90127
let calculate_interval = function (instance) {
91128
let intervalles = [];
@@ -370,15 +407,20 @@ function create_editor(id, name) {
370407
}
371408
},
372409
'Shift-Tab': function (cm) { cm.execCommand('indentLess') },
373-
}
410+
},
411+
hintOptions: {hint: CodeMirror.pythonHint},
374412
});
375413
editor.id = id
376414
editor.name = name
377415
editor.is_saved = true
416+
editor.ext_autocomplete = localStorage.getItem("betterocaml-autocomplete") == "true"
417+
editor.autocomplete_timeout = Date.now()
378418
editor.current_marker = editor.markText({ line: 0 }, { line: 0 }, { css: "color: #fe4" });
419+
editor.syntaxErrorMark = [];
379420
editor.on("cursorActivity", cursor_activity);
380421
editor.on('drop', editor_drop);
381422
editor.on("keyup", function (cm, event) {
423+
check_error(cm);
382424
if (cm.ext_autocomplete && // Only trigger if jetbrain style autocompletion is activated
383425
cm.autocomplete_timeout < Date.now() - 500 && // Only trigger if the user stopped typing
384426
!cm.state.completionActive && /*Enables keyboard navigation in autocomplete list*/

0 commit comments

Comments
 (0)