tools: improve `v check-md` by checking for broken TOC headline links (#10417)

pull/10454/head
Andreas Heissenberger 2021-06-14 12:12:02 +02:00 committed by GitHub
parent 90d04b0ce6
commit 7983495c57
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
5 changed files with 165 additions and 38 deletions

View File

@ -9,6 +9,7 @@ import rand
import term import term
import vhelp import vhelp
import v.pref import v.pref
import regex
const ( const (
too_long_line_length = 100 too_long_line_length = 100
@ -162,6 +163,7 @@ fn (mut f MDFile) progress(message string) {
fn (mut f MDFile) check() CheckResult { fn (mut f MDFile) check() CheckResult {
mut res := CheckResult{} mut res := CheckResult{}
mut anchor_data := AnchorData{}
for j, line in f.lines { for j, line in f.lines {
// f.progress('line: $j') // f.progress('line: $j')
if line.len > too_long_line_length { if line.len > too_long_line_length {
@ -187,8 +189,14 @@ fn (mut f MDFile) check() CheckResult {
res.errors++ res.errors++
} }
} }
if f.state == .markdown {
anchor_data.add_links(j, line)
anchor_data.add_link_targets(j, line)
}
f.parse_line(j, line) f.parse_line(j, line)
} }
anchor_data.check_link_target_match(f.path, mut res)
res += f.check_examples() res += f.check_examples()
return res return res
} }
@ -234,6 +242,121 @@ fn (mut f MDFile) parse_line(lnumber int, line string) {
} }
} }
struct Headline {
line int
lable string
level int
}
struct Anchor {
line int
}
type AnchorTarget = Anchor | Headline
struct AnchorLink {
line int
lable string
}
struct AnchorData {
mut:
links map[string][]AnchorLink
anchors map[string][]AnchorTarget
}
fn (mut ad AnchorData) add_links(line_number int, line string) {
query := r'\[(?P<lable>[^\]]+)\]\(\s*#(?P<link>[a-z\-]+)\)'
mut re := regex.regex_opt(query) or { panic(err) }
res := re.find_all_str(line)
for elem in res {
re.match_string(elem)
link := re.get_group_by_name(elem, 'link')
ad.links[link] << AnchorLink{
line: line_number
lable: re.get_group_by_name(elem, 'lable')
}
}
}
fn (mut ad AnchorData) add_link_targets(line_number int, line string) {
if line.trim_space().starts_with('#') {
if headline_start_pos := line.index(' ') {
headline := line.substr(headline_start_pos + 1, line.len)
link := create_ref_link(headline)
ad.anchors[link] << Headline{
line: line_number
lable: headline
level: headline_start_pos
}
}
} else {
query := r'<a\s*id=["\'](?P<link>[a-z\-]+)["\']\s*/>'
mut re := regex.regex_opt(query) or { panic(err) }
res := re.find_all_str(line)
for elem in res {
re.match_string(elem)
link := re.get_group_by_name(elem, 'link')
ad.anchors[link] << Anchor{
line: line_number
}
}
}
}
fn (mut ad AnchorData) check_link_target_match(fpath string, mut res CheckResult) {
mut checked_headlines := []string{}
mut found_error_warning := false
for link, linkdata in ad.links {
if link in ad.anchors {
checked_headlines << link
if ad.anchors[link].len > 1 {
found_error_warning = true
res.errors++
for anchordata in ad.anchors[link] {
eprintln(eline(fpath, anchordata.line, 0, 'multiple link targets of existing link (#$link)'))
}
}
} else {
found_error_warning = true
res.errors++
for brokenlink in linkdata {
eprintln(eline(fpath, brokenlink.line, 0, 'no link target found for existing link [$brokenlink.lable](#$link)'))
}
}
}
for link, anchor_lists in ad.anchors {
if !(link in checked_headlines) {
if anchor_lists.len > 1 {
for anchor in anchor_lists {
line := match anchor {
Headline {
anchor.line
}
Anchor {
anchor.line
}
}
wprintln(wline(fpath, line, 0, 'multiple link target for non existing link (#$link)'))
found_error_warning = true
res.warnings++
}
}
}
}
if found_error_warning {
eprintln('') // fix suppressed last error output
}
}
fn create_ref_link(s string) string {
query_remove := r'[^a-z \-]'
mut re := regex.regex_opt(query_remove) or { panic(err) }
return re.replace_simple(s.to_lower(), '').replace(' ', '-')
}
fn (mut f MDFile) debug() { fn (mut f MDFile) debug() {
for e in f.examples { for e in f.examples {
eprintln('f.path: $f.path | example: $e') eprintln('f.path: $f.path | example: $e')

View File

@ -83,7 +83,7 @@ For more details and troubleshooting, please visit the [vab GitHub repository](h
* [Structs](#structs) * [Structs](#structs)
* [Embedded structs](#embedded-structs) * [Embedded structs](#embedded-structs)
* [Default field values](#default-field-values) * [Default field values](#default-field-values)
* [Short struct literal syntax](#short-struct-initialization-syntax) * [Short struct literal syntax](#short-struct-literal-syntax)
* [Access modifiers](#access-modifiers) * [Access modifiers](#access-modifiers)
* [Methods](#methods) * [Methods](#methods)
* [Unions](#unions) * [Unions](#unions)
@ -2270,7 +2270,7 @@ You can also install modules already created by someone else with [VPM](https://
```powershell ```powershell
v install [module] v install [module]
``` ```
###### Example: **Example:**
```powershell ```powershell
v install ui v install ui
``` ```
@ -2280,7 +2280,7 @@ Removing a module with v:
```powershell ```powershell
v remove [module] v remove [module]
``` ```
###### Example: **Example:**
```powershell ```powershell
v remove ui v remove ui
``` ```
@ -2290,7 +2290,7 @@ Updating an installed module from [VPM](https://vpm.vlang.io/):
```powershell ```powershell
v update [module] v update [module]
``` ```
###### Example: **Example:**
```powershell ```powershell
v update ui v update ui
``` ```
@ -2305,7 +2305,7 @@ To see all the modules you have installed, you can use:
```powershell ```powershell
v list v list
``` ```
###### Example **Example:**
```powershell ```powershell
> v list > v list
Installed modules: Installed modules:
@ -2318,7 +2318,7 @@ outdated Show installed modules that need updates.
```powershell ```powershell
v outdated v outdated
``` ```
###### Example **Example:**
```powershell ```powershell
> v outdated > v outdated
Modules are up to date. Modules are up to date.
@ -4024,7 +4024,7 @@ created by the JS Backend (flag: `-b js`).
`$` is used as a prefix for compile-time operations. `$` is used as a prefix for compile-time operations.
#### $if #### `$if` condition
```v ```v
// Support for multiple conditions in one branch // Support for multiple conditions in one branch
$if ios || android { $if ios || android {
@ -4076,7 +4076,7 @@ Full list of builtin options:
| `gnu`, `hpux`, `haiku`, `qnx` | `cplusplus` | `big_endian` | | `gnu`, `hpux`, `haiku`, `qnx` | `cplusplus` | `big_endian` |
| `solaris` | | | | | `solaris` | | | |
#### $embed_file #### `$embed_file`
```v ignore ```v ignore
import os import os
@ -4099,7 +4099,7 @@ executable, increasing your binary size, but making it more self contained
and thus easier to distribute. In this case, `f.data()` will cause *no IO*, and thus easier to distribute. In this case, `f.data()` will cause *no IO*,
and it will always return the same data. and it will always return the same data.
#### $tmpl for embedding and parsing V template files #### `$tmpl` for embedding and parsing V template files
V has a simple template language for text and html templates, and they can easily V has a simple template language for text and html templates, and they can easily
be embedded via `$tmpl('path/to/template.txt')`: be embedded via `$tmpl('path/to/template.txt')`:
@ -4149,7 +4149,7 @@ numbers: [1, 2, 3]
#### $env #### `$env`
```v ```v
module main module main

View File

@ -74,37 +74,37 @@ different capabilities:
| structured data types | + | + | + | | | structured data types | + | + | + | |
### Strengths ### Strengths
#### default **default**
- very fast - very fast
- unlimited access from different coroutines - unlimited access from different coroutines
- easy to handle - easy to handle
#### `mut` **`mut`**
- very fast - very fast
- easy to handle - easy to handle
#### `shared` **`shared`**
- concurrent access from different coroutines - concurrent access from different coroutines
- data type may be complex structure - data type may be complex structure
- sophisticated access possible (several statements within one `lock` - sophisticated access possible (several statements within one `lock`
block) block)
#### `atomic` **`atomic`**
- concurrent access from different coroutines - concurrent access from different coroutines
- reasonably fast - reasonably fast
### Weaknesses ### Weaknesses
#### default **default**
- read only - read only
#### `mut` **`mut`**
- access only from one coroutine at a time - access only from one coroutine at a time
#### `shared` **`shared`**
- lock/unlock are slow - lock/unlock are slow
- moderately difficult to handle (needs `lock` block) - moderately difficult to handle (needs `lock` block)
#### `atomic` **`atomic`**
- limited to single (max. 64 bit) integers (and pointers) - limited to single (max. 64 bit) integers (and pointers)
- only a small set of predefined operations possible - only a small set of predefined operations possible
- very difficult to handle correctly - very difficult to handle correctly
@ -191,3 +191,5 @@ are sometimes surprising. Each statement should be seen as a single
transaction that is unrelated to the previous or following transaction that is unrelated to the previous or following
statement. Therefore - but also for performance reasons - it's often statement. Therefore - but also for performance reasons - it's often
better to group consecutive coherent statements in an explicit `lock` block. better to group consecutive coherent statements in an explicit `lock` block.
### Channels

View File

@ -23,7 +23,7 @@ provides V language support for Visual Studio Code.
[install V compiler](https://github.com/vlang/v/blob/master/doc/docs.md#install-from-source) [install V compiler](https://github.com/vlang/v/blob/master/doc/docs.md#install-from-source)
on your operating system. on your operating system.
### Setup ### Setup Extention
Install [V VS Code Extention](https://marketplace.visualstudio.com/items?itemName=vlanguage.vscode-vlang). Install [V VS Code Extention](https://marketplace.visualstudio.com/items?itemName=vlanguage.vscode-vlang).
@ -45,7 +45,7 @@ for Visual Studio Code provides visual conditional debugging.
[DWARF](https://en.wikipedia.org/wiki/DWARF) information to show and [DWARF](https://en.wikipedia.org/wiki/DWARF) information to show and
edit the variable. edit the variable.
### Setup ### Setup Debugging
1. Install the [C/C++ Extention](https://marketplace.visualstudio.com/items?itemName=ms-vscode.cpptools) 1. Install the [C/C++ Extention](https://marketplace.visualstudio.com/items?itemName=ms-vscode.cpptools)
2. Open `RUN AND DEBUG` panel (Debug Icon in left panel). 2. Open `RUN AND DEBUG` panel (Debug Icon in left panel).

View File

@ -1,9 +1,11 @@
This package is to generate data-driven HTML output. V allows for easily using text templates, expanded at compile time to
V functions, that efficiently produce text output. This is especially
usefull for templated HTML views, but the mechanism is general enough
to be used for other kinds of text output also.
# Directives # Template directives
Each directive begins with an `@` sign. Each template directive begins with an `@` sign.
Some directives begin contains a `{}` block, others only have `''` (string) parameters. Some directives contain a `{}` block, others only have `''` (string) parameters.
More on the directives itself.
Newlines on the beginning and end are ignored in `{}` blocks, Newlines on the beginning and end are ignored in `{}` blocks,
otherwise this (see [if](#if) for this syntax): otherwise this (see [if](#if) for this syntax):
@ -12,17 +14,17 @@ otherwise this (see [if](#if) for this syntax):
<span>This is shown if bool_val is true</span> <span>This is shown if bool_val is true</span>
} }
``` ```
would result in: ... would output:
```html ```html
<span>This is shown if bool_val is true</span> <span>This is shown if bool_val is true</span>
``` ```
which could result in unreadable output. ... which is less readable.
## if ## if
The if directive consists of three parts, the `@if` tag, the condition (same syntax like in V) The if directive, consists of three parts, the `@if` tag, the condition (same syntax like in V)
and the `{}` block where you can write html which will be rendered if the condition is true: and the `{}` block, where you can write html, which will be rendered if the condition is true:
``` ```
@if <condition> {} @if <condition> {}
``` ```
@ -42,19 +44,20 @@ The first example would result in:
```html ```html
<span>This is shown if bool_val is true</span> <span>This is shown if bool_val is true</span>
``` ```
while the one-liner results in: ... while the one-liner results in:
```html ```html
<span>This is shown if bool_val is true</span> <span>This is shown if bool_val is true</span>
``` ```
## for ## for
The for directive consists of three parts, the `@for` tag, the condition (same syntax like in V) The for directive consists of three parts, the `@for` tag,
and the `{}` block where you can write html which will be rendered for each loop: the condition (same syntax like in V) and the `{}` block,
where you can write text, rendered for each iteration of the loop:
``` ```
@for <condition> {} @for <condition> {}
``` ```
### Example ### Example for @for
```html ```html
@for i, val in my_vals { @for i, val in my_vals {
<span>$i - $val</span> <span>$i - $val</span>
@ -72,7 +75,7 @@ The first example would result in:
<span>2 - "Third"</span> <span>2 - "Third"</span>
... ...
``` ```
while the one-liner results in: ... while the one-liner results in:
```html ```html
<span>0 - "First"</span> <span>0 - "First"</span>
<span>1 - "Second"</span> <span>1 - "Second"</span>
@ -92,8 +95,7 @@ The include directive is for including other html files (which will be processed
and consists of two parts, the `@include` tag and a following `'<path>'` string. and consists of two parts, the `@include` tag and a following `'<path>'` string.
The path parameter is relative to the `/templates` directory in the corresponding project. The path parameter is relative to the `/templates` directory in the corresponding project.
### Example ### Example for the folder structure of a project using templates:
Files
``` ```
Project root Project root
/templates /templates
@ -117,11 +119,11 @@ where you can insert your src
@js '<url>' @js '<url>'
``` ```
### Example ### Example for the @js directive:
```html ```html
@js 'myscripts.js' @js 'myscripts.js'
``` ```
# Variables # Variables
All variables which are declared before can be used through the `@{my_var}` syntax. All variables, which are declared before the $tmpl can be used through the `@{my_var}` syntax.
It's also possible to use properties of structs here like `@{my_struct.prop}`. It's also possible to use properties of structs here like `@{my_struct.prop}`.