TranslatePosition translates a single position between Dingo and Go files. LSP uses character offsets (0-indexed), where each character (including tabs) counts as 1. This is NOT visual columns - tabs are 1 character, not 4 visual columns.
( uri protocol.DocumentURI, pos protocol.Position, dir Direction, )
| 50 | // LSP uses character offsets (0-indexed), where each character (including tabs) counts as 1. |
| 51 | // This is NOT visual columns - tabs are 1 character, not 4 visual columns. |
| 52 | func (t *Translator) TranslatePosition( |
| 53 | uri protocol.DocumentURI, |
| 54 | pos protocol.Position, |
| 55 | dir Direction, |
| 56 | ) (protocol.DocumentURI, protocol.Position, error) { |
| 57 | dirName := "DingoToGo" |
| 58 | if dir == GoToDingo { |
| 59 | dirName = "GoToDingo" |
| 60 | } |
| 61 | log.Printf("[LSP Translator] TranslatePosition START: direction=%s, uri=%s, line=%d, char=%d", |
| 62 | dirName, uri.Filename(), pos.Line, pos.Character) |
| 63 | |
| 64 | // Convert LSP position (0-based) to 1-based for dmap |
| 65 | line := int(pos.Line) + 1 |
| 66 | col := int(pos.Character) + 1 // LSP character offset is 0-indexed, dmap uses 1-indexed |
| 67 | |
| 68 | // Determine file paths using config-aware path calculation |
| 69 | var goPath string |
| 70 | if dir == DingoToGo { |
| 71 | goPath = dingoToGoPathWithConfig(uri.Filename(), t.dingoConfig) |
| 72 | } else { |
| 73 | goPath = uri.Filename() |
| 74 | } |
| 75 | |
| 76 | // Load source map reader |
| 77 | reader, err := t.cache.Get(goPath) |
| 78 | if err != nil { |
| 79 | // CRITICAL FIX C6: Still translate URI even with 1:1 positions |
| 80 | // Bug was: returning .dingo URI to gopls when source map missing |
| 81 | if dir == DingoToGo { |
| 82 | // Must return .go URI, not .dingo URI |
| 83 | goURI := lspuri.File(goPath) |
| 84 | return goURI, pos, fmt.Errorf("source map not found: %s (file not transpiled)", goPath) |
| 85 | } |
| 86 | // CRITICAL FIX: For Go->Dingo without map, return location unchanged |
| 87 | // This handles standard library and external package definitions |
| 88 | // No source map = not a transpiled file, so return the .go location as-is |
| 89 | log.Printf("[LSP Translator] No source map for %s, returning location unchanged (likely stdlib/external package)", goPath) |
| 90 | return uri, pos, nil |
| 91 | } |
| 92 | |
| 93 | // Translate position using binary .dmap reader |
| 94 | var newLine, newCol int |
| 95 | var newURI protocol.DocumentURI |
| 96 | |
| 97 | if dir == DingoToGo { |
| 98 | // Use DingoLineToGoLine which handles both: |
| 99 | // - Transformed lines (returns GoLineStart where actual code is) |
| 100 | // - Untransformed lines (uses line shift calculation) |
| 101 | newLine = reader.DingoLineToGoLine(line) |
| 102 | |
| 103 | // Apply column translation for transformed lines (error propagation, etc.) |
| 104 | // This adjusts for LHS changes: "userID := func()?" -> "tmp, err := func()" |
| 105 | var colFound bool |
| 106 | newCol, colFound = reader.TranslateDingoColumn(line, col) |
| 107 | if colFound { |
| 108 | log.Printf("[LSP Translator] DingoToGo: column translated %d -> %d", col, newCol) |
| 109 | } else { |