Skip to content

Commit

Permalink
Show better error location for missing CSS definition semicolon.
Browse files Browse the repository at this point in the history
Fixes #726
  • Loading branch information
gdotdesign committed Jan 27, 2025
1 parent 233ada4 commit 6f6275f
Show file tree
Hide file tree
Showing 5 changed files with 78 additions and 5 deletions.
18 changes: 18 additions & 0 deletions spec/errors/css_definition_expected_semicolon_2
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
component Main {
style root {
background: red
}
}
--------------------------------------------------------------------------------
░ ERROR (CSS_DEFINITION_EXPECTED_SEMICOLON) ░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░

I was expecting the semicolon of a CSS definition but I found "a space" instead:

┌ errors/css_definition_expected_semicolon_2:3:19
├────────────────────────────────────────────────
1│ component Main {
2│ style root {
3│ background: red
│ ⌃⌃⌃⌃
4│ }
5│ }
15 changes: 14 additions & 1 deletion src/errorable.cr
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,8 @@ module Mint
class Error < Exception
# Anything that can be a snippet.
alias SnippetTarget = TypeChecker::Checkable | SnippetData |
Ast::Node | Parser | String
Ast::Node | Parser | String |
Tuple(Parser, Parser::Location)

alias Element = Text | Bold | Code

Expand Down Expand Up @@ -80,6 +81,18 @@ module Mint
def snippet(value : SnippetTarget)
target =
case value
in Tuple(Parser, Parser::Location)
parser, position =
value

min =
parser.input[position.offset]? == '\0' ? 0 : 1

SnippetData.new(
to: position.offset + [min, parser.word(position).to_s.size].max,
filename: parser.file.relative_path,
input: parser.file.contents,
from: position.offset)
in Parser
min =
value.char == '\0' ? 0 : 1
Expand Down
38 changes: 38 additions & 0 deletions src/parser.cr
Original file line number Diff line number Diff line change
Expand Up @@ -181,6 +181,18 @@ module Mint
word
end

# Returns the word (non whitespace sequence) a the position.
def word(position : Location)
current = position.offset
word = ""

while (char = input[current]?) && !char.ascii_whitespace?
word += char
end

word
end

# Returns whether or not the word is at the current position.
def word?(word) : Bool
word.chars.each_with_index.all? do |char, i|
Expand Down Expand Up @@ -411,5 +423,31 @@ module Mint
end
end
end

# Returns the last non whitespace position.
def last_non_whitespace_position
position, line, column =
@position.to_tuple

while input[position - 1]?.try(&.ascii_whitespace?)
case previous_char
when '\n'
current = position - 2
column = 0_i64
line -= 1

while input[current]? && (input[current] != '\n')
current -= 1
column += 1
end
else
column -= 1
end

position -= 1
end

Location.new(offset: position, line: line, column: column)
end
end
end
9 changes: 6 additions & 3 deletions src/parsers/css_definition.cr
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ module Mint

value =
many(parse_whitespace: false) {
string_literal || interpolation || raw { char.in_set?("^;{\0\"") }
string_literal || interpolation || raw { char.in_set?("^;}{\0\"") }
}.map do |item|
if item.is_a?(Ast::StringLiteral) && (raw = static_value(item))
%("#{raw}")
Expand All @@ -19,8 +19,11 @@ module Mint
end

next error :css_definition_expected_semicolon do
expected "the semicolon of a CSS definition", word
snippet self
position =
last_non_whitespace_position

expected "the semicolon of a CSS definition", word(position)
snippet({self, position})
end unless char! ';'

Ast::CssDefinition.new(
Expand Down
3 changes: 2 additions & 1 deletion src/utils/terminal_snippet.cr
Original file line number Diff line number Diff line change
Expand Up @@ -165,7 +165,8 @@ module Mint
line.highlight(from, to)

"#{gutter} #{a}\n#{" " * gutter_width}#{b}"
elsif from == input.size && line.offset + line.contents.size == from
elsif (from == input.size && line.offset + line.contents.size == from) ||
(line.offset + line.contents.size == (to - 1))
"#{gutter} #{line.contents}\n#{" " * gutter_width}#{" " * line.contents.size}⌃⌃⌃⌃"
end || "#{gutter} #{line.contents}"
end
Expand Down

0 comments on commit 6f6275f

Please sign in to comment.