All Downloads are FREE. Search and download functionalities are using the official Maven repository.

com.google.javascript.jscomp.LightweightMessageFormatter Maven / Gradle / Ivy

There is a newer version: 9.0.8
Show newest version
/*
 * Copyright 2007 The Closure Compiler Authors.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
package com.google.javascript.jscomp;

import static com.google.common.base.Preconditions.checkNotNull;
import static com.google.javascript.jscomp.SourceExcerptProvider.SourceExcerpt.LINE;

import com.google.common.base.Strings;
import com.google.debugging.sourcemap.proto.Mapping.OriginalMapping;
import com.google.javascript.jscomp.SourceExcerptProvider.ExcerptFormatter;
import com.google.javascript.jscomp.SourceExcerptProvider.SourceExcerpt;
import com.google.javascript.rhino.TokenUtil;

/**
 * Lightweight message formatter. The format of messages this formatter
 * produces is very compact and to the point.
 *
 */
public final class LightweightMessageFormatter extends AbstractMessageFormatter {
  private final SourceExcerpt excerpt;
  private static final ExcerptFormatter excerptFormatter =
      new LineNumberingFormatter();
  private boolean includeLocation = true;
  private boolean includeLevel = true;

  /**
   * A constructor for when the client doesn't care about source information.
   */
  private LightweightMessageFormatter() {
    super(null);
    this.excerpt = LINE;
  }

  public LightweightMessageFormatter(SourceExcerptProvider source) {
    this(source, LINE);
  }

  public LightweightMessageFormatter(SourceExcerptProvider source,
      SourceExcerpt excerpt) {
    super(source);
    checkNotNull(source);
    this.excerpt = excerpt;
  }

  public static LightweightMessageFormatter withoutSource() {
    return new LightweightMessageFormatter();
  }

  public LightweightMessageFormatter setIncludeLocation(boolean includeLocation) {
    this.includeLocation = includeLocation;
    return this;
  }

  public LightweightMessageFormatter setIncludeLevel(boolean includeLevel) {
    this.includeLevel = includeLevel;
    return this;
  }

  @Override
  public String formatError(JSError error) {
    return format(error, false);
  }

  @Override
  public String formatWarning(JSError warning) {
    return format(warning, true);
  }

  private String format(JSError error, boolean warning) {
    SourceExcerptProvider source = getSource();
    String sourceName = error.sourceName;
    int lineNumber = error.lineNumber;
    int charno = error.getCharno();

    // Format the non-reverse-mapped position.
    StringBuilder b = new StringBuilder();
    StringBuilder boldLine = new StringBuilder();
    String nonMappedPosition = formatPosition(sourceName, lineNumber);

    // Check if we can reverse-map the source.
    if (includeLocation) {
      OriginalMapping mapping = source == null ? null : source.getSourceMapping(
          error.sourceName, error.lineNumber, error.getCharno());
      if (mapping == null) {
        boldLine.append(nonMappedPosition);
      } else {
        sourceName = mapping.getOriginalFile();
        lineNumber = mapping.getLineNumber();
        charno = mapping.getColumnPosition();

        b.append(nonMappedPosition);
        b.append("\nOriginally at:\n");
        boldLine.append(formatPosition(sourceName, lineNumber));
      }
    }

    if (includeLevel) {
      boldLine.append(getLevelName(warning ? CheckLevel.WARNING : CheckLevel.ERROR));
      boldLine.append(" - ");
    }

    boldLine.append(error.description);

    b.append(maybeEmbolden(boldLine.toString()));
    b.append('\n');

    String sourceExcerptWithPositionIndicator =
        getExcerptWithPosition(error, sourceName, lineNumber, charno);
    if (sourceExcerptWithPositionIndicator != null) {
      b.append(sourceExcerptWithPositionIndicator);
    }
    return b.toString();
  }

  String getExcerptWithPosition(JSError error) {
    return getExcerptWithPosition(error, error.sourceName, error.lineNumber, error.getCharno());
  }

  String getExcerptWithPosition(JSError error, String sourceName, int lineNumber, int charno) {
    StringBuilder b = new StringBuilder();

    SourceExcerptProvider source = getSource();
    String sourceExcerpt =
        source == null ? null : excerpt.get(source, sourceName, lineNumber, excerptFormatter);

    if (sourceExcerpt != null) {
      b.append(sourceExcerpt);
      b.append('\n');

      // padding equal to the excerpt and arrow at the end
      // charno == sourceExcerpt.length() means something is missing
      // at the end of the line
      if (excerpt.equals(LINE) && 0 <= charno && charno <= sourceExcerpt.length()) {
        for (int i = 0; i < charno; i++) {
          char c = sourceExcerpt.charAt(i);
          if (TokenUtil.isWhitespace(c)) {
            b.append(c);
          } else {
            b.append(' ');
          }
        }
        if (error.node == null) {
          b.append("^");
        } else {
          int length =
              Math.max(1, Math.min(error.node.getLength(), sourceExcerpt.length() - charno));
          for (int i = 0; i < length; i++) {
            b.append("^");
          }
        }
        b.append("\n");
      }
    }
    return b.toString();
  }

  private static String formatPosition(String sourceName, int lineNumber) {
    StringBuilder b = new StringBuilder();
    if (sourceName != null) {
      b.append(sourceName);
      if (lineNumber > 0) {
        b.append(':');
        b.append(lineNumber);
      }
      b.append(": ");
    }
    return b.toString();
  }

  /**
   * Formats a region by appending line numbers in front, e.g.
   *
   * 
   *    9| if (foo) {
   *   10|   alert('bar');
   *   11| }
   * 
* * and return line excerpt without any modification. */ static class LineNumberingFormatter implements ExcerptFormatter { @Override public String formatLine(String line, int lineNumber) { return line; } @Override public String formatRegion(Region region) { if (region == null) { return null; } String code = region.getSourceExcerpt(); if (code.isEmpty()) { return null; } // max length of the number display int numberLength = Integer.toString(region.getEndingLineNumber()) .length(); // formatting StringBuilder builder = new StringBuilder(code.length() * 2); int start = 0; int end = code.indexOf('\n', start); int lineNumber = region.getBeginningLineNumber(); while (start >= 0) { // line extraction String line; if (end < 0) { line = code.substring(start); if (line.isEmpty()) { return builder.substring(0, builder.length() - 1); } } else { line = code.substring(start, end); } builder.append(" "); // nice spaces for the line number int spaces = numberLength - Integer.toString(lineNumber).length(); builder.append(Strings.repeat(" ", spaces)); builder.append(lineNumber); builder.append("| "); // end & update if (end < 0) { builder.append(line); start = -1; } else { builder.append(line); builder.append('\n'); start = end + 1; end = code.indexOf('\n', start); lineNumber++; } } return builder.toString(); } } }




© 2015 - 2024 Weber Informatics LLC | Privacy Policy