From 26de9151eefeffa121d9f9ea7b3fe9ab459237db Mon Sep 17 00:00:00 2001 From: Nikola Hristov Date: Sat, 5 Jul 2025 23:01:53 +0300 Subject: [PATCH] Fixed the language server crashing upon a go to definition request on untracked file --- ls/ls.go | 42 +++++++++++++++++++++++++++++++++++------- ls/ls_clang_to_ide.go | 36 ++++++++++++++++++++++++++++++++---- ls/ls_ide_to_clang.go | 30 ++++++++++++++++++++++++++++++ 3 files changed, 97 insertions(+), 11 deletions(-) diff --git a/ls/ls.go b/ls/ls.go index b487f087..40b497e 100644 --- a/ls/ls.go +++ b/ls/ls.go @@ -83,6 +83,15 @@ type Config struct { Jobs int } +var extToFileType = map[string]string{ + ".h": "cpp", + ".hpp": "cpp", +} + +type translationOpts struct { + loadRelToIde bool +} + var yellow = color.New(color.FgHiYellow) func (ls *INOLanguageServer) writeLock(logger jsonrpc.FunctionLogger, requireClangd bool) { @@ -603,7 +612,8 @@ func (ls *INOLanguageServer) textDocumentDefinitionReqFromIDE(ctx context.Contex var ideLocations []lsp.Location if clangLocations != nil { - ideLocations, err = ls.clang2IdeLocationsArray(logger, clangLocations) + opts := translationOpts{loadRelToIde: true} + ideLocations, err = ls.clang2IdeLocationsArray2(logger, clangLocations, &opts) if err != nil { logger.Logf("Error: %v", err) ls.Close() @@ -989,15 +999,13 @@ func (ls *INOLanguageServer) exitNotifFromIDE(logger jsonrpc.FunctionLogger) { ls.Close() } -func (ls *INOLanguageServer) textDocumentDidOpenNotifFromIDE(logger jsonrpc.FunctionLogger, ideParam *lsp.DidOpenTextDocumentParams) { - ls.writeLock(logger, true) - defer ls.writeUnlock(logger) +func (ls *INOLanguageServer) implTextDocumentDidOpenNotifFromIDE(logger jsonrpc.FunctionLogger, ideParam lsp.TextDocumentItem) *jsonrpc.ResponseError { - ideTextDocItem := ideParam.TextDocument + ideTextDocItem := ideParam clangURI, _, err := ls.ide2ClangDocumentURI(logger, ideTextDocItem.URI) if err != nil { logger.Logf("Error: %s", err) - return + return &jsonrpc.ResponseError{Code: jsonrpc.ErrorCodesInternalError, Message: err.Error()} } if ls.ideURIIsPartOfTheSketch(ideTextDocItem.URI) { @@ -1017,7 +1025,7 @@ func (ls *INOLanguageServer) textDocumentDidOpenNotifFromIDE(logger jsonrpc.Func // Notify clangd that sketchCpp has been opened only once if ls.sketchTrackedFilesCount != 1 { logger.Logf("Clang already notified, do not notify it anymore") - return + return nil } } @@ -1045,7 +1053,17 @@ func (ls *INOLanguageServer) textDocumentDidOpenNotifFromIDE(logger jsonrpc.Func logger.Logf("Error sending notification to clangd server: %v", err) logger.Logf("Please restart the language server.") ls.Close() + return &jsonrpc.ResponseError{Code: jsonrpc.ErrorCodesInternalError, Message: err.Error()} + } + return nil + +} + +func (ls *INOLanguageServer) textDocumentDidOpenNotifFromIDE(logger jsonrpc.FunctionLogger, ideParam *lsp.DidOpenTextDocumentParams) { + ls.writeLock(logger, true) + defer ls.writeUnlock(logger) + ls.implTextDocumentDidOpenNotifFromIDE(logger, ideParam.TextDocument) } func (ls *INOLanguageServer) textDocumentDidChangeNotifFromIDE(logger jsonrpc.FunctionLogger, ideParams *lsp.DidChangeTextDocumentParams) { @@ -1619,6 +1637,16 @@ type UnknownURIError struct { URI lsp.DocumentURI } +// UnknownFileExtensionError when a file extension isn't recognized +// and a file with it has to be opened. +type UnknownFileExtensionError struct { + extension string +} + func (e *UnknownURIError) Error() string { return "Document is not available: " + e.URI.String() } + +func (e *UnknownFileExtensionError) Error() string { + return "Unknown file extension " + e.extension +} diff --git a/ls/ls_clang_to_ide.go b/ls/ls_clang_to_ide.go index 882d1b5..d8ef4ca 100644 --- a/ls/ls_clang_to_ide.go +++ b/ls/ls_clang_to_ide.go @@ -27,12 +27,16 @@ func (ls *INOLanguageServer) clangURIRefersToIno(clangURI lsp.DocumentURI) bool return clangURI.AsPath().EquivalentTo(ls.buildSketchCpp) } +func (ls *INOLanguageServer) clang2IdeRangeAndDocumentURI(logger jsonrpc.FunctionLogger, clangURI lsp.DocumentURI, clangRange lsp.Range) (lsp.DocumentURI, lsp.Range, bool, error) { + return ls.clang2IdeRangeAndDocumentURI2(logger, clangURI, clangRange, nil) +} + // Convert Range and DocumentURI from Clang to IDE. // Returns: // - The IDE DocumentURI and Range // - a boolean that is true if the clang range is in the preprocessed area of the sketch // - an error -func (ls *INOLanguageServer) clang2IdeRangeAndDocumentURI(logger jsonrpc.FunctionLogger, clangURI lsp.DocumentURI, clangRange lsp.Range) (lsp.DocumentURI, lsp.Range, bool, error) { +func (ls *INOLanguageServer) clang2IdeRangeAndDocumentURI2(logger jsonrpc.FunctionLogger, clangURI lsp.DocumentURI, clangRange lsp.Range, opts *translationOpts) (lsp.DocumentURI, lsp.Range, bool, error) { // Sketchbook/Sketch/Sketch.ino <-> build-path/sketch/Sketch.ino.cpp // Sketchbook/Sketch/AnotherTab.ino <-> build-path/sketch/Sketch.ino.cpp (different section from above) if ls.clangURIRefersToIno(clangURI) { @@ -80,7 +84,25 @@ func (ls *INOLanguageServer) clang2IdeRangeAndDocumentURI(logger jsonrpc.Functio return lsp.NilURI, lsp.NilRange, false, err } idePath := ls.sketchRoot.JoinPath(rel).String() - ideURI, err := ls.idePathToIdeURI(logger, idePath) + + var ideURI lsp.DocumentURI + if opts == nil || !opts.loadRelToIde { + ideURI, err = ls.idePathToIdeURI(logger, idePath) + } else { + doc, ok := ls.trackedIdeDocs[idePath] + if !ok { + var shouldOpen bool + doc, shouldOpen, err = makeTextDocumentItem(logger, idePath) + if err != nil { + logger.Logf("ERROR: could not open '%s': %s", idePath, err) + } + if shouldOpen { + ls.implTextDocumentDidOpenNotifFromIDE(logger, doc) + } + + } + ideURI = doc.URI + } if ideRange.End.Line > 0 { ideRange.End.Line-- } @@ -296,9 +318,12 @@ func (ls *INOLanguageServer) cland2IdeTextEdits(logger jsonrpc.FunctionLogger, c } func (ls *INOLanguageServer) clang2IdeLocationsArray(logger jsonrpc.FunctionLogger, clangLocations []lsp.Location) ([]lsp.Location, error) { + return ls.clang2IdeLocationsArray2(logger, clangLocations, nil) +} +func (ls *INOLanguageServer) clang2IdeLocationsArray2(logger jsonrpc.FunctionLogger, clangLocations []lsp.Location, opts *translationOpts) ([]lsp.Location, error) { ideLocations := []lsp.Location{} for _, clangLocation := range clangLocations { - ideLocation, inPreprocessed, err := ls.clang2IdeLocation(logger, clangLocation) + ideLocation, inPreprocessed, err := ls.clang2IdeLocation2(logger, clangLocation, opts) if err != nil { logger.Logf("ERROR converting location %s: %s", clangLocation, err) return nil, err @@ -313,7 +338,10 @@ func (ls *INOLanguageServer) clang2IdeLocationsArray(logger jsonrpc.FunctionLogg } func (ls *INOLanguageServer) clang2IdeLocation(logger jsonrpc.FunctionLogger, clangLocation lsp.Location) (lsp.Location, bool, error) { - ideURI, ideRange, inPreprocessed, err := ls.clang2IdeRangeAndDocumentURI(logger, clangLocation.URI, clangLocation.Range) + return ls.clang2IdeLocation2(logger, clangLocation, nil) +} +func (ls *INOLanguageServer) clang2IdeLocation2(logger jsonrpc.FunctionLogger, clangLocation lsp.Location, opts *translationOpts) (lsp.Location, bool, error) { + ideURI, ideRange, inPreprocessed, err := ls.clang2IdeRangeAndDocumentURI2(logger, clangLocation.URI, clangLocation.Range, opts) return lsp.Location{ URI: ideURI, Range: ideRange, diff --git a/ls/ls_ide_to_clang.go b/ls/ls_ide_to_clang.go index e6ccc11..73b8cf6 100644 --- a/ls/ls_ide_to_clang.go +++ b/ls/ls_ide_to_clang.go @@ -17,13 +17,43 @@ package ls import ( "fmt" + "os" + "path/filepath" "github.com/arduino/arduino-language-server/sourcemapper" "go.bug.st/lsp" "go.bug.st/lsp/jsonrpc" ) +func getFileType(ext string) string { + if fileType, ok := extToFileType[ext]; ok { + return fileType + } + return "unknown" +} func (ls *INOLanguageServer) idePathToIdeURI(logger jsonrpc.FunctionLogger, inoPath string) (lsp.DocumentURI, error) { + return ls.idePathToIdeURI2(logger, inoPath) +} + +func makeTextDocumentItem(logger jsonrpc.FunctionLogger, path string) (lsp.TextDocumentItem, bool, error) { + ext := filepath.Ext(path) + filetype := getFileType(path) + uri := lsp.NewDocumentURI(path) + if filetype == "unknown" { + return lsp.TextDocumentItem{URI: uri, LanguageID: filetype}, false, &UnknownFileExtensionError{ext} + } + languageID := filetype + version := 0 + + text, err := os.ReadFile(path) + if err != nil { + logger.Logf("Could not read file: %v", err) + return lsp.TextDocumentItem{}, false, err + } + return lsp.TextDocumentItem{URI: uri, LanguageID: languageID, Version: version, Text: string(text)}, true, nil + +} +func (ls *INOLanguageServer) idePathToIdeURI2(logger jsonrpc.FunctionLogger, inoPath string) (lsp.DocumentURI, error) { if inoPath == sourcemapper.NotIno.File { return sourcemapper.NotInoURI, nil } pFad - Phonifier reborn

Pfad - The Proxy pFad of © 2024 Garber Painting. All rights reserved.

Note: This service is not intended for secure transactions such as banking, social media, email, or purchasing. Use at your own risk. We assume no liability whatsoever for broken pages.


Alternative Proxies:

Alternative Proxy

pFad Proxy

pFad v3 Proxy

pFad v4 Proxy