From 34361050fc09581e9dde8be67ba7da5991734e50 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Simon=20Andr=C3=A9?= Date: Sat, 19 Sep 2026 23:09:43 +0200 Subject: [PATCH] docs: update structure --- README.md | 46 ++++++----- docs/getting-started.md | 47 ++++++------ docs/index.md | 28 +++++-- docs/installation.md | 8 +- docs/model.md | 164 ++++++++++++++++++++++++++++++++++++++++ docs/public-api.md | 33 -------- 6 files changed, 240 insertions(+), 86 deletions(-) create mode 100644 docs/model.md delete mode 100644 docs/public-api.md diff --git a/README.md b/README.md index 78c0917..0c17642 100644 --- a/README.md +++ b/README.md @@ -1,4 +1,4 @@ -# ALTO CodeSnippet +# ALTO Code Snippet Represent immutable code snippets with source lines, selections, and presentation-neutral annotations. @@ -9,7 +9,7 @@ annotations.   ![License](https://img.shields.io/github/license/altophp/code-snippet?label=License&labelColor=050608&color=00B7FF)   [![GitHub Sponsors](https://img.shields.io/github/sponsors/smnandre?logo=githubsponsors&logoColor=00B7FF&label=%20Sponsor&labelColor=050608&color=00B7FF)](https://github.com/sponsors/smnandre) -CodeSnippet turns source code into a portable model with original line numbers, selected lines, +Code Snippet turns source code into a portable model with original line numbers, selected lines, and generic byte-range annotations. Renderers can consume that model without coupling this package to HTML, SVG, terminals, slides, or a syntax highlighter. @@ -24,20 +24,21 @@ echo $snippet->lines()[2]->number; // 26 ## Installation -Install ALTO CodeSnippet with Composer: +Code Snippet is currently distributed from its development branch. Add the +repository explicitly, then require `dev-main`: ```bash -composer require alto/code-snippet +composer config repositories.alto-code-snippet vcs https://github.com/altophp/code-snippet +composer require alto/code-snippet:dev-main ``` -CodeSnippet requires PHP 8.4 or later and `alto/language`. +Code Snippet requires PHP 8.4 or later and installs `alto/language`. -## Quick Start +## Quick start -Create a snippet, select a line, and attach an application-defined annotation: +Create a snippet, keep its original source position, and select one line: ```php -use Alto\Code\Snippet\CodeAnnotation; use Alto\Code\Snippet\CodeSnippet; $snippet = CodeSnippet::fromCode( @@ -45,16 +46,16 @@ $snippet = CodeSnippet::fromCode( 'php', sourceName: 'src/Runner.php', startLine: 24, -) - ->selectLines(3) - ->annotate(new CodeAnnotation( - offset: 0, - length: 6, - type: 'syntax', - data: ['scope' => 'keyword'], - )); - -echo json_encode($snippet, JSON_THROW_ON_ERROR); +)->selectLines(3); + +$line = $snippet->lines()[2]; +printf("line=%d selected=%s %s\n", $line->number, $line->selected ? 'true' : 'false', $line->code); +``` + +The result is: + +```text +line=26 selected=true execute(); ``` Every transformation returns a new value. The original snippet remains unchanged. @@ -126,10 +127,15 @@ $json = json_encode($snippet, JSON_THROW_ON_ERROR); ## Package boundary -CodeSnippet does not read files, detect languages, locate declarations, tokenize code, or render +Code Snippet does not read files, detect languages, locate declarations, tokenize code, or render output. It only owns the immutable, presentation-neutral data model passed between those steps. -See the [documentation](docs/index.md) for installation, a guided example, and the public API. +## Documentation + +- [Installation](docs/installation.md): install the development package and verify it. +- [Getting started](docs/getting-started.md): create a snippet and inspect a selected line. +- [Model](docs/model.md): work with snippets, lines, annotations, segments, and transformations. +- [Documentation index](docs/index.md): read the package overview and boundaries. ## Contributing diff --git a/docs/getting-started.md b/docs/getting-started.md index a9a7f31..013f17e 100644 --- a/docs/getting-started.md +++ b/docs/getting-started.md @@ -1,10 +1,15 @@ # Getting started -Create a snippet with its original source position, select lines, then attach generic byte-range -annotations: +After [installation](installation.md), save this as `snippet.php` beside +`vendor/`. It creates a snippet that starts on line 24 of its original file, +then selects its third line. The package does not read that file; the source +name is metadata. ```php -use Alto\Code\Snippet\CodeAnnotation; +selectLines(3) - ->annotate(new CodeAnnotation( - offset: 0, - length: 6, - type: 'syntax', - data: ['scope' => 'keyword'], - )); -``` - -`selectLines()` uses one-based positions inside the snippet. Every `CodeLine` also carries its -original source line number. +)->selectLines(3); -```php $line = $snippet->lines()[2]; +printf( + "index=%d number=%d selected=%s\n", + $line->index, + $line->number, + $line->selected ? 'true' : 'false', +); +echo $line->code, "\n"; +``` + +Run `php snippet.php`. The output is: -$line->index; // 3 -$line->number; // 26 -$line->selected; // true +```text +index=3 number=26 selected=true + execute(); ``` -Annotations on the complete snippet use byte offsets relative to `code()`. Line annotations are -clipped and shifted to line-relative offsets. `segments()` then exposes contiguous text regions -with stable annotation sets. +`selectLines()` uses one-based positions inside the snippet. Selection marks a +line; it does not remove the other lines or define a visual effect. Continue +with the [model](model.md) to annotate, slice, indent, and export snippets. diff --git a/docs/index.md b/docs/index.md index 33ec3f6..7255ffb 100644 --- a/docs/index.md +++ b/docs/index.md @@ -1,11 +1,23 @@ -# CodeSnippet +# Code Snippet -CodeSnippet represents code as immutable lines with source provenance, selected lines, and -presentation-neutral annotations. +Code Snippet stores code, original line numbers, selections, and annotations as +immutable values. Use it to pass an excerpt between extraction, analysis, and +presentation without tying the data to a renderer. -- [Installation](installation.md) -- [Getting started](getting-started.md) -- [Public API](public-api.md) +```php +use Alto\Code\Snippet\CodeSnippet; -The package owns the portable snippet model. It does not read files, locate declarations, tokenize -code, or render output. +$snippet = CodeSnippet::fromCode("first\nsecond\nthird", 'php', startLine: 24) + ->selectLines(3); + +echo $snippet->lines()[2]->number; // 26 +``` + +## Documentation + +- [Installation](installation.md): install the package and verify it can create a snippet. +- [Getting started](getting-started.md): create a snippet and inspect a selected line. +- [Model](model.md): work with snippets, lines, annotations, segments, and transformations. + +The package owns the portable snippet model. It does not read files, detect +languages, locate declarations, tokenize code, or render output. diff --git a/docs/installation.md b/docs/installation.md index 7b96f10..802f884 100644 --- a/docs/installation.md +++ b/docs/installation.md @@ -1,12 +1,14 @@ # Installation -Install CodeSnippet with Composer: +Code Snippet is currently distributed from its development branch. Add the +repository explicitly, then require `dev-main`: ```bash -composer require alto/code-snippet +composer config repositories.alto-code-snippet vcs https://github.com/altophp/code-snippet +composer require alto/code-snippet:dev-main ``` -CodeSnippet requires PHP 8.4 or later and `alto/language`. +Code Snippet requires PHP 8.4 or later and installs `alto/language`. Create a snippet from code and an optional language slug: diff --git a/docs/model.md b/docs/model.md new file mode 100644 index 0000000..2e8756f --- /dev/null +++ b/docs/model.md @@ -0,0 +1,164 @@ +# Model + +Code Snippet represents source text and presentation hints with four immutable +values. Transformations return a new value, so the original snippet remains +available to another consumer. + +| Value | Role | +| --- | --- | +| `CodeSnippet` | Complete code, provenance, selections, and annotations | +| `CodeLine` | One line with snippet and original source coordinates | +| `CodeAnnotation` | Typed metadata attached to a byte range | +| `CodeSegment` | Contiguous line text with one stable annotation set | + +All four values implement `JsonSerializable`. + +## Snippets + +Create a `CodeSnippet` from code. Supply a language when known, a source name +for provenance, and the original first line number when the excerpt came from a +larger file. + +```php +use Alto\Code\Snippet\CodeSnippet; + +$snippet = CodeSnippet::fromCode( + "one\ntwo\nthree", + 'php', + sourceName: 'src/Example.php', + startLine: 20, +)->selectLines(2); +``` + +The code is stored verbatim, including LF, CRLF, or CR line endings. Read it +with `code()`, `language()`, `sourceName()`, `startLine()`, `endLine()`, and +`lineCount()`. An empty snippet has zero lines. + +## Lines + +`lines()` returns `CodeLine` values with two coordinate systems: + +| Value | Coordinate system | Example | +| --- | --- | --- | +| `index` | One-based position inside the snippet | `2` | +| `number` | One-based position in the original source | `21` when `startLine` is `20` | +| `code` | Line content without its line break | `two` | +| `selected` | Presentation-neutral line marker | `true` | + +```php +$line = $snippet->lines()[1]; + +printf( + "index=%d source=%d selected=%s code=%s\n", + $line->index, + $line->number, + $line->selected ? 'true' : 'false', + $line->code, +); +``` + +This prints `index=2 source=21 selected=true code=two`. Selected line numbers +must exist inside the snippet. + +## Annotations + +A `CodeAnnotation` attaches an application-defined type and optional metadata +to a half-open byte range: the start offset is included and the end offset is +excluded. + +```php +use Alto\Code\Snippet\CodeAnnotation; + +$annotated = $snippet->annotate( + new CodeAnnotation(4, 3, 'focus'), + new CodeAnnotation(5, 5, 'warning', ['label' => 'Review']), +); +``` + +Snippet offsets are relative to `code()`. `CodeLine::annotations()` clips an +annotation to the line and shifts its offset to the beginning of that line. +Annotations may overlap or cross line breaks. + +```text +Snippet "abc\ndef": annotation [2, 5) + line 1 "abc": [2, 3) covers "c" + line 2 "def": [0, 1) covers "d" +``` + +Offsets use PHP byte semantics. For UTF-8, byte positions can differ from +character positions. Offsets must be non-negative, lengths positive, types +non-blank, and ranges contained in the snippet. + +## Segments + +`CodeLine::segments()` splits a line wherever its active annotations change. +Overlapping annotations remain active together; the consumer decides how to +combine their visual styles. + +```php +$overlap = CodeSnippet::fromCode('abcd')->annotate( + new CodeAnnotation(0, 3, 'focus'), + new CodeAnnotation(1, 2, 'warning'), +); + +foreach ($overlap->lines()[0]->segments() as $segment) { + $types = array_map( + static fn(CodeAnnotation $annotation): string => $annotation->type, + $segment->annotations, + ); + printf("%d %s [%s]\n", $segment->offset, $segment->text, implode(',', $types)); +} +``` + +The result is: + +```text +0 a [focus] +1 bc [focus,warning] +3 d [] +``` + +Segment offsets are line-relative. Unannotated text is still a segment; an +empty line has no segments. + +## Transformations + +`slice($start, $end)` projects a half-open byte range from an annotated +snippet. Crossing annotations are clipped and shifted. Original line numbers +and selections follow the remaining code. + +```php +$slice = $snippet->slice(4, 7); +$line = $slice->lines()[0]; +``` + +Here `$line->code` is `two`, its snippet index is `1`, its original source +number is `21`, and it remains selected. + +Use `dedent()` or its `unindent()` alias to remove common indentation. +`indent($spaces)` adds spaces to non-empty lines. Both operations preserve +source line numbers and move annotations with the surviving text. Annotations +inside removed indentation disappear, and annotations crossing it are clipped. + +## Export + +`toArray()` exports code, language, provenance, selections, lines, and +annotations. `json_encode()` produces the same portable model through +`JsonSerializable`. + +```php +$payload = $snippet->toArray(); +$json = json_encode($snippet, JSON_THROW_ON_ERROR); +``` + +The package does not provide a JSON import factory or a renderer. +[Code Slicer](https://altophp.com/code-slicer) can locate source declarations, +[Code Highlight](https://altophp.com/code-highlight) can tokenize code, and +[Code Kit](https://altophp.com/code-kit) can connect those stages to this model. + +## Invalid input + +Invalid source positions, selected lines, slice ranges, annotation ranges, +language identifiers, or indentation values raise `InvalidArgumentException`. +Use `lineCount()` and `strlen($snippet->code())` to check the corresponding +line and byte boundaries before applying user-provided coordinates. diff --git a/docs/public-api.md b/docs/public-api.md deleted file mode 100644 index 7606505..0000000 --- a/docs/public-api.md +++ /dev/null @@ -1,33 +0,0 @@ -# Public API - -## `CodeSnippet` - -Create a snippet with `CodeSnippet::fromCode()`. Read its code and metadata with `code()`, -`language()`, `sourceName()`, `startLine()`, `endLine()`, and `lineCount()`. - -- `selectLines()` marks one-based snippet lines. -- `annotate()` attaches generic byte-range annotations. -- `slice()` projects a half-open byte range and preserves matching metadata. -- `dedent()` and `unindent()` remove common indentation. -- `indent()` adds spaces to non-empty lines. -- `lines()` returns `CodeLine` values. -- `toArray()` and `jsonSerialize()` export the complete model. - -Source code and line endings remain byte-for-byte identical until an explicit indentation -transformation is applied. - -## `CodeLine` - -A line exposes its snippet-relative `index`, original source `number`, plain `code`, selection -state, line-relative annotations, and derived `segments()`. - -## `CodeAnnotation` - -An annotation contains a byte `offset`, positive `length`, application-defined `type`, and generic -`data`. Its end offset is available through `endOffset()`. - -## `CodeSegment` - -A segment contains contiguous text and the annotations active across that complete region. - -All four value objects implement `JsonSerializable`.