diff --git a/packages/language-server/go-to-definitions/go-to-action.js b/packages/language-server/go-to-definitions/go-to-action.js index 098db32..d626edd 100644 --- a/packages/language-server/go-to-definitions/go-to-action.js +++ b/packages/language-server/go-to-definitions/go-to-action.js @@ -1,7 +1,6 @@ const lsp = require('vscode-languageserver/node') const path = require('path') -const fs = require('fs').promises -const url = require('url') +const findFnLine = require('../helpers/find-fn-line') module.exports = async function goToAction(document, position) { const fileName = path.basename(document.uri) @@ -65,18 +64,3 @@ function extractActionInfo(document, position) { function resolveActionPath(projectRoot, actionPath) { return path.join(projectRoot, 'api', 'controllers', `${actionPath}.js`) } - -async function findFnLine(filePath) { - try { - const content = await fs.readFile(url.fileURLToPath(filePath), 'utf8') - const lines = content.split('\n') - for (let i = 0; i < lines.length; i++) { - if (lines[i].includes('fn:')) { - return i // Return the line number (0-based index) - } - } - return 0 // If 'fn:' is not found, return the first line - } catch (error) { - return 0 // Return the first line if there's an error - } -} diff --git a/packages/language-server/go-to-definitions/go-to-helper.js b/packages/language-server/go-to-definitions/go-to-helper.js new file mode 100644 index 0000000..3e5da2e --- /dev/null +++ b/packages/language-server/go-to-definitions/go-to-helper.js @@ -0,0 +1,47 @@ +const lsp = require('vscode-languageserver/node') +const path = require('path') +const fs = require('fs').promises +const findProjectRoot = require('../helpers/find-project-root') +const findFnLine = require('../helpers/find-fn-line') + +module.exports = async function goToHelper(document, position) { + const helperInfo = extractHelperInfo(document, position) + + if (!helperInfo) { + return null + } + + const projectRoot = await findProjectRoot(document.uri) + const fullHelperPath = + path.join(projectRoot, 'api', 'helpers', ...helperInfo.helperPath) + '.js' + + if (fullHelperPath) { + const fnLineNumber = await findFnLine(fullHelperPath) + return lsp.Location.create( + fullHelperPath, + lsp.Range.create(fnLineNumber, 0, fnLineNumber, 0) + ) + } +} + +function extractHelperInfo(document, position) { + const text = document.getText() + const offset = document.offsetAt(position) + + // Regular expression to match sails.helpers.exampleHelper() or sails.helpers.exampleHelper.with() + // Also matches nested helpers like sails.helpers.mail.send() or sails.helpers.mail.send.with() + const regex = /sails\.helpers\.([a-zA-Z0-9.]+)(?:\.with)?\s*\(/g + let match + + while ((match = regex.exec(text)) !== null) { + const start = match.index + const end = start + match[0].length + + if (start <= offset && offset <= end) { + const helperPath = match[1].split('.').filter((part) => part !== 'with') + return { helperPath } + } + } + + return null +} diff --git a/packages/language-server/helpers/find-fn-line.js b/packages/language-server/helpers/find-fn-line.js new file mode 100644 index 0000000..77226a1 --- /dev/null +++ b/packages/language-server/helpers/find-fn-line.js @@ -0,0 +1,21 @@ +const fs = require('fs').promises +const url = require('url') + +module.exports = async function findFnLine(filePath) { + try { + const resolvedPath = filePath.startsWith('file:') + ? url.fileURLToPath(filePath) + : filePath + + const content = await fs.readFile(resolvedPath, 'utf8') + const lines = content.split('\n') + for (let i = 0; i < lines.length; i++) { + if (lines[i].includes('fn:')) { + return i // Return the line number (0-based index) + } + } + return 0 // If 'fn:' is not found, return the first line + } catch (error) { + return 0 // Return the first line if there's an error + } +} diff --git a/packages/language-server/index.js b/packages/language-server/index.js index ef45ecb..85b838e 100644 --- a/packages/language-server/index.js +++ b/packages/language-server/index.js @@ -1,10 +1,17 @@ const lsp = require('vscode-languageserver/node') const TextDocument = require('vscode-languageserver-textdocument').TextDocument + +// Validators const validateDocument = require('./validators/validate-document') + +// Go-to definitions const goToAction = require('./go-to-definitions/go-to-action') const goToPolicy = require('./go-to-definitions/go-to-policy') const goToView = require('./go-to-definitions/go-to-view') const goToInertiaPage = require('./go-to-definitions/go-to-inertia-page') +const goToHelper = require('./go-to-definitions/go-to-helper') + +// Completions const sailsCompletions = require('./completions/sails-completions') const connection = lsp.createConnection(lsp.ProposedFeatures.all) @@ -40,12 +47,14 @@ connection.onDefinition(async (params) => { const policyDefinition = await goToPolicy(document, params.position) const viewDefinition = await goToView(document, params.position) const inertiaPageDefinition = await goToInertiaPage(document, params.position) + const helperDefinition = await goToHelper(document, params.position) const definitions = [ actionDefinition, policyDefinition, viewDefinition, - inertiaPageDefinition + inertiaPageDefinition, + helperDefinition ].filter(Boolean) return definitions.length > 0 ? definitions : null