From 8301ef0251ccfc10dd7d4d15ac23a0b1a7e0119b Mon Sep 17 00:00:00 2001 From: Kelvin Oghenerhoro Omereshone Date: Fri, 13 Sep 2024 15:41:14 +0100 Subject: [PATCH 1/6] fix(language-server): use findProjectRoot --- .../go-to-definitions/go-to-view.js | 18 ++---------------- 1 file changed, 2 insertions(+), 16 deletions(-) diff --git a/packages/language-server/go-to-definitions/go-to-view.js b/packages/language-server/go-to-definitions/go-to-view.js index 4d4159e..598041d 100644 --- a/packages/language-server/go-to-definitions/go-to-view.js +++ b/packages/language-server/go-to-definitions/go-to-view.js @@ -1,7 +1,8 @@ const lsp = require('vscode-languageserver/node') const path = require('path') const fs = require('fs').promises -const url = require('url') + +const findProjectRoot = require('../helpers/find-project-root') module.exports = async function goToView(document, position) { const viewInfo = extractViewInfo(document, position) @@ -67,18 +68,3 @@ function extractViewInfo(document, position) { function resolveViewPath(projectRoot, viewPath) { return path.join(projectRoot, 'views', `${viewPath}.ejs`) } - -async function findProjectRoot(uri) { - let currentPath = path.dirname(url.fileURLToPath(uri)) - const root = path.parse(currentPath).root - - while (currentPath !== root) { - try { - await fs.access(path.join(currentPath, 'package.json')) - return currentPath - } catch (error) { - currentPath = path.dirname(currentPath) - } - } - throw new Error('Could not find project root') -} From de609b446152f02a249daece4126f0d8a173aac1 Mon Sep 17 00:00:00 2001 From: Kelvin Oghenerhoro Omereshone Date: Fri, 13 Sep 2024 15:41:50 +0100 Subject: [PATCH 2/6] feat(language-server): and helper to load-sails --- .../language-server/helpers/load-sails.js | 38 +++++++++++++++++++ 1 file changed, 38 insertions(+) create mode 100644 packages/language-server/helpers/load-sails.js diff --git a/packages/language-server/helpers/load-sails.js b/packages/language-server/helpers/load-sails.js new file mode 100644 index 0000000..7d3a4f1 --- /dev/null +++ b/packages/language-server/helpers/load-sails.js @@ -0,0 +1,38 @@ +const findSails = require('./find-sails') + +module.exports = async function loadSails(workspaceUri, operation) { + let Sails + let sailsApp + + try { + const { sailsPath } = await findSails(workspaceUri) + Sails = require(sailsPath).constructor + + sailsApp = await new Promise((resolve, reject) => { + new Sails().load( + { + hooks: { shipwright: false }, + log: { level: 'silent' } + }, + (err, sails) => { + if (err) { + console.error('Failed to load Sails app:', err) + return reject(err) + } + resolve(sails) + } + ) + }) + + // Execute the operation with the loaded Sails app + return await operation(sailsApp) + } catch (error) { + console.error('Error loading or working with Sails app:', error) + throw error + } finally { + // Ensure Sails is lowered even if an error occurred + if (sailsApp && typeof sailsApp.lower === 'function') { + await new Promise((resolve) => sailsApp.lower(resolve)) + } + } +} From 147fda95d4af30abd0d2a20be94b493ee1a6f01f Mon Sep 17 00:00:00 2001 From: Kelvin Oghenerhoro Omereshone Date: Fri, 13 Sep 2024 15:42:15 +0100 Subject: [PATCH 3/6] feat: helper to find the path of Sails --- packages/language-server/helpers/find-sails.js | 12 ++++++++++++ 1 file changed, 12 insertions(+) create mode 100644 packages/language-server/helpers/find-sails.js diff --git a/packages/language-server/helpers/find-sails.js b/packages/language-server/helpers/find-sails.js new file mode 100644 index 0000000..12c3517 --- /dev/null +++ b/packages/language-server/helpers/find-sails.js @@ -0,0 +1,12 @@ +const path = require('path') +const fs = require('fs') +const findProjectRoot = require('./find-project-root') + +module.exports = async function findSails(workspaceUri) { + const projectRoot = await findProjectRoot(workspaceUri) + const sailsPath = path.join(projectRoot, 'node_modules', 'sails') + if (fs.existsSync(sailsPath)) { + return { sailsPath, projectRoot } + } + throw new Error('Sails not found in node_modules') +} From b39e8ba17508aca5ac036442c60ce88e56b2e68b Mon Sep 17 00:00:00 2001 From: Kelvin Oghenerhoro Omereshone Date: Fri, 13 Sep 2024 15:43:09 +0100 Subject: [PATCH 4/6] feat(language-server): update info to find project root --- .../helpers/find-project-root.js | 18 ++++++++++++++++++ 1 file changed, 18 insertions(+) create mode 100644 packages/language-server/helpers/find-project-root.js diff --git a/packages/language-server/helpers/find-project-root.js b/packages/language-server/helpers/find-project-root.js new file mode 100644 index 0000000..d92a316 --- /dev/null +++ b/packages/language-server/helpers/find-project-root.js @@ -0,0 +1,18 @@ +const path = require('path') +const url = require('url') +const fs = require('fs').promises + +module.exports = async function findProjectRoot(uri) { + let currentPath = path.dirname(url.fileURLToPath(uri)) + const root = path.parse(currentPath).root + + while (currentPath !== root) { + try { + await fs.access(path.join(currentPath, 'package.json')) + return currentPath + } catch (error) { + currentPath = path.dirname(currentPath) + } + } + throw new Error('Could not find project root') +} From 52cbeadaf6c13f2344dd2ba3f097606176c1aaab Mon Sep 17 00:00:00 2001 From: Kelvin Oghenerhoro Omereshone Date: Fri, 13 Sep 2024 15:43:27 +0100 Subject: [PATCH 5/6] feat(language-server): add Sails completion --- packages/language-server/index.js | 28 +++++++++++++++++++++++----- 1 file changed, 23 insertions(+), 5 deletions(-) diff --git a/packages/language-server/index.js b/packages/language-server/index.js index e02f649..18d3af2 100644 --- a/packages/language-server/index.js +++ b/packages/language-server/index.js @@ -4,6 +4,7 @@ const validateDocument = require('./validators/validate-document') 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 sailsCompletions = require('./completions/sails-completions') const connection = lsp.createConnection(lsp.ProposedFeatures.all) const documents = new lsp.TextDocuments(TextDocument) @@ -12,11 +13,10 @@ connection.onInitialize((params) => { return { capabilities: { textDocumentSync: lsp.TextDocumentSyncKind.Incremental, - definitionProvider: true - // completionProvider: { - // resolveProvider: true, - // triggerCharacters: ['"', "'", '.'] - // } + definitionProvider: true, + completionProvider: { + triggerCharacters: ['"', "'", '.'] + } } } }) @@ -48,6 +48,24 @@ connection.onDefinition(async (params) => { return definitions.length > 0 ? definitions : null }) +connection.onCompletion(async (params) => { + const document = documents.get(params.textDocument.uri) + if (!document) { + return null + } + + const completions = await sailsCompletions(document, params.position) + + if (completions) { + return { + isIncomplete: false, + items: completions + } + } + + return null +}) + documents.listen(connection) connection.listen() From cd7df23a3c2bdf796d723e35dda1bcf48d5f481f Mon Sep 17 00:00:00 2001 From: Kelvin Oghenerhoro Omereshone Date: Fri, 13 Sep 2024 15:43:44 +0100 Subject: [PATCH 6/6] feat(language-server): add Sails completion provider --- .../completions/sails-completions.js | 63 +++++++++++++++++++ 1 file changed, 63 insertions(+) create mode 100644 packages/language-server/completions/sails-completions.js diff --git a/packages/language-server/completions/sails-completions.js b/packages/language-server/completions/sails-completions.js new file mode 100644 index 0000000..3a168c2 --- /dev/null +++ b/packages/language-server/completions/sails-completions.js @@ -0,0 +1,63 @@ +const lsp = require('vscode-languageserver/node') +const loadSails = require('../helpers/load-sails') + +module.exports = async function sailsCompletions(document, position) { + const text = document.getText() + const offset = document.offsetAt(position) + const line = text.substring(0, offset).split('\n').pop() + + const match = line.match(/sails((?:\.[a-zA-Z_$][0-9a-zA-Z_$]*)*)\.$/) + if (match) { + try { + return await loadSails(document.uri, (sailsApp) => { + const path = match[1].split('.').filter(Boolean) + return getNestedCompletions(sailsApp, path) + }) + } catch (error) { + return [] + } + } + + return null +} + +function getNestedCompletions(obj, path) { + let current = obj + for (const key of path) { + if (current && typeof current === 'object' && key in current) { + current = current[key] + } else { + return [] + } + } + + if (typeof current !== 'object' || current === null) { + return [] + } + + const completions = Object.keys(current).map((key) => { + const value = current[key] + let kind = lsp.CompletionItemKind.Property + let detail = 'Property' + + if (typeof value === 'function') { + kind = lsp.CompletionItemKind.Method + detail = 'Method' + } else if (typeof value === 'object' && value !== null) { + detail = 'Object' + } + + return { + label: key, + kind: kind, + detail: detail, + documentation: `Access to sails${path.length ? '.' + path.join('.') : ''}.${key}`, + sortText: key.startsWith('_') ? `z${key}` : key // Add this line + } + }) + + // Sort the completions + completions.sort((a, b) => a.sortText.localeCompare(b.sortText)) + + return completions +}