|
1 | 1 | /* eslint-disable */
|
2 | 2 | import p5CodeAstAnalyzer from './p5CodeAstAnalyzer';
|
| 3 | +import * as parser from '@babel/parser'; |
3 | 4 | import { getAST, getContext } from './rename-variable';
|
| 5 | +import { selectFiles } from '../selectors/files'; |
| 6 | +import { setSelectedFile } from '../actions/ide'; |
4 | 7 | import announceToScreenReader from '../utils/ScreenReaderHelper';
|
| 8 | +import store from '../../../index'; |
5 | 9 | const traverse = require('@babel/traverse').default;
|
6 | 10 |
|
| 11 | +function getScriptLoadOrder(files) { |
| 12 | + const indexHtmlFile = files.find((f) => f.name.endsWith('index.html')); |
| 13 | + if (!indexHtmlFile) return []; |
| 14 | + |
| 15 | + const scriptRegex = /<script\s+[^>]*src=["']([^"']+)["']/g; |
| 16 | + const scripts = []; |
| 17 | + let match; |
| 18 | + while ((match = scriptRegex.exec(indexHtmlFile.content)) !== null) { |
| 19 | + scripts.push(match[1]); |
| 20 | + } |
| 21 | + return scripts; |
| 22 | +} |
| 23 | + |
| 24 | +function buildProjectSymbolTable(files, scriptOrder) { |
| 25 | + const symbolTable = {}; |
| 26 | + |
| 27 | + for (const scriptName of scriptOrder) { |
| 28 | + const file = files.find((f) => f.name.endsWith(scriptName)); |
| 29 | + if (!file) continue; |
| 30 | + |
| 31 | + let ast; |
| 32 | + try { |
| 33 | + ast = parser.parse(file.content, { |
| 34 | + sourceType: 'script', |
| 35 | + plugins: ['jsx', 'typescript'] |
| 36 | + }); |
| 37 | + } catch (e) { |
| 38 | + continue; |
| 39 | + } |
| 40 | + |
| 41 | + traverse(ast, { |
| 42 | + FunctionDeclaration(path) { |
| 43 | + const name = path.node.id?.name; |
| 44 | + if (name && !symbolTable[name]) { |
| 45 | + symbolTable[name] = { |
| 46 | + file: file.name, |
| 47 | + pos: path.node.start |
| 48 | + }; |
| 49 | + } |
| 50 | + }, |
| 51 | + VariableDeclarator(path) { |
| 52 | + const name = path.node.id?.name; |
| 53 | + if (name && !symbolTable[name]) { |
| 54 | + symbolTable[name] = { |
| 55 | + file: file.name, |
| 56 | + pos: path.node.start |
| 57 | + }; |
| 58 | + } |
| 59 | + } |
| 60 | + }); |
| 61 | + } |
| 62 | + |
| 63 | + return symbolTable; |
| 64 | +} |
| 65 | + |
7 | 66 | export function jumpToDefinition(pos) {
|
| 67 | + const state = store.getState(); |
| 68 | + const files = selectFiles(state); |
| 69 | + |
8 | 70 | const cm = this._cm;
|
9 | 71 | const token = cm.getTokenAt(pos);
|
10 | 72 | const tokenType = token.type;
|
@@ -154,6 +216,37 @@ export function jumpToDefinition(pos) {
|
154 | 216 | }
|
155 | 217 | });
|
156 | 218 | }
|
| 219 | + if (!found) { |
| 220 | + const scriptOrder = getScriptLoadOrder(files); |
| 221 | + |
| 222 | + const projectSymbolTable = |
| 223 | + buildProjectSymbolTable(files, scriptOrder) || {}; |
| 224 | + const globalSymbol = projectSymbolTable[varName]; |
| 225 | + |
| 226 | + if (globalSymbol) { |
| 227 | + const targetFileObj = files.find((f) => f.name === globalSymbol.file); |
| 228 | + if (!targetFileObj) { |
| 229 | + return; |
| 230 | + } |
| 231 | + |
| 232 | + store.dispatch(setSelectedFile(targetFileObj.id)); |
| 233 | + |
| 234 | + if (!targetFileObj.cmInstance) { |
| 235 | + return; |
| 236 | + } |
| 237 | + |
| 238 | + const targetFileCM = targetFileObj.cmInstance; |
| 239 | + const defPos = targetFileCM.posFromIndex(globalSymbol.pos); |
| 240 | + targetFileCM.setCursor(defPos); |
| 241 | + targetFileCM.focus(); |
| 242 | + |
| 243 | + announceToScreenReader( |
| 244 | + `Jumped to definition of ${varName} in ${globalSymbol.file}` |
| 245 | + ); |
| 246 | + found = true; |
| 247 | + } |
| 248 | + } |
| 249 | + |
157 | 250 | if (!found) {
|
158 | 251 | announceToScreenReader(`No definition found for ${varName}`, true);
|
159 | 252 | }
|
|
0 commit comments