Skip to content

Commit a79c2f1

Browse files
committed
context-aware hinter code
1 parent c6f0b50 commit a79c2f1

File tree

3 files changed

+624
-0
lines changed

3 files changed

+624
-0
lines changed
Lines changed: 100 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,100 @@
1+
import CodeMirror from 'codemirror';
2+
import parseCode from './parseCode';
3+
4+
const scopeMap = require('./finalScopeMap.json');
5+
6+
export default function contextAwareHinter(cm, options = {}) {
7+
const { hinter } = options;
8+
if (!hinter || typeof hinter.search !== 'function') {
9+
console.warn('Hinter is not available or invalid.');
10+
return { list: [], from: cm.getCursor(), to: cm.getCursor() };
11+
}
12+
13+
const { line, ch } = cm.getCursor(); // getCursor has line, ch, sticky
14+
console.log('cm.getcursor ', cm.getCursor());
15+
const { start, end, string } = cm.getTokenAt({ line, ch });
16+
// console.log('cm.gettokenat', cm.getTokenAt());
17+
const currentWord = string.trim();
18+
console.log('currentwork ', currentWord);
19+
20+
const context = parseCode(cm); // e.g. 'draw'
21+
const allHints = hinter.search(currentWord); // <- from options, not cm.hinter
22+
const whitelist = scopeMap[context]?.whitelist || [];
23+
const blacklist = scopeMap[context]?.blacklist || [];
24+
console.log('allhints: ', allHints);
25+
26+
// for each hint, only keep ones that match the typed prefix
27+
const filteredHints = allHints
28+
.filter(
29+
(h) =>
30+
h &&
31+
h.item &&
32+
typeof h.item.text === 'string' &&
33+
h.item.text.startsWith(currentWord)
34+
)
35+
.map((h) => {
36+
const name = h.item.text;
37+
const isWhitelisted = whitelist.includes(name);
38+
const isBlacklisted = blacklist.includes(name);
39+
40+
const from = CodeMirror.Pos(line, start);
41+
const to = CodeMirror.Pos(line, end);
42+
43+
let className = '';
44+
if (isBlacklisted) {
45+
className = 'blacklisted-hint';
46+
} else if (isWhitelisted) {
47+
className = 'whitelisted-hint';
48+
}
49+
50+
return {
51+
text: name, // Ensure `text` is explicitly defined
52+
displayText: h.item.displayText || name,
53+
className,
54+
from,
55+
to,
56+
render: (el, self, data) => {
57+
el.innerText = data.text;
58+
if (isBlacklisted) {
59+
el.style.color = 'red';
60+
el.style.opacity = 0.6;
61+
} else if (isWhitelisted) {
62+
el.style.fontWeight = 'bold';
63+
}
64+
},
65+
hint: (editor, data, completion) => {
66+
const { from: fromPos, to: toPos } = completion;
67+
68+
if (!completion.text || typeof completion.text !== 'string') {
69+
console.error('Invalid completion.text:', completion);
70+
return;
71+
}
72+
73+
editor.replaceRange(completion.text, fromPos, toPos);
74+
75+
if (blacklist.includes(completion.text)) {
76+
console.warn(
77+
`Using "${completion.text}" inside "${context}" is not recommended.`
78+
);
79+
}
80+
}
81+
};
82+
});
83+
84+
console.log('filtered hints: ', filteredHints);
85+
86+
const sorted = filteredHints.sort((a, b) => {
87+
const score = (name) => {
88+
if (whitelist.includes(name)) return 0;
89+
if (blacklist.includes(name)) return 2;
90+
return 1;
91+
};
92+
return score(a.text) - score(b.text) || a.text.localeCompare(b.text);
93+
});
94+
95+
return {
96+
list: sorted,
97+
from: CodeMirror.Pos(line, start),
98+
to: CodeMirror.Pos(line, end)
99+
};
100+
}

0 commit comments

Comments
 (0)