summaryrefslogtreecommitdiff
path: root/src/Gemtext.php
blob: d382c45b8d71808b09105f05e64a05dd9f680f38 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
<?php

namespace GeminiFoundation;

class Gemtext
{
  protected string $input;
  protected array $output;

  protected string $preformattedMode = 'off';

  public function __construct(string $input)
  {
    $this->input = $input;
  }

  public function parse(): array
  {
    $input = fopen('php://temp', 'r+');
    fputs($input, $this->input);
    rewind($input);

    $output = [];
    while ($line = fgets($input)) {
      $output[] = $this->parseLine($line);
    }

    fclose($input);

    return $this->output = $output;
  }

  private function parseLine(string $input)
  {
    $line = [
      'raw' => $input,
    ];

    $input = ltrim($input);

    if (str_starts_with($input, '```')) {
      $this->preformattedMode = $this->preformattedMode === 'on' ? 'off' : 'on';

      $line['type'] = $this->preformattedMode === 'on' ? 'preformatted_on' : 'preformatted_off';
      $line['text'] = $line['alt'] = trim(substr($input, 3));
    }
    else if ($this->preformattedMode === 'on') {
      $line['type'] = 'preformatted';
      $line['text'] = $line['raw'];
    }

    else if (str_starts_with($input, '#')) {
      $firstWhitespacePosition = $this->findWhitespace($input);

      $line['type'] = 'heading';
      $line['size'] = substr_count(
        haystack: $input,
        needle: '#',
        length: min($firstWhitespacePosition, 3)
      );
      $line['text'] = trim(substr(
        string: $input,
        offset: min($firstWhitespacePosition, 3)
      ));
    }
    else if (str_starts_with($input, '*')) {
      $line['type'] = 'listitem';
      $line['text'] = trim(substr($input, 1));
    }
    else if (str_starts_with($input, '>')) {
      $line['type'] = 'quote';
      $line['text'] = trim(substr($input, 1));
    }
    else if (str_starts_with($input, '=>')) {
      $whitespacePosition = $this->findWhitespace($input, 3);

      $link = substr(string: $input, offset: 2, length: $whitespacePosition - 2);
      $text = substr(string: $input, offset: $whitespacePosition);

      $line['type'] = 'link';
      $line['link'] = trim($link);
      $line['text'] = trim($text);
    }
    else {
      $line['type'] = 'text';
      $line['text'] = rtrim($input);
    }

    return $line;
  }

  private function findWhitespace(string $input, int $offset = 0): int
  {
    $space = strpos($input, ' ', $offset);
    $tab = strpos($input, "\t", $offset);

    return $space === false ? ($tab === false ? PHP_INT_MAX : $tab) : $space;
  }
}