Skip to content

Commit 093dccc

Browse files
authored
Merge pull request #5 from kammeows/Branch_Kamakshi
minor bug fix in autocomplete suggestions
2 parents 87d89a5 + e3c7f18 commit 093dccc

File tree

14 files changed

+1283
-4001
lines changed

14 files changed

+1283
-4001
lines changed

.env.example

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,7 @@ MAILGUN_KEY=<your-mailgun-api-key>
1919
ML5_LIBRARY_USERNAME=ml5
2020
ML5_LIBRARY_EMAIL=[email protected]
2121
ML5_LIBRARY_PASS=helloml5
22-
MONGO_URL=mongodb://localhost:27017/p5js-web-editor
22+
MONGO_URL=mongodb://127.0.0.1:27017/p5js-web-editor
2323
PORT=8000
2424
PREVIEW_PORT=8002
2525
EDITOR_URL=http://localhost:8000

client/modules/IDE/components/Editor/index.jsx

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -74,6 +74,8 @@ import { FolderIcon } from '../../../../common/icons';
7474
import IconButton from '../../../../common/IconButton';
7575

7676
import contextAwareHinter from '../contextAwareHinter';
77+
import showRenameDialog from '../showRenameDialog';
78+
import { handleRename } from '../rename-variable';
7779

7880
emmet(CodeMirror);
7981

@@ -171,6 +173,7 @@ class Editor extends React.Component {
171173
},
172174
Enter: 'emmetInsertLineBreak',
173175
Esc: 'emmetResetAbbreviation',
176+
F2: (cm) => this.renameVariable(cm),
174177
[`Shift-Tab`]: false,
175178
[`${metaKey}-Enter`]: () => null,
176179
[`Shift-${metaKey}-Enter`]: () => null,
@@ -534,6 +537,27 @@ class Editor extends React.Component {
534537
}
535538
}
536539

540+
renameVariable(cm) {
541+
const cursorCoords = cm.cursorCoords(true, 'page');
542+
const selection = cm.getSelection();
543+
const pos = cm.getCursor(); // or selection start
544+
const token = cm.getTokenAt(pos);
545+
const tokenType = token.type;
546+
if (!selection) {
547+
return;
548+
}
549+
550+
const sel = cm.listSelections()[0];
551+
const fromPos =
552+
CodeMirror.cmpPos(sel.anchor, sel.head) <= 0 ? sel.anchor : sel.head;
553+
554+
showRenameDialog(tokenType, cursorCoords, selection, (newName) => {
555+
if (newName && newName.trim() !== '' && newName !== selection) {
556+
handleRename(fromPos, selection, newName, cm);
557+
}
558+
});
559+
}
560+
537561
initializeDocuments(files) {
538562
this._docs = {};
539563
files.forEach((file) => {

client/modules/IDE/components/contextAwareHinter.js

Lines changed: 34 additions & 42 deletions
Original file line numberDiff line numberDiff line change
@@ -1,28 +1,13 @@
1-
import CodeMirror from 'codemirror';
21
import parseCode from './parseCode';
32
import parseCodeVariables from './parseCodeVariables';
43
import classMap from './class-with-methods-map.json';
54

65
const scopeMap = require('./finalScopeMap.json');
76

8-
function formatHintDisplay(name, isBlacklisted) {
9-
return `
10-
<div class="fun-item">
11-
<span class="fun-name">${name}</span>
12-
${
13-
isBlacklisted
14-
? `<div class="inline-warning">⚠️ "${name}" is discouraged in this context.</div>`
15-
: ''
16-
}
17-
</div>
18-
`;
19-
}
20-
217
function getExpressionBeforeCursor(cm) {
228
const cursor = cm.getCursor();
239
const line = cm.getLine(cursor.line);
2410
const uptoCursor = line.slice(0, cursor.ch);
25-
2611
const match = uptoCursor.match(
2712
/([a-zA-Z_$][\w$]*(?:\.[a-zA-Z_$][\w$]*)*)\.(?:[a-zA-Z_$][\w$]*)?$/
2813
);
@@ -32,7 +17,6 @@ function getExpressionBeforeCursor(cm) {
3217
export default function contextAwareHinter(cm, options = {}) {
3318
const {
3419
p5ClassMap = {},
35-
varMap = [],
3620
varScopeMap = {},
3721
userFuncMap = {},
3822
userClassMap = {}
@@ -81,20 +65,15 @@ export default function contextAwareHinter(cm, options = {}) {
8165
}
8266

8367
const to = { line: cursor.line, ch: cursor.ch };
84-
let tokenLength = 0;
85-
if (dotMatch) {
86-
const typed = dotMatch[1] || ''; // what's typed after the dot
87-
tokenLength = typed.length;
88-
}
89-
9068
const typed = dotMatch?.[1]?.toLowerCase() || '';
9169

9270
const methodHints = methods
9371
.filter((method) => method.toLowerCase().startsWith(typed))
9472
.map((method) => ({
9573
item: {
9674
text: method,
97-
type: 'fun'
75+
type: 'fun',
76+
isMethod: true
9877
},
9978
displayText: method,
10079
from,
@@ -111,34 +90,48 @@ export default function contextAwareHinter(cm, options = {}) {
11190
const currentContext = parseCode(cm);
11291
const allHints = hinter.search(currentWord);
11392

114-
const whitelist = scopeMap[currentContext]?.whitelist || [];
93+
// const whitelist = scopeMap[currentContext]?.whitelist || [];
11594
const blacklist = scopeMap[currentContext]?.blacklist || [];
11695

11796
const lowerCurrentWord = currentWord.toLowerCase();
11897

11998
function isInScope(varName) {
120-
const varScope = varScopeMap[varName];
121-
if (!varScope) return false;
122-
if (varScope === 'global') return true;
123-
if (varScope === currentContext) return true;
124-
return false;
99+
return Object.entries(varScopeMap).some(
100+
([scope, vars]) =>
101+
vars.has(varName) && (scope === 'global' || scope === currentContext)
102+
);
125103
}
126104

127-
const varHints = varMap
105+
const allVarNames = Array.from(
106+
new Set(
107+
Object.values(varScopeMap)
108+
.map((s) => Array.from(s)) // convert Set to Array
109+
.flat()
110+
.filter((name) => typeof name === 'string')
111+
)
112+
);
113+
114+
const varHints = allVarNames
128115
.filter(
129116
(varName) =>
130117
varName.toLowerCase().startsWith(lowerCurrentWord) && isInScope(varName)
131118
)
132-
.map((varName) => ({
133-
item: {
134-
text: varName,
135-
type: userFuncMap[varName] ? 'fun' : 'var'
136-
},
137-
isBlacklisted: blacklist.includes(varName),
138-
displayText: formatHintDisplay(varName, blacklist.includes(varName)),
139-
from: { line, ch },
140-
to: { line, ch: ch - currentWord.length }
141-
}));
119+
.map((varName) => {
120+
const isFunc = !!userFuncMap[varName];
121+
const baseItem = isFunc
122+
? { ...userFuncMap[varName] }
123+
: {
124+
text: varName,
125+
type: 'var',
126+
params: [],
127+
p5: false
128+
};
129+
130+
return {
131+
item: baseItem,
132+
isBlacklisted: blacklist.includes(varName)
133+
};
134+
});
142135

143136
const filteredHints = allHints
144137
.filter(
@@ -154,8 +147,7 @@ export default function contextAwareHinter(cm, options = {}) {
154147

155148
return {
156149
...hint,
157-
isBlacklisted,
158-
displayText: formatHintDisplay(name, isBlacklisted)
150+
isBlacklisted
159151
};
160152
});
161153

client/modules/IDE/components/finalScopeMap.json

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -403,6 +403,11 @@
403403
"loadTable",
404404
"loadXML",
405405
"soundFormats"
406+
],
407+
"blacklist":[
408+
"preload",
409+
"setup",
410+
"draw"
406411
]
407412
},
408413
"doubleClicked": {

client/modules/IDE/components/parseCode.js

Lines changed: 12 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
1-
const acorn = require('acorn');
2-
const walk = require('acorn-walk');
1+
const parser = require('@babel/parser');
2+
const traverse = require('@babel/traverse').default;
33

44
export default function parseCode(_cm) {
55
const code = _cm.getValue();
@@ -8,37 +8,35 @@ export default function parseCode(_cm) {
88

99
let ast;
1010
try {
11-
ast = acorn.parse(code, {
12-
ecmaVersion: 'latest',
13-
sourceType: 'script'
11+
ast = parser.parse(code, {
12+
sourceType: 'script',
13+
plugins: ['jsx', 'typescript'] // add plugins as needed
1414
});
1515
} catch (e) {
16-
console.warn('Failed to parse code', e.message);
16+
// console.warn('Failed to parse code with Babel:', e.message);
1717
return 'global';
1818
}
1919

2020
let context = 'global';
2121

22-
walk.fullAncestor(ast, (node, ancestors) => {
23-
if (
24-
node.type === 'FunctionDeclaration' ||
25-
node.type === 'FunctionExpression' ||
26-
node.type === 'ArrowFunctionExpression'
27-
) {
22+
traverse(ast, {
23+
Function(path) {
24+
const { node } = path;
2825
if (offset >= node.start && offset <= node.end) {
2926
if (node.id && node.id.name) {
3027
context = node.id.name;
3128
} else {
32-
const parent = ancestors[ancestors.length - 2];
29+
const parent = path.parentPath.node;
3330
if (
34-
parent?.type === 'VariableDeclarator' &&
31+
parent.type === 'VariableDeclarator' &&
3532
parent.id.type === 'Identifier'
3633
) {
3734
context = parent.id.name;
3835
} else {
3936
context = '(anonymous)';
4037
}
4138
}
39+
path.stop(); // Stop traversal once we found the function context
4240
}
4341
}
4442
});

client/modules/IDE/components/parseCodeBabel.js

Lines changed: 0 additions & 46 deletions
This file was deleted.

0 commit comments

Comments
 (0)