225 lines
5.7 KiB
Plaintext
225 lines
5.7 KiB
Plaintext
// Document symbols and go-to-definition provider for the ƿit LSP.
|
|
|
|
// SymbolKind constants (LSP spec)
|
|
def KIND_FUNCTION = 12
|
|
def KIND_VARIABLE = 13
|
|
def KIND_CONSTANT = 14
|
|
|
|
// Walk AST to extract document symbols (top-level vars/defs and functions).
|
|
var document_symbols = function(doc) {
|
|
var symbols = []
|
|
var ast = doc.ast
|
|
var _i = 0
|
|
var _j = 0
|
|
var stmt = null
|
|
var decl = null
|
|
var name = null
|
|
var kind = null
|
|
var range = null
|
|
|
|
if (ast == null || ast.statements == null) {
|
|
return symbols
|
|
}
|
|
|
|
while (_i < length(ast.statements)) {
|
|
stmt = ast.statements[_i]
|
|
|
|
if (stmt.kind == "var" || stmt.kind == "def") {
|
|
name = null
|
|
kind = KIND_VARIABLE
|
|
|
|
if (stmt.left != null && stmt.left.name != null) {
|
|
name = stmt.left.name
|
|
}
|
|
|
|
if (stmt.kind == "def") {
|
|
kind = KIND_CONSTANT
|
|
}
|
|
|
|
if (stmt.right != null && (stmt.right.kind == "function" || stmt.right.kind == "arrow function")) {
|
|
kind = KIND_FUNCTION
|
|
}
|
|
|
|
if (name != null) {
|
|
range = {
|
|
start: {line: stmt.from_row, character: stmt.from_column},
|
|
end: {line: stmt.to_row, character: stmt.to_column}
|
|
}
|
|
symbols[] = {
|
|
name: name,
|
|
kind: kind,
|
|
range: range,
|
|
selectionRange: {
|
|
start: {line: stmt.left.from_row, character: stmt.left.from_column},
|
|
end: {line: stmt.left.to_row, character: stmt.left.to_column}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
if (stmt.kind == "var_list" && stmt.list != null) {
|
|
_j = 0
|
|
while (_j < length(stmt.list)) {
|
|
decl = stmt.list[_j]
|
|
if (decl.left != null && decl.left.name != null) {
|
|
kind = (decl.kind == "def") ? KIND_CONSTANT : KIND_VARIABLE
|
|
if (decl.right != null && (decl.right.kind == "function" || decl.right.kind == "arrow function")) {
|
|
kind = KIND_FUNCTION
|
|
}
|
|
range = {
|
|
start: {line: decl.from_row, character: decl.from_column},
|
|
end: {line: decl.to_row, character: decl.to_column}
|
|
}
|
|
symbols[] = {
|
|
name: decl.left.name,
|
|
kind: kind,
|
|
range: range,
|
|
selectionRange: {
|
|
start: {line: decl.left.from_row, character: decl.left.from_column},
|
|
end: {line: decl.left.to_row, character: decl.left.to_column}
|
|
}
|
|
}
|
|
}
|
|
_j = _j + 1
|
|
}
|
|
}
|
|
|
|
_i = _i + 1
|
|
}
|
|
|
|
return symbols
|
|
}
|
|
|
|
// Find the declaration location of a name at a given position.
|
|
// Uses the semantic index when available, falls back to AST walk.
|
|
var definition = function(doc, line, col, token_at) {
|
|
var tok = token_at(doc, line, col)
|
|
var name = null
|
|
var _i = 0
|
|
var sym = null
|
|
var decl = null
|
|
|
|
if (tok == null || tok.kind != "name" || tok.value == null) {
|
|
return null
|
|
}
|
|
|
|
name = tok.value
|
|
|
|
// Use the semantic index if available
|
|
if (doc.index != null) {
|
|
_i = 0
|
|
while (_i < length(doc.index.symbols)) {
|
|
sym = doc.index.symbols[_i]
|
|
if (sym.name == name && sym.decl_span != null) {
|
|
return {
|
|
uri: doc.uri,
|
|
range: {
|
|
start: {line: sym.decl_span.from_row, character: sym.decl_span.from_col},
|
|
end: {line: sym.decl_span.to_row, character: sym.decl_span.to_col}
|
|
}
|
|
}
|
|
}
|
|
_i = _i + 1
|
|
}
|
|
}
|
|
|
|
// Fallback: walk statements for var/def with this name
|
|
if (doc.ast != null) {
|
|
decl = find_declaration(doc.ast.statements, name)
|
|
if (decl != null) {
|
|
return {
|
|
uri: doc.uri,
|
|
range: {
|
|
start: {line: decl.from_row, character: decl.from_column},
|
|
end: {line: decl.to_row, character: decl.to_column}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
return null
|
|
}
|
|
|
|
// Recursively search statements for a var/def declaration of a given name.
|
|
var find_declaration = function(statements, name) {
|
|
var _i = 0
|
|
var _j = 0
|
|
var stmt = null
|
|
var result = null
|
|
|
|
if (statements == null) {
|
|
return null
|
|
}
|
|
|
|
while (_i < length(statements)) {
|
|
stmt = statements[_i]
|
|
|
|
// Direct var/def
|
|
if ((stmt.kind == "var" || stmt.kind == "def")
|
|
&& stmt.left != null && stmt.left.name == name) {
|
|
return stmt
|
|
}
|
|
|
|
// var_list
|
|
if (stmt.kind == "var_list" && stmt.list != null) {
|
|
_j = 0
|
|
while (_j < length(stmt.list)) {
|
|
if (stmt.list[_j].left != null && stmt.list[_j].left.name == name) {
|
|
return stmt.list[_j]
|
|
}
|
|
_j = _j + 1
|
|
}
|
|
}
|
|
|
|
// Recurse into blocks
|
|
if (stmt.statements != null) {
|
|
result = find_declaration(stmt.statements, name)
|
|
if (result != null) {
|
|
return result
|
|
}
|
|
}
|
|
|
|
// if/else
|
|
if (stmt.kind == "if") {
|
|
if (stmt.then != null && stmt.then.statements != null) {
|
|
result = find_declaration(stmt.then.statements, name)
|
|
if (result != null) {
|
|
return result
|
|
}
|
|
}
|
|
if (stmt.else != null && stmt.else.statements != null) {
|
|
result = find_declaration(stmt.else.statements, name)
|
|
if (result != null) {
|
|
return result
|
|
}
|
|
}
|
|
}
|
|
|
|
// Function body
|
|
if ((stmt.kind == "function" || stmt.kind == "arrow function") && stmt.statements != null) {
|
|
result = find_declaration(stmt.statements, name)
|
|
if (result != null) {
|
|
return result
|
|
}
|
|
}
|
|
|
|
// var/def with function right side
|
|
if ((stmt.kind == "var" || stmt.kind == "def") && stmt.right != null) {
|
|
if ((stmt.right.kind == "function" || stmt.right.kind == "arrow function") && stmt.right.statements != null) {
|
|
result = find_declaration(stmt.right.statements, name)
|
|
if (result != null) {
|
|
return result
|
|
}
|
|
}
|
|
}
|
|
|
|
_i = _i + 1
|
|
}
|
|
return null
|
|
}
|
|
|
|
return {
|
|
document_symbols: document_symbols,
|
|
definition: definition
|
|
}
|