1 //===--- SourceCode.h - Manipulating source code as strings -----*- C++ -*-===//
2 //
3 //                     The LLVM Compiler Infrastructure
4 //
5 // This file is distributed under the University of Illinois Open Source
6 // License. See LICENSE.TXT for details.
7 //
8 //===----------------------------------------------------------------------===//
9 #include "SourceCode.h"
10 
11 #include "clang/Basic/SourceManager.h"
12 #include "llvm/Support/Errc.h"
13 #include "llvm/Support/Error.h"
14 
15 namespace clang {
16 namespace clangd {
17 using namespace llvm;
18 
19 llvm::Expected<size_t> positionToOffset(StringRef Code, Position P,
20                                         bool AllowColumnsBeyondLineLength) {
21   if (P.line < 0)
22     return llvm::make_error<llvm::StringError>(
23         llvm::formatv("Line value can't be negative ({0})", P.line),
24         llvm::errc::invalid_argument);
25   if (P.character < 0)
26     return llvm::make_error<llvm::StringError>(
27         llvm::formatv("Character value can't be negative ({0})", P.character),
28         llvm::errc::invalid_argument);
29   size_t StartOfLine = 0;
30   for (int I = 0; I != P.line; ++I) {
31     size_t NextNL = Code.find('\n', StartOfLine);
32     if (NextNL == StringRef::npos)
33       return llvm::make_error<llvm::StringError>(
34           llvm::formatv("Line value is out of range ({0})", P.line),
35           llvm::errc::invalid_argument);
36     StartOfLine = NextNL + 1;
37   }
38 
39   size_t NextNL = Code.find('\n', StartOfLine);
40   if (NextNL == StringRef::npos)
41     NextNL = Code.size();
42 
43   if (StartOfLine + P.character > NextNL && !AllowColumnsBeyondLineLength)
44     return llvm::make_error<llvm::StringError>(
45         llvm::formatv("Character value is out of range ({0})", P.character),
46         llvm::errc::invalid_argument);
47   // FIXME: officially P.character counts UTF-16 code units, not UTF-8 bytes!
48   return std::min(NextNL, StartOfLine + P.character);
49 }
50 
51 Position offsetToPosition(StringRef Code, size_t Offset) {
52   Offset = std::min(Code.size(), Offset);
53   StringRef Before = Code.substr(0, Offset);
54   int Lines = Before.count('\n');
55   size_t PrevNL = Before.rfind('\n');
56   size_t StartOfLine = (PrevNL == StringRef::npos) ? 0 : (PrevNL + 1);
57   // FIXME: officially character counts UTF-16 code units, not UTF-8 bytes!
58   Position Pos;
59   Pos.line = Lines;
60   Pos.character = static_cast<int>(Offset - StartOfLine);
61   return Pos;
62 }
63 
64 Position sourceLocToPosition(const SourceManager &SM, SourceLocation Loc) {
65   Position P;
66   P.line = static_cast<int>(SM.getSpellingLineNumber(Loc)) - 1;
67   P.character = static_cast<int>(SM.getSpellingColumnNumber(Loc)) - 1;
68   return P;
69 }
70 
71 Range halfOpenToRange(const SourceManager &SM, CharSourceRange R) {
72   // Clang is 1-based, LSP uses 0-based indexes.
73   Position Begin = sourceLocToPosition(SM, R.getBegin());
74   Position End = sourceLocToPosition(SM, R.getEnd());
75 
76   return {Begin, End};
77 }
78 
79 } // namespace clangd
80 } // namespace clang
81