@@ -162,6 +162,7 @@ public class TypeScriptASTConverter {
162162 private final JsonObject syntaxKinds ;
163163 private final Map <Integer , String > nodeFlagMap = new LinkedHashMap <>();
164164 private final Map <Integer , String > syntaxKindMap = new LinkedHashMap <>();
165+ private int [] lineStarts ;
165166
166167 private int syntaxKindExtends ;
167168
@@ -199,6 +200,8 @@ private void makeEnumIdMap(JsonObject enumObject, Map<Integer, String> idToName)
199200 * into a parser {@link Result}.
200201 */
201202 public Result convertAST (JsonObject ast , String source ) {
203+ this .lineStarts = toIntArray (ast .getAsJsonArray ("$lineStarts" ));
204+
202205 List <ParseError > errors = new ArrayList <ParseError >();
203206
204207 // process parse diagnostics (i.e., syntax errors) reported by the TypeScript compiler
@@ -207,11 +210,8 @@ public Result convertAST(JsonObject ast, String source) {
207210 for (JsonElement elt : parseDiagnostics ) {
208211 JsonObject parseDiagnostic = elt .getAsJsonObject ();
209212 String message = parseDiagnostic .get ("messageText" ).getAsString ();
210- JsonObject pos = parseDiagnostic .get ("$pos" ).getAsJsonObject ();
211- int line = pos .get ("line" ).getAsInt () + 1 ;
212- int column = pos .get ("character" ).getAsInt ();
213- int offset = pos .get ("$offset" ).getAsInt ();
214- errors .add (new ParseError (message , line , column , offset ));
213+ Position pos = getPosition (parseDiagnostic .get ("$pos" ));
214+ errors .add (new ParseError (message , pos .getLine (), pos .getColumn (), pos .getOffset ()));
215215 }
216216 return new Result (source , null , new ArrayList <>(), new ArrayList <>(), errors );
217217 }
@@ -231,14 +231,44 @@ public Result convertAST(JsonObject ast, String source) {
231231 return new Result (source , converted , tokens , comments , errors );
232232 }
233233
234+ /**
235+ * Converts a JSON array to an int array.
236+ * The array is assumed to only contain integers.
237+ */
238+ private static int [] toIntArray (JsonArray array ) {
239+ int [] result = new int [array .size ()];
240+ for (int i = 0 ; i < array .size (); ++i ) {
241+ result [i ] = array .get (i ).getAsInt ();
242+ }
243+ return result ;
244+ }
245+
246+ private int getLineFromPos (int pos ) {
247+ int low = 0 , high = this .lineStarts .length - 1 ;
248+ while (low < high ) {
249+ int mid = high - ((high - low ) >> 1 ); // Get middle, rounding up.
250+ int startOfLine = lineStarts [mid ];
251+ if (startOfLine <= pos ) {
252+ low = mid ;
253+ } else {
254+ high = mid - 1 ;
255+ }
256+ }
257+ return low ;
258+ }
259+
260+ private int getColumnFromLinePos (int line , int pos ) {
261+ return pos - lineStarts [line ];
262+ }
263+
234264 /**
235265 * Extract tokens and comments from the given TypeScript AST.
236266 */
237267 private void extractTokensAndComments (JsonObject ast , List <Token > tokens , List <Comment > comments ) {
238268 for (JsonElement elt : ast .get ("$tokens" ).getAsJsonArray ()) {
239269 JsonObject token = elt .getAsJsonObject ();
240270 String text = token .get ("text" ).getAsString ();
241- Position start = getPosition (token .get ("tokenPos" ). getAsJsonObject (), true );
271+ Position start = getPosition (token .get ("tokenPos" ));
242272 Position end = advance (start , text );
243273 SourceLocation loc = new SourceLocation (text , start , end );
244274 String kind = getKind (token );
@@ -886,7 +916,7 @@ private Node convertClass(JsonObject node, String kind, SourceLocation loc) thro
886916 } else {
887917 superInterfaces = convertSuperInterfaceClause (supers );
888918 }
889- afterHead = heritageClause .get ("$end" ).getAsJsonObject (). get ( "$offset" ). getAsInt ();
919+ afterHead = heritageClause .get ("$end" ).getAsInt ();
890920 }
891921 if (superInterfaces == null ) {
892922 superInterfaces = new ArrayList <>();
@@ -2191,8 +2221,8 @@ private Position advance(Position pos, String skipped) {
21912221 * Get the source location of the given AST node.
21922222 */
21932223 private SourceLocation getSourceLocation (JsonObject node ) {
2194- Position start = getPosition (node .get ("$pos" ). getAsJsonObject (), true );
2195- Position end = getPosition (node .get ("$end" ). getAsJsonObject (), false );
2224+ Position start = getPosition (node .get ("$pos" ));
2225+ Position end = getPosition (node .get ("$end" ));
21962226 int startOffset = start .getOffset ();
21972227 int endOffset = end .getOffset ();
21982228 if (startOffset > endOffset )
@@ -2207,15 +2237,11 @@ private SourceLocation getSourceLocation(JsonObject node) {
22072237 * For start positions, we need to skip over whitespace, which is included in
22082238 * the positions reported by the TypeScript compiler.
22092239 */
2210- private Position getPosition (JsonObject pos , boolean isStart ) {
2211- int line = pos .get ("line" ).getAsInt () + 1 ;
2212- int column = pos .get ("character" ).getAsInt ();
2213- int offset = pos .get ("$offset" ).getAsInt ();
2214- if (isStart ) {
2215- while (offset < source .length () && Character .isWhitespace (source .charAt (offset )))
2216- ++offset ;
2217- }
2218- return new Position (line , column , offset );
2240+ private Position getPosition (JsonElement elm ) {
2241+ int offset = elm .getAsInt ();
2242+ int line = getLineFromPos (offset );
2243+ int column = getColumnFromLinePos (line , offset );
2244+ return new Position (line + 1 , column , offset );
22192245 }
22202246
22212247 private Iterable <JsonElement > getModifiers (JsonObject node ) {
0 commit comments