Skip to content

Commit

Permalink
feat: implement go to policy
Browse files Browse the repository at this point in the history
  • Loading branch information
DominusKelvin committed Sep 11, 2024
1 parent 289f294 commit c535cfa
Show file tree
Hide file tree
Showing 2 changed files with 98 additions and 4 deletions.
90 changes: 90 additions & 0 deletions packages/language-server/go-to-definitions/go-to-policy.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,90 @@
const lsp = require('vscode-languageserver/node')
const path = require('path')
const fs = require('fs').promises

module.exports = async function goToPolicy(document, position) {
const fileName = path.basename(document.uri)

if (fileName !== 'policies.js') {
return null
}

const policyInfo = extractPolicyInfo(document, position)

if (!policyInfo) {
return null
}

const projectRoot = path.dirname(path.dirname(document.uri))
const fullPolicyPath = resolvePolicyPath(projectRoot, policyInfo.policy)

if (await fileExists(fullPolicyPath)) {
return lsp.Location.create(fullPolicyPath, lsp.Range.create(0, 0, 0, 0))
}

return null
}

function extractPolicyInfo(document, position) {
const text = document.getText()
const offset = document.offsetAt(position)

// This regex matches policy definitions, including arrays of policies and boolean values
const regex =
/(['"])((?:\*|[\w-]+(?:\/\*?)?))?\1\s*:\s*((?:\[?\s*(?:(?:['"][\w-]+['"](?:\s*,\s*)?)+)\s*\]?)|true|false)/g
let match

while ((match = regex.exec(text)) !== null) {
const [fullMatch, , route, policiesOrBoolean] = match
const start = match.index
const end = start + fullMatch.length

// Check if the cursor is anywhere within the entire match
if (start <= offset && offset <= end) {
// If policiesOrBoolean is a boolean, ignore it
if (policiesOrBoolean === true || policiesOrBoolean === false) {
continue
}

// Remove brackets if present and split into individual policies
const policies = policiesOrBoolean
.replace(/^\[|\]$/g, '')
.split(',')
.map((p) => p.trim().replace(/^['"]|['"]$/g, ''))

// Find which policy the cursor is on
let currentStart = start + fullMatch.indexOf(policiesOrBoolean)
for (const policy of policies) {
const policyStart = text.indexOf(policy, currentStart)
const policyEnd = policyStart + policy.length

if (offset >= policyStart && offset <= policyEnd) {
return {
policy,
range: lsp.Range.create(
document.positionAt(policyStart),
document.positionAt(policyEnd)
)
}
}

currentStart = policyEnd
}
}
}

return null
}

function resolvePolicyPath(projectRoot, policyPath) {
return path.join(projectRoot, 'api', 'policies', `${policyPath}.js`)
}

async function fileExists(filePath) {
try {
await fs.access(new URL(filePath))
return true
} catch {
return false
}
}
12 changes: 8 additions & 4 deletions packages/language-server/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ const lsp = require('vscode-languageserver/node')
const TextDocument = require('vscode-languageserver-textdocument').TextDocument
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 connection = lsp.createConnection(lsp.ProposedFeatures.all)
const documents = new lsp.TextDocuments(TextDocument)
Expand Down Expand Up @@ -32,10 +33,13 @@ connection.onDefinition(async (params) => {
if (!document) {
return null
}
const definitions = []
const actionDefinitions = await goToAction(document, params.position)
definitions.push(actionDefinitions)
return definitions || null

const actionDefinition = await goToAction(document, params.position)
const policyDefinition = await goToPolicy(document, params.position)

const definitions = [actionDefinition, policyDefinition].filter(Boolean)

return definitions.length > 0 ? definitions : null
})

documents.listen(connection)
Expand Down

0 comments on commit c535cfa

Please sign in to comment.