formatTokens.js 2.1 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374
  1. import LinesAndColumns from "lines-and-columns";
  2. import {formatTokenType} from "../parser/tokenizer/types";
  3. export default function formatTokens(code, tokens) {
  4. if (tokens.length === 0) {
  5. return "";
  6. }
  7. const tokenKeys = Object.keys(tokens[0]).filter(
  8. (k) => k !== "type" && k !== "value" && k !== "start" && k !== "end" && k !== "loc",
  9. );
  10. const typeKeys = Object.keys(tokens[0].type).filter((k) => k !== "label" && k !== "keyword");
  11. const headings = ["Location", "Label", "Raw", ...tokenKeys, ...typeKeys];
  12. const lines = new LinesAndColumns(code);
  13. const rows = [headings, ...tokens.map(getTokenComponents)];
  14. const padding = headings.map(() => 0);
  15. for (const components of rows) {
  16. for (let i = 0; i < components.length; i++) {
  17. padding[i] = Math.max(padding[i], components[i].length);
  18. }
  19. }
  20. return rows
  21. .map((components) => components.map((component, i) => component.padEnd(padding[i])).join(" "))
  22. .join("\n");
  23. function getTokenComponents(token) {
  24. const raw = code.slice(token.start, token.end);
  25. return [
  26. formatRange(token.start, token.end),
  27. formatTokenType(token.type),
  28. truncate(String(raw), 14),
  29. // @ts-ignore: Intentional dynamic access by key.
  30. ...tokenKeys.map((key) => formatValue(token[key], key)),
  31. // @ts-ignore: Intentional dynamic access by key.
  32. ...typeKeys.map((key) => formatValue(token.type[key], key)),
  33. ];
  34. }
  35. // eslint-disable-next-line @typescript-eslint/no-explicit-any
  36. function formatValue(value, key) {
  37. if (value === true) {
  38. return key;
  39. } else if (value === false || value === null) {
  40. return "";
  41. } else {
  42. return String(value);
  43. }
  44. }
  45. function formatRange(start, end) {
  46. return `${formatPos(start)}-${formatPos(end)}`;
  47. }
  48. function formatPos(pos) {
  49. const location = lines.locationForIndex(pos);
  50. if (!location) {
  51. return "Unknown";
  52. } else {
  53. return `${location.line + 1}:${location.column + 1}`;
  54. }
  55. }
  56. }
  57. function truncate(s, length) {
  58. if (s.length > length) {
  59. return `${s.slice(0, length - 3)}...`;
  60. } else {
  61. return s;
  62. }
  63. }