Skip to content

Commit 2056575

Browse files
authored
Conditionally enable tokenizing (#372)
* Enable 'vhdl_ls off' and 'vhdl_ls on' to conditionally enable / disable parsing * Add documentation about ignoring errors in the Readme.
1 parent 1b864e6 commit 2056575

File tree

2 files changed

+167
-2
lines changed

2 files changed

+167
-2
lines changed

README.md

Lines changed: 26 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -183,12 +183,37 @@ Using the `lint` table, you can configure the severity of diagnostics or turn of
183183
> [!WARNING]
184184
> You can overwrite every diagnostic error code including syntax or analysis errors using the lint table.
185185
> However, the intended use-case is for lints only.
186-
> Overwriting syntax or analysis errors (e.g., error codes `unused` or `syntax`) can cause unwanted side effects
186+
> Overwriting syntax or analysis errors (e.g., error codes `mismatched_kinds` or `syntax`) can cause unwanted side
187+
> effects
187188
188189
Paths in the `vhdl_ls.toml` can contain glob patterns (i.e., `.../*/`).
189190
On Unix machines, they can contain environment variables using the `$NAME` or `${NAME}` syntax.
190191
On Windows machines, use the `%NAME%` syntax to substitute environment variables.
191192

193+
## Ignoring errors
194+
195+
You can use the comment-pair `-- vhdl_ls off` and `-- vhdl_ls on` to conditionally disable and re-enable parsing of
196+
source code. This can be helpful to ignore errors from correct code that vhdl_ls does not yet support, i.e., PSL
197+
statements or certain VHDL-2019 constructs.
198+
199+
```vhdl
200+
library ieee;
201+
use ieee.std_logic_1164.all;
202+
203+
entity ent is
204+
port (
205+
clk : in std_logic
206+
);
207+
end entity;
208+
209+
architecture arch of ent is
210+
begin
211+
-- vhdl_ls off
212+
default clock is rising_edge(clk);
213+
-- vhdl_ls on
214+
end architecture;
215+
```
216+
192217
## As an LSP-client developer how should I integrate VHDL-LS?
193218

194219
I recommend that the `lsp-client` polls GitHub and downloads

vhdl_lang/src/syntax/tokens/tokenizer.rs

Lines changed: 141 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -758,7 +758,18 @@ pub struct Comment {
758758
pub multi_line: bool,
759759
}
760760

761+
impl Comment {
762+
pub(crate) fn is_start_of_ignored_region(&self) -> bool {
763+
self.value.trim() == "vhdl_ls off"
764+
}
765+
766+
pub(crate) fn is_end_of_ignored_region(&self) -> bool {
767+
self.value.trim() == "vhdl_ls on"
768+
}
769+
}
770+
761771
use crate::standard::VHDLStandard;
772+
use itertools::Itertools;
762773
use std::convert::AsRef;
763774
use std::fmt::{Debug, Display, Formatter};
764775
use std::ops::{Add, AddAssign, Sub};
@@ -889,6 +900,45 @@ impl Token {
889900
pub fn equal_format(&self, other: &Token) -> bool {
890901
self.kind == other.kind && self.value == other.value
891902
}
903+
904+
pub(crate) fn leading_is_start_of_ignored_region(&self) -> bool {
905+
let Some(comments) = &self.comments else {
906+
return false;
907+
};
908+
if let Some((index, _)) = comments
909+
.leading
910+
.iter()
911+
.find_position(|comment| comment.is_start_of_ignored_region())
912+
{
913+
// This construct guards against the case where we have a `vhdl_ls on` in the same
914+
// comment block after a a `vhdl_ls off`
915+
!comments.leading[index..]
916+
.iter()
917+
.any(Comment::is_end_of_ignored_region)
918+
} else {
919+
false
920+
}
921+
}
922+
923+
pub(crate) fn leading_is_end_of_ignored_region(&self) -> bool {
924+
let Some(comments) = &self.comments else {
925+
return false;
926+
};
927+
comments
928+
.leading
929+
.iter()
930+
.any(Comment::is_end_of_ignored_region)
931+
}
932+
933+
pub(crate) fn trailing_is_end_of_ignored_region(&self) -> bool {
934+
let Some(comments) = &self.comments else {
935+
return false;
936+
};
937+
comments
938+
.trailing
939+
.as_ref()
940+
.is_some_and(Comment::is_end_of_ignored_region)
941+
}
892942
}
893943

894944
impl Operator {
@@ -1973,7 +2023,38 @@ impl<'a> Tokenizer<'a> {
19732023

19742024
pub fn pop(&mut self) -> DiagnosticResult<Option<Token>> {
19752025
match self.pop_raw() {
1976-
Ok(token) => Ok(token),
2026+
Ok(None) => Ok(None),
2027+
Ok(Some(token)) => {
2028+
if token.leading_is_start_of_ignored_region() {
2029+
if !token.trailing_is_end_of_ignored_region() {
2030+
loop {
2031+
match self.pop_raw() {
2032+
Ok(None) => {
2033+
// Note: we should probably emit an unterminated error here
2034+
// instead of silently failing.
2035+
return Ok(None);
2036+
}
2037+
Ok(Some(tok)) => {
2038+
if tok.trailing_is_end_of_ignored_region() {
2039+
break;
2040+
} else if tok.leading_is_end_of_ignored_region() {
2041+
// Note: to be pedantic, the 'vhdl_ls on' should be
2042+
// removed from the comments. However, because we have
2043+
// no public API and the comments don't really play
2044+
// a crucial role in the language server or binary,
2045+
// we just emit the token for simplicity.
2046+
return Ok(Some(tok));
2047+
}
2048+
}
2049+
Err(_) => {}
2050+
}
2051+
}
2052+
}
2053+
self.pop()
2054+
} else {
2055+
Ok(Some(token))
2056+
}
2057+
}
19772058
Err(err) => {
19782059
self.state.start = self.reader.state();
19792060
Err(Diagnostic::syntax_error(
@@ -3106,4 +3187,63 @@ entity -- €
31063187
vec![View, Default]
31073188
);
31083189
}
3190+
3191+
#[test]
3192+
fn ignore_code_in_between_explicitly_ignored_regions() {
3193+
let tokens = kinds_tokenize(
3194+
"\
3195+
entity foo is
3196+
--vhdl_ls off
3197+
I am ignored cod€
3198+
--vhdl_ls on
3199+
end foo;
3200+
",
3201+
);
3202+
assert_eq!(
3203+
&tokens,
3204+
&[Entity, Identifier, Is, End, Identifier, SemiColon]
3205+
);
3206+
3207+
let tokens = kinds_tokenize(
3208+
"\
3209+
before Is
3210+
--vhdl_ls off
3211+
single_token
3212+
--vhdl_ls on
3213+
End after
3214+
",
3215+
);
3216+
assert_eq!(&tokens, &[Identifier, Is, End, After]);
3217+
}
3218+
3219+
#[test]
3220+
fn ignore_code_without_on() {
3221+
// This should potentially emit a warning, but for now we just silently
3222+
// ignore that there should be a `vhdl_ls on` at some point.
3223+
let tokens = kinds_tokenize(
3224+
"\
3225+
before Is
3226+
--vhdl_ls off
3227+
single_token
3228+
End after
3229+
",
3230+
);
3231+
assert_eq!(&tokens, &[Identifier, Is]);
3232+
}
3233+
3234+
#[test]
3235+
fn on_directly_after_off() {
3236+
// This should potentially emit a warning, but for now we just silently
3237+
// ignore that there should be a `vhdl_ls on` at some point.
3238+
let tokens = kinds_tokenize(
3239+
"\
3240+
before Is
3241+
--vhdl_ls off
3242+
--vhdl_ls on
3243+
single_token
3244+
End after
3245+
",
3246+
);
3247+
assert_eq!(&tokens, &[Identifier, Is, Identifier, End, After]);
3248+
}
31093249
}

0 commit comments

Comments
 (0)