Description
Is your feature request related to a problem? Please describe
I want a better way to find the NGINX context based on line number.
I was attempting to write a language server for NGINX, using nginx-go-crossplane
to parse files and then cross-reference with nginx-directive-reference
to drive IDE features like autocomplete and accurate documentation.
The problem I have is NGINX directives often re-use a name; if my language server wants to show a tooltip and when the user hovers over a listen
directive, I don't have a good way to tell if they mean HTTP listen
, stream listen
or mail listen
.
Sometimes it's possible to walk the Config.Parsed
tree to detect parent context.
Sometimes the user might ask for autocomplete suggestions on a blank line, and all I have to work with is a line number. The Payload
does not contain enough information to determine NGINX context if there's not a directive on that line.
Describe the solution you'd like
Parse
keeps a record of the current NGINX context (blockCtx
), I'd like that information to escape.
type Directive struct {
Directive string `json:"directive"`
Line int `json:"line"`
+ // EndLine is the line number with the } that ends this block directive. Will be nil for directives that are not blocks.
+ EndLine *int `json:"endLine,omitempty"`
Args []string `json:"args"`
I think this can be captured with some non-trivial refactoring around parser.Parse
to return the line number with recursive calls.
Describe alternatives you've considered
I tried to recursively walk Config.Parsed
, keeping track of the last directive where d.Line < x && len(d.Block) > 0
, but this fails to detect when a context ends.
From F5 internal prototype:
export function findClosestDirective(p: Payload, line: number): Directive | undefined {
let possibleParent: Directive | undefined = undefined;
for (const d of allDirectives(p)) { // recursively walks the directive tree and generates a flat list
if (d.line === line) {
return d
}
if (d.line < line && d.block) {
possibleParent = d
}
if (d.line > line) {
return possibleParent
}
}
return possibleParent
}
http {
server { # ✅ I can walk the Config.Parsed tree to see this server is in an http
# ✅ on a blank the last seen context on this line is "server"
}
# ❌ on a blank the last seen context on this line is "server", as the } is not represented in the payload
}
I considering requesting something easier to implement like:
type Directive struct {
Directive string `json:"directive"`
Line int `json:"line"`
+ // Context is the stack of NGINX contexts that contain this directive.
+ Context []string `json:"context,omitempty"`
Args []string `json:"args"`
But doesn't doesn't solve the use case where all I have is a line number.