From 470b86e1bc85ecf08e613e8c5217f9b34c420991 Mon Sep 17 00:00:00 2001 From: Remy Suen Date: Tue, 29 Jul 2025 16:24:46 -0400 Subject: [PATCH 1/3] Fix file path calculations for \\wsl$ hosts Signed-off-by: Remy Suen --- CHANGELOG.md | 2 ++ internal/bake/hcl/documentLink_test.go | 6 +++--- internal/compose/completion.go | 11 ++++++----- internal/compose/documentLink_test.go | 6 +++--- internal/types/common.go | 2 +- 5 files changed, 15 insertions(+), 12 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 90a0f2d..530bd9a 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -7,6 +7,8 @@ All notable changes to the Docker Language Server will be documented in this fil ### Fixed - Compose + - textDocument/completion + - fix build stage lookups for files in a folder under the `\\wsl$` host ([#369](https://github.com/docker/docker-language-server/issues/369)) - textDocument/documentLink - improve handling of malformed image attribute values with registry prefixes ([#369](https://github.com/docker/docker-language-server/issues/369)) - convert links properly if a WSL URI with a dollar sign is used ([#366](https://github.com/docker/docker-language-server/issues/366)) diff --git a/internal/bake/hcl/documentLink_test.go b/internal/bake/hcl/documentLink_test.go index de204b6..e56f3ca 100644 --- a/internal/bake/hcl/documentLink_test.go +++ b/internal/bake/hcl/documentLink_test.go @@ -113,7 +113,7 @@ func TestDocumentLink_WSL(t *testing.T) { name: "Dockerfile", content: "target \"api\" {\n dockerfile = \"Dockerfile\"\n}", target: "file://wsl%24/docker-desktop/tmp/Dockerfile", - tooltip: "\\\\wsl%24\\docker-desktop\\tmp\\Dockerfile", + tooltip: "\\\\wsl$\\docker-desktop\\tmp\\Dockerfile", linkRange: protocol.Range{ Start: protocol.Position{Line: 1, Character: 16}, End: protocol.Position{Line: 1, Character: 26}, @@ -123,7 +123,7 @@ func TestDocumentLink_WSL(t *testing.T) { name: "./Dockerfile", content: "target \"api\" {\n dockerfile = \"./Dockerfile\"\n}", target: "file://wsl%24/docker-desktop/tmp/Dockerfile", - tooltip: "\\\\wsl%24\\docker-desktop\\tmp\\Dockerfile", + tooltip: "\\\\wsl$\\docker-desktop\\tmp\\Dockerfile", linkRange: protocol.Range{ Start: protocol.Position{Line: 1, Character: 16}, End: protocol.Position{Line: 1, Character: 28}, @@ -133,7 +133,7 @@ func TestDocumentLink_WSL(t *testing.T) { name: "../other/Dockerfile", content: "target \"api\" {\n dockerfile = \"../other/Dockerfile\"\n}", target: "file://wsl%24/docker-desktop/other/Dockerfile", - tooltip: "\\\\wsl%24\\docker-desktop\\other\\Dockerfile", + tooltip: "\\\\wsl$\\docker-desktop\\other\\Dockerfile", linkRange: protocol.Range{ Start: protocol.Position{Line: 1, Character: 16}, End: protocol.Position{Line: 1, Character: 35}, diff --git a/internal/compose/completion.go b/internal/compose/completion.go index b9c2114..55215d2 100644 --- a/internal/compose/completion.go +++ b/internal/compose/completion.go @@ -36,8 +36,9 @@ func extendingCurrentFile(documentPath document.DocumentPath, extendsNode *ast.M if extends, ok := extendsNode.Value.(*ast.MappingNode); ok { for _, extendsAttribute := range extends.Values { if extendsAttribute.Key.GetToken().Value == "file" { - path := filepath.Join(documentPath.Folder, extendsAttribute.Value.GetToken().Value) - if !samePath(filepath.Join(documentPath.Folder, documentPath.FileName), path) { + _, path := types.Concatenate(documentPath.Folder, extendsAttribute.Value.GetToken().Value, documentPath.WSLDollarSignHost) + _, originalPath := types.Concatenate(documentPath.Folder, documentPath.FileName, documentPath.WSLDollarSignHost) + if !samePath(originalPath, path) { return false } break @@ -53,7 +54,7 @@ var buildTargetModifier = textEditModifier{ }, modify: func(file *ast.File, manager *document.Manager, documentPath document.DocumentPath, edit protocol.TextEdit, attributeName, spacing string, path []*ast.MappingValueNode) protocol.TextEdit { if _, ok := path[2].Value.(*ast.NullNode); ok { - dockerfilePath := filepath.Join(documentPath.Folder, "Dockerfile") + _, dockerfilePath := types.Concatenate(documentPath.Folder, "Dockerfile", documentPath.WSLDollarSignHost) stages := findBuildStages(manager, dockerfilePath, "") if len(stages) > 0 { edit.NewText = fmt.Sprintf("%v%v", edit.NewText, createChoiceSnippetText(stages)) @@ -70,7 +71,7 @@ var buildTargetModifier = textEditModifier{ } } - dockerfilePath := filepath.Join(documentPath.Folder, dockerfileAttributePath) + _, dockerfilePath := types.Concatenate(documentPath.Folder, dockerfileAttributePath, documentPath.WSLDollarSignHost) stages := findBuildStages(manager, dockerfilePath, "") if len(stages) > 0 { edit.NewText = fmt.Sprintf("%v%v", edit.NewText, createChoiceSnippetText(stages)) @@ -434,7 +435,7 @@ func buildTargetCompletionItems(params *protocol.CompletionParams, manager *docu } } - dockerfilePath := filepath.Join(documentPath.Folder, dockerfileAttributePath) + _, dockerfilePath := types.Concatenate(documentPath.Folder, dockerfileAttributePath, documentPath.WSLDollarSignHost) if _, ok := path[3].Value.(*ast.NullNode); ok { return createBuildStageItems(params, manager, dockerfilePath, "", prefixLength), true } else if prefix, ok := path[3].Value.(*ast.StringNode); ok { diff --git a/internal/compose/documentLink_test.go b/internal/compose/documentLink_test.go index 07b348c..807340f 100644 --- a/internal/compose/documentLink_test.go +++ b/internal/compose/documentLink_test.go @@ -46,7 +46,7 @@ func TestDocumentLink_WSL(t *testing.T) { End: protocol.Position{Line: 1, Character: 13}, }, Target: types.CreateStringPointer("file://wsl%24/docker-desktop/tmp/file.yaml"), - Tooltip: types.CreateStringPointer("\\\\wsl%24\\docker-desktop\\tmp\\file.yaml"), + Tooltip: types.CreateStringPointer("\\\\wsl$\\docker-desktop\\tmp\\file.yaml"), }, { Range: protocol.Range{ @@ -54,7 +54,7 @@ func TestDocumentLink_WSL(t *testing.T) { End: protocol.Position{Line: 2, Character: 16}, }, Target: types.CreateStringPointer("file://wsl%24/docker-desktop/tmp/file2.yaml"), - Tooltip: types.CreateStringPointer("\\\\wsl%24\\docker-desktop\\tmp\\file2.yaml"), + Tooltip: types.CreateStringPointer("\\\\wsl$\\docker-desktop\\tmp\\file2.yaml"), }, { Range: protocol.Range{ @@ -62,7 +62,7 @@ func TestDocumentLink_WSL(t *testing.T) { End: protocol.Position{Line: 3, Character: 23}, }, Target: types.CreateStringPointer("file://wsl%24/docker-desktop/other/file3.yaml"), - Tooltip: types.CreateStringPointer("\\\\wsl%24\\docker-desktop\\other\\file3.yaml"), + Tooltip: types.CreateStringPointer("\\\\wsl$\\docker-desktop\\other\\file3.yaml"), }, }, }, diff --git a/internal/types/common.go b/internal/types/common.go index d680642..4c081a8 100644 --- a/internal/types/common.go +++ b/internal/types/common.go @@ -100,7 +100,7 @@ func AbsoluteFolder(documentURL *url.URL) (string, error) { func Concatenate(folder, file string, wslDollarSign bool) (uri string, absoluteFilePath string) { if wslDollarSign { - return "file://wsl%24" + path.Join(strings.ReplaceAll(folder, "\\", "/"), file), "\\\\wsl%24" + strings.ReplaceAll(path.Join(folder, file), "/", "\\") + return "file://wsl%24" + path.Join(strings.ReplaceAll(folder, "\\", "/"), file), "\\\\wsl$" + strings.ReplaceAll(path.Join(folder, file), "/", "\\") } abs := filepath.ToSlash(filepath.Join(folder, file)) return fmt.Sprintf("file:///%v", strings.TrimPrefix(abs, "/")), filepath.FromSlash(abs) From ed3c08a57970b15a300e3b39677549221ff49430 Mon Sep 17 00:00:00 2001 From: Remy Suen Date: Tue, 29 Jul 2025 16:25:44 -0400 Subject: [PATCH 2/3] Fix the GitHub issue link so it points to the right one Signed-off-by: Remy Suen --- CHANGELOG.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 530bd9a..d329752 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -8,7 +8,7 @@ All notable changes to the Docker Language Server will be documented in this fil - Compose - textDocument/completion - - fix build stage lookups for files in a folder under the `\\wsl$` host ([#369](https://github.com/docker/docker-language-server/issues/369)) + - fix build stage lookups for files in a folder under the `\\wsl$` host ([#382](https://github.com/docker/docker-language-server/issues/382)) - textDocument/documentLink - improve handling of malformed image attribute values with registry prefixes ([#369](https://github.com/docker/docker-language-server/issues/369)) - convert links properly if a WSL URI with a dollar sign is used ([#366](https://github.com/docker/docker-language-server/issues/366)) From f019171498ab3e64300747f5fe7e48a815a966b9 Mon Sep 17 00:00:00 2001 From: Remy Suen Date: Tue, 29 Jul 2025 17:08:58 -0400 Subject: [PATCH 3/3] Change OpenDockerfile to accept a URI As the wsl$ URIs can complicate things, instead of generating a URI from the provided path, the OpenDockerfile function should instead accept a URI so that the caller has the option to pass in the appropriate URI to use. Signed-off-by: Remy Suen --- internal/bake/hcl/completion.go | 2 +- internal/bake/hcl/definition.go | 4 +- internal/bake/hcl/diagnosticsCollector.go | 6 +- internal/bake/hcl/inlayHint.go | 2 +- internal/bake/hcl/inlineCompletion.go | 2 +- internal/compose/completion.go | 22 ++-- internal/compose/completion_test.go | 122 ++++++++++++++++++++++ internal/pkg/document/manager.go | 7 +- 8 files changed, 146 insertions(+), 21 deletions(-) diff --git a/internal/bake/hcl/completion.go b/internal/bake/hcl/completion.go index c555f61..065ad2d 100644 --- a/internal/bake/hcl/completion.go +++ b/internal/bake/hcl/completion.go @@ -73,7 +73,7 @@ func Completion(ctx context.Context, params *protocol.CompletionParams, manager continue } - _, nodes := document.OpenDockerfile(ctx, manager, dockerfilePath) + _, nodes := document.OpenDockerfile(ctx, manager, "", dockerfilePath) if nodes != nil { if attribute, ok := attributes["target"]; ok && isInsideRange(attribute.Expr.Range(), params.Position) { if _, ok := attributes["dockerfile-inline"]; ok { diff --git a/internal/bake/hcl/definition.go b/internal/bake/hcl/definition.go index 0dcb120..c20a538 100644 --- a/internal/bake/hcl/definition.go +++ b/internal/bake/hcl/definition.go @@ -122,7 +122,7 @@ func ResolveExpression(ctx context.Context, definitionLinkSupport bool, manager value, _ := literalValueExpr.Value(&hcl.EvalContext{}) target := value.AsString() - bytes, nodes := document.OpenDockerfile(ctx, manager, dockerfilePath) + bytes, nodes := document.OpenDockerfile(ctx, manager, "", dockerfilePath) lines := strings.Split(string(bytes), "\n") for _, child := range nodes { if strings.EqualFold(child.Value, "FROM") { @@ -188,7 +188,7 @@ func ResolveExpression(ctx context.Context, definitionLinkSupport bool, manager end-- } arg := string(doc.Input()[start:end]) - bytes, nodes := document.OpenDockerfile(ctx, manager, dockerfilePath) + bytes, nodes := document.OpenDockerfile(ctx, manager, "", dockerfilePath) lines := strings.Split(string(bytes), "\n") for _, child := range nodes { if strings.EqualFold(child.Value, "ARG") { diff --git a/internal/bake/hcl/diagnosticsCollector.go b/internal/bake/hcl/diagnosticsCollector.go index f64cd74..f26e7fb 100644 --- a/internal/bake/hcl/diagnosticsCollector.go +++ b/internal/bake/hcl/diagnosticsCollector.go @@ -231,7 +231,7 @@ func (c *BakeHCLDiagnosticsCollector) CollectDiagnostics(source, workspaceFolder } nodes, ok := dockerfileContent[dockerfile] if !ok { - _, nodes = document.OpenDockerfile(context.Background(), c.docs, dockerfilePath) + _, nodes = document.OpenDockerfile(context.Background(), c.docs, "", dockerfilePath) dockerfileContent[block.Labels[0]] = nodes } diagnostic := c.checkTargetTarget(nodes, expr, literalValueExpr, source) @@ -255,7 +255,7 @@ func (c *BakeHCLDiagnosticsCollector) CollectDiagnostics(source, workspaceFolder } nodes, ok := dockerfileContent[dockerfile] if !ok { - _, nodes = document.OpenDockerfile(context.Background(), c.docs, dockerfile) + _, nodes = document.OpenDockerfile(context.Background(), c.docs, "", dockerfile) dockerfileContent[dockerfile] = nodes } c.collectARGs(nodes, args) @@ -265,7 +265,7 @@ func (c *BakeHCLDiagnosticsCollector) CollectDiagnostics(source, workspaceFolder nodes, ok := dockerfileContent[dockerfilePath] if !ok { - _, nodes = document.OpenDockerfile(context.Background(), c.docs, dockerfilePath) + _, nodes = document.OpenDockerfile(context.Background(), c.docs, "", dockerfilePath) dockerfileContent[dockerfilePath] = nodes } argsDiagnostics := c.checkTargetArgs(nodes, input, expr, source, args) diff --git a/internal/bake/hcl/inlayHint.go b/internal/bake/hcl/inlayHint.go index bd99de2..519b036 100644 --- a/internal/bake/hcl/inlayHint.go +++ b/internal/bake/hcl/inlayHint.go @@ -27,7 +27,7 @@ func InlayHint(docs *document.Manager, doc document.BakeHCLDocument, rng protoco if expr, ok := attribute.Expr.(*hclsyntax.ObjectConsExpr); ok && len(expr.Items) > 0 { dockerfilePath, err := doc.DockerfileForTarget(block) if dockerfilePath != "" && err == nil { - _, nodes := document.OpenDockerfile(context.Background(), docs, dockerfilePath) + _, nodes := document.OpenDockerfile(context.Background(), docs, "", dockerfilePath) args := map[string]string{} for _, child := range nodes { if strings.EqualFold(child.Value, "ARG") { diff --git a/internal/bake/hcl/inlineCompletion.go b/internal/bake/hcl/inlineCompletion.go index 79f9dfe..309e9c6 100644 --- a/internal/bake/hcl/inlineCompletion.go +++ b/internal/bake/hcl/inlineCompletion.go @@ -90,7 +90,7 @@ func InlineCompletion(ctx context.Context, params *protocol.InlineCompletionPara argNames := []string{} args := map[string]string{} targets := []string{} - _, nodes := document.OpenDockerfile(ctx, manager, dockerfilePath) + _, nodes := document.OpenDockerfile(ctx, manager, "", dockerfilePath) before := true for _, child := range nodes { if strings.EqualFold(child.Value, "ARG") && before { diff --git a/internal/compose/completion.go b/internal/compose/completion.go index 55215d2..b03471e 100644 --- a/internal/compose/completion.go +++ b/internal/compose/completion.go @@ -54,8 +54,8 @@ var buildTargetModifier = textEditModifier{ }, modify: func(file *ast.File, manager *document.Manager, documentPath document.DocumentPath, edit protocol.TextEdit, attributeName, spacing string, path []*ast.MappingValueNode) protocol.TextEdit { if _, ok := path[2].Value.(*ast.NullNode); ok { - _, dockerfilePath := types.Concatenate(documentPath.Folder, "Dockerfile", documentPath.WSLDollarSignHost) - stages := findBuildStages(manager, dockerfilePath, "") + dockerfileURI, dockerfilePath := types.Concatenate(documentPath.Folder, "Dockerfile", documentPath.WSLDollarSignHost) + stages := findBuildStages(manager, dockerfileURI, dockerfilePath, "") if len(stages) > 0 { edit.NewText = fmt.Sprintf("%v%v", edit.NewText, createChoiceSnippetText(stages)) return edit @@ -71,8 +71,8 @@ var buildTargetModifier = textEditModifier{ } } - _, dockerfilePath := types.Concatenate(documentPath.Folder, dockerfileAttributePath, documentPath.WSLDollarSignHost) - stages := findBuildStages(manager, dockerfilePath, "") + dockerfileURI, dockerfilePath := types.Concatenate(documentPath.Folder, dockerfileAttributePath, documentPath.WSLDollarSignHost) + stages := findBuildStages(manager, dockerfileURI, dockerfilePath, "") if len(stages) > 0 { edit.NewText = fmt.Sprintf("%v%v", edit.NewText, createChoiceSnippetText(stages)) return edit @@ -402,8 +402,8 @@ func findDependencies(file *ast.File, dependencyType string) []string { return services } -func findBuildStages(manager *document.Manager, dockerfilePath, prefix string) []completionItemText { - _, nodes := document.OpenDockerfile(context.Background(), manager, dockerfilePath) +func findBuildStages(manager *document.Manager, dockerfileURI, dockerfilePath, prefix string) []completionItemText { + _, nodes := document.OpenDockerfile(context.Background(), manager, dockerfileURI, dockerfilePath) items := []completionItemText{} for _, child := range nodes { if strings.EqualFold(child.Value, "FROM") { @@ -435,16 +435,16 @@ func buildTargetCompletionItems(params *protocol.CompletionParams, manager *docu } } - _, dockerfilePath := types.Concatenate(documentPath.Folder, dockerfileAttributePath, documentPath.WSLDollarSignHost) + dockerfileURI, dockerfilePath := types.Concatenate(documentPath.Folder, dockerfileAttributePath, documentPath.WSLDollarSignHost) if _, ok := path[3].Value.(*ast.NullNode); ok { - return createBuildStageItems(params, manager, dockerfilePath, "", prefixLength), true + return createBuildStageItems(params, manager, dockerfileURI, dockerfilePath, "", prefixLength), true } else if prefix, ok := path[3].Value.(*ast.StringNode); ok { if int(params.Position.Line) == path[3].Value.GetToken().Position.Line-1 { offset := int(params.Position.Character) - path[3].Value.GetToken().Position.Column + 1 // offset can be greater than the length if there's just empty whitespace after the string value, // must be non-negative, if negative it suggests the cursor is in the whitespace before the attribute's value if offset >= 0 && offset <= len(prefix.Value) { - return createBuildStageItems(params, manager, dockerfilePath, prefix.Value[0:offset], prefixLength), true + return createBuildStageItems(params, manager, dockerfileURI, dockerfilePath, prefix.Value[0:offset], prefixLength), true } } } @@ -453,9 +453,9 @@ func buildTargetCompletionItems(params *protocol.CompletionParams, manager *docu return nil, false } -func createBuildStageItems(params *protocol.CompletionParams, manager *document.Manager, dockerfilePath, prefix string, prefixLength protocol.UInteger) []protocol.CompletionItem { +func createBuildStageItems(params *protocol.CompletionParams, manager *document.Manager, dockerfileURI, dockerfilePath, prefix string, prefixLength protocol.UInteger) []protocol.CompletionItem { items := []protocol.CompletionItem{} - for _, itemText := range findBuildStages(manager, dockerfilePath, prefix) { + for _, itemText := range findBuildStages(manager, dockerfileURI, dockerfilePath, prefix) { items = append(items, protocol.CompletionItem{ Label: itemText.label, Documentation: itemText.documentation, diff --git a/internal/compose/completion_test.go b/internal/compose/completion_test.go index ce5b30d..c0854e4 100644 --- a/internal/compose/completion_test.go +++ b/internal/compose/completion_test.go @@ -4397,6 +4397,128 @@ services: } } +func TestCompletion_BuildStageLookups_WSL(t *testing.T) { + testCases := []struct { + name string + dockerfileContent string + content string + line uint32 + character uint32 + list func() *protocol.CompletionList + }{ + { + name: "target attribute is null", + dockerfileContent: "FROM scratch AS base", + content: ` +services: + postgres: + build: + target: `, + line: 4, + character: 14, + list: func() *protocol.CompletionList { + return &protocol.CompletionList{ + Items: []protocol.CompletionItem{ + { + Label: "base", + Documentation: "scratch", + TextEdit: textEdit("base", 4, 14, 0), + }, + }, + } + }, + }, + { + name: "target attribute has a valid prefix", + dockerfileContent: "FROM scratch AS base", + content: ` +services: + postgres: + build: + target: b`, + line: 4, + character: 15, + list: func() *protocol.CompletionList { + return &protocol.CompletionList{ + Items: []protocol.CompletionItem{ + { + Label: "base", + Documentation: "scratch", + TextEdit: textEdit("base", 4, 15, 1), + }, + }, + } + }, + }, + { + name: "build completion items include autofilled stages when build is empty", + dockerfileContent: "FROM busybox as bstage\nFROM alpine as astage", + content: ` +services: + postgres: + build: + `, + line: 4, + character: 6, + list: func() *protocol.CompletionList { + items := serviceBuildProperties(4, 6, 0) + for i := range items { + if items[i].Label == "target" { + items[i].TextEdit = textEdit("target: ${1|bstage,astage|}", 4, 6, 0) + break + } + } + return &protocol.CompletionList{Items: items} + }, + }, + { + name: "build completion items include autofilled stages when build is empty when build object has other attributes", + dockerfileContent: "FROM busybox as bstage\nFROM alpine as astage", + content: ` +services: + postgres: + build: + dockerfile: Dockerfile + `, + line: 5, + character: 6, + list: func() *protocol.CompletionList { + items := serviceBuildProperties(5, 6, 0) + for i := range items { + if items[i].Label == "target" { + items[i].TextEdit = textEdit("target: ${1|bstage,astage|}", 5, 6, 0) + break + } + } + return &protocol.CompletionList{ + Items: items, + } + }, + }, + } + + dockerfileURI := "file://wsl%24/docker-desktop/tmp/Dockerfile" + composeFileURI := "file://wsl%24/docker-desktop/tmp/compose.yaml" + + for _, tc := range testCases { + t.Run(tc.name, func(t *testing.T) { + manager := document.NewDocumentManager() + changed, err := manager.Write(context.Background(), uri.URI(dockerfileURI), protocol.DockerfileLanguage, 1, []byte(tc.dockerfileContent)) + require.NoError(t, err) + require.True(t, changed) + doc := document.NewComposeDocument(manager, uri.URI(composeFileURI), 1, []byte(tc.content)) + list, err := Completion(context.Background(), &protocol.CompletionParams{ + TextDocumentPositionParams: protocol.TextDocumentPositionParams{ + TextDocument: protocol.TextDocumentIdentifier{URI: composeFileURI}, + Position: protocol.Position{Line: tc.line, Character: tc.character}, + }, + }, manager, doc) + require.NoError(t, err) + require.Equal(t, tc.list(), list) + }) + } +} + func TestCompletion_CustomServiceProvider(t *testing.T) { testCases := []struct { name string diff --git a/internal/pkg/document/manager.go b/internal/pkg/document/manager.go index b9da8ec..a27dd20 100644 --- a/internal/pkg/document/manager.go +++ b/internal/pkg/document/manager.go @@ -44,8 +44,11 @@ func parseDockerfile(dockerfilePath string) ([]byte, *parser.Result, error) { return dockerfileBytes, result, err } -func OpenDockerfile(ctx context.Context, manager *Manager, path string) ([]byte, []*parser.Node) { - doc := manager.Get(ctx, uri.URI(fmt.Sprintf("file:///%v", strings.TrimPrefix(filepath.ToSlash(path), "/")))) +func OpenDockerfile(ctx context.Context, manager *Manager, documentURI, path string) ([]byte, []*parser.Node) { + if documentURI == "" { + documentURI = fmt.Sprintf("file:///%v", strings.TrimPrefix(filepath.ToSlash(path), "/")) + } + doc := manager.Get(ctx, uri.URI(documentURI)) if doc != nil { if dockerfile, ok := doc.(DockerfileDocument); ok { return dockerfile.Input(), dockerfile.Nodes()