-
Notifications
You must be signed in to change notification settings - Fork 135
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Symbols Support #265
Symbols Support #265
Changes from 4 commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -167,6 +167,26 @@ func (p *parser) BlockTypeCandidates(file ihcl.TokenizedFile, pos hcl.Pos) Compl | |
return list | ||
} | ||
|
||
func (p *parser) Blocks(file ihcl.TokenizedFile) ([]ConfigBlock, error) { | ||
blocks, err := file.Blocks() | ||
if err != nil { | ||
return nil, err | ||
} | ||
|
||
var cfgBlocks []ConfigBlock | ||
|
||
for _, block := range blocks { | ||
cfgBlock, err := p.ParseBlockFromTokens(block) | ||
if err != nil { | ||
p.logger.Printf("parsing config block failed: %s", err) | ||
continue | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Just curious what's the motivation here behind logging and not erroring out here? There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I did not want a file with blocks we don't support yet There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Gotcha, that makes sense - can you just document this reasoning in the form of an in-line comment above the log line? |
||
} | ||
cfgBlocks = append(cfgBlocks, cfgBlock) | ||
} | ||
|
||
return cfgBlocks, nil | ||
} | ||
|
||
type completableBlockType struct { | ||
TypeName string | ||
LabelSchema LabelSchema | ||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,91 @@ | ||
package handlers | ||
|
||
import ( | ||
"context" | ||
"fmt" | ||
"time" | ||
|
||
lsctx "github.com/hashicorp/terraform-ls/internal/context" | ||
ihcl "github.com/hashicorp/terraform-ls/internal/hcl" | ||
ilsp "github.com/hashicorp/terraform-ls/internal/lsp" | ||
"github.com/hashicorp/terraform-ls/internal/terraform/lang" | ||
"github.com/sourcegraph/go-lsp" | ||
) | ||
|
||
func (h *logHandler) TextDocumentSymbol(ctx context.Context, params lsp.DocumentSymbolParams) ([]lsp.SymbolInformation, error) { | ||
var symbols []lsp.SymbolInformation | ||
|
||
fs, err := lsctx.DocumentStorage(ctx) | ||
if err != nil { | ||
return symbols, err | ||
} | ||
|
||
pf, err := lsctx.ParserFinder(ctx) | ||
if err != nil { | ||
return symbols, err | ||
} | ||
|
||
file, err := fs.GetDocument(ilsp.FileHandlerFromDocumentURI(params.TextDocument.URI)) | ||
if err != nil { | ||
return symbols, err | ||
} | ||
|
||
text, err := file.Text() | ||
if err != nil { | ||
return symbols, err | ||
} | ||
|
||
hclFile := ihcl.NewFile(file, text) | ||
|
||
// TODO: block until it's available <-pf.ParserLoadingDone() | ||
// requires https://github.com/hashicorp/terraform-ls/issues/8 | ||
// TODO: replace crude wait/timeout loop | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 🤔 I first thought this is "dirty", but then I realized that it may represent just the right balance between waiting for too long or erroring prematurely. So with that in mind I don't think it's crude, I actually think that functionally this is a sensible and pragmatic solution we could replicate in other handlers too eventually. The only things I'd be careful about is (a) the timeout - 3 seconds feels quite long to me, I'd reduce it down to 2 seconds max (b) how this positions us towards/against complying with LSP. As far as I understand this waiting mechanism greatly increases the chance of us providing outdated data, and so that may put more pressure on resolving #8 otherwise we're diverging from the LSP, which we shouldn't be. This is why I leaned more towards erroring than waiting in the completion handler. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I can adjust this to max There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
I see, I wasn't aware of that. In that case I assume it's actually desirable to even wait longer before responding? i.e. we can and should tolerate longer timeout. I was just applying caution because I saw some clients setting hard timeout for responses for some methods - e.g. formatting in particular is expected to be done within a second or two in some clients's implementations, otherwise the request is just cancelled. I assume the caution is not applicable to all methods then. So feel free to keep it at 3 seconds. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Regardless though we really need to resolve #8 |
||
var isParserLoaded bool | ||
var elapsed time.Duration | ||
sleepFor := 10 * time.Millisecond | ||
maxWait := 3 * time.Second | ||
for !isParserLoaded { | ||
time.Sleep(sleepFor) | ||
elapsed += sleepFor | ||
if elapsed >= maxWait { | ||
return symbols, fmt.Errorf("parser is not available yet for %s", file.Dir()) | ||
} | ||
var err error | ||
isParserLoaded, err = pf.IsParserLoaded(file.Dir()) | ||
if err != nil { | ||
return symbols, err | ||
} | ||
} | ||
|
||
p, err := pf.ParserForDir(file.Dir()) | ||
if err != nil { | ||
return symbols, fmt.Errorf("finding compatible parser failed: %w", err) | ||
} | ||
|
||
blocks, err := p.Blocks(hclFile) | ||
if err != nil { | ||
return symbols, err | ||
} | ||
|
||
for _, block := range blocks { | ||
symbols = append(symbols, lsp.SymbolInformation{ | ||
Name: symbolName(block), | ||
Kind: lsp.SKStruct, // We don't have a great SymbolKind match for blocks | ||
Location: lsp.Location{ | ||
Range: ilsp.HCLRangeToLSP(block.Range()), | ||
URI: params.TextDocument.URI, | ||
}, | ||
}) | ||
} | ||
|
||
return symbols, nil | ||
|
||
} | ||
|
||
func symbolName(b lang.ConfigBlock) string { | ||
name := b.BlockType() | ||
for _, l := range b.Labels() { | ||
name += "." + l.Value | ||
} | ||
return name | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Thank you for fixing this! 👍