Fix color highlight for errors.

The output from the compiler changed and now mentions the line number
(non only the charters, which actually becomes columns). This patch
introduces the idea of a range of lines spanned by an error and
correctly marks the faulty region.

I think a better solution would be to introduce a "point" record of
the form {line:int;column:int}. However, such a change would
reverberate through all the source code and so it is much more
invasive. I can prepare such a patch if desired, after this one, which
fixes the regression.
This commit is contained in:
Christian Pellegrin 2020-01-05 21:21:53 +00:00
parent 1660e5989a
commit 243f7a1536
3 changed files with 73 additions and 11 deletions

View File

@ -103,6 +103,15 @@ let default_keywords = [
let keywords = ref (String_set.of_list default_keywords) let keywords = ref (String_set.of_list default_keywords)
let add_keyword kwd = keywords := String_set.add kwd !keywords let add_keyword kwd = keywords := String_set.add kwd !keywords
(* +-----------------------------------------------------------------+
| Span of Lines |
+-----------------------------------------------------------------+ *)
type lines = {
start: int;
stop: int;
}
(* +-----------------------------------------------------------------+ (* +-----------------------------------------------------------------+
| Error reporting | | Error reporting |
+-----------------------------------------------------------------+ *) +-----------------------------------------------------------------+ *)
@ -126,9 +135,22 @@ let get_ocaml_error_message exn =
Scanf.sscanf Scanf.sscanf
str str
"Characters %d-%d:\n%[\000-\255]" "Characters %d-%d:\n%[\000-\255]"
(fun start stop msg -> ((start, stop), msg)) (fun start stop msg -> ((start, stop), msg, None))
with _ -> with Scanf.Scan_failure(_) ->
((0, 0), str) try
Scanf.sscanf
str
"Line %d, characters %d-%d:\n%[\000-\255]"
(fun line start stop msg -> ((start, stop), msg, Some{start=line; stop=line}))
with Scanf.Scan_failure(_) ->
try
Scanf.sscanf
str
"Lines %d-%d, characters %d-%d:\n%[\000-\255]"
(fun start_line stop_line start stop msg -> ((start, stop),
msg, Some{start=start_line;stop=stop_line}))
with Scanf.Scan_failure(_) ->
((0, 0), str, None)
let collect_formatters buf pps f = let collect_formatters buf pps f =
(* First flush all formatters. *) (* First flush all formatters. *)
@ -365,10 +387,10 @@ let check_phrase phrase =
None None
with exn -> with exn ->
(* The phrase contains errors. *) (* The phrase contains errors. *)
let loc, msg = get_ocaml_error_message exn in let loc, msg, line = get_ocaml_error_message exn in
Toploop.toplevel_env := env; Toploop.toplevel_env := env;
Btype.backtrack snap; Btype.backtrack snap;
Some ([loc], msg) Some ([loc], msg, [line])
(* +-----------------------------------------------------------------+ (* +-----------------------------------------------------------------+
| Prompt | | Prompt |

View File

@ -254,6 +254,12 @@ type location = int * int
(** Type of a string-location. It is composed of a start and stop (** Type of a string-location. It is composed of a start and stop
offsets (in bytes). *) offsets (in bytes). *)
type lines = {
start: int;
stop: int;
}
(** Type for a range of lines in a buffer from start to stop. *)
(** Result of a function processing a programx. *) (** Result of a function processing a programx. *)
type 'a result = type 'a result =
| Value of 'a | Value of 'a
@ -310,12 +316,12 @@ val get_message : (Format.formatter -> 'a -> unit) -> 'a -> string
(** [get_message printer x] applies [printer] on [x] and (** [get_message printer x] applies [printer] on [x] and
returns everything it prints as a string. *) returns everything it prints as a string. *)
val get_ocaml_error_message : exn -> location * string val get_ocaml_error_message : exn -> location * string * (lines option)
(** [get_ocaml_error_message exn] returns the location and error (** [get_ocaml_error_message exn] returns the location and error
message for the exception [exn] which must be an exception from message for the exception [exn] which must be an exception from
the compiler. *) the compiler. *)
val check_phrase : Parsetree.toplevel_phrase -> (location list * string) option val check_phrase : Parsetree.toplevel_phrase -> (location list * string * lines option list) option
(** [check_phrase phrase] checks that [phrase] can be executed (** [check_phrase phrase] checks that [phrase] can be executed
without typing or compilation errors. It returns [None] if without typing or compilation errors. It returns [None] if
[phrase] is OK and an error message otherwise. [phrase] is OK and an error message otherwise.

View File

@ -14,6 +14,7 @@ open Lwt_react
open LTerm_dlist open LTerm_dlist
open LTerm_text open LTerm_text
open LTerm_geom open LTerm_geom
open UTop
open UTop_token open UTop_token
open UTop_styles open UTop_styles
open UTop_private open UTop_private
@ -77,7 +78,40 @@ let index_of_offset src ofs =
in in
aux 0 0 aux 0 0
let convert_locs str locs = List.map (fun (a, b) -> (index_of_offset str a, index_of_offset str b)) locs let convert_loc str (a, b) = (index_of_offset str a, index_of_offset str b)
let convert_locs str locs = List.map (fun (a, b) -> convert_loc str (a,b)) locs
let get_line src line =
let rec aux line' ofs skipped =
if ofs >= String.length src then
("", 0)
else if line' = line then
(String.sub src ofs (String.length src - ofs), skipped)
else
let ch, next_ofs = Zed_utf8.unsafe_extract_next src ofs in
if Zed_utf8.escaped_char ch = "\\n" then
aux (line' + 1) next_ofs (skipped + 1)
else
aux line' next_ofs (skipped + 1)
in
aux 1 0 0
let convert_one_line str line ofs=
let selected, skipped = get_line str line in
index_of_offset selected ofs + skipped
let convert_line str (start_ofs, end_ofs) lines =
(convert_one_line str lines.start start_ofs,
convert_one_line str lines.stop end_ofs)
let convert_loc_line input locs lines =
List.map2 (fun loc line ->
match line with
| None ->
convert_loc input loc
| Some line ->
convert_line input loc line) locs lines
(* +-----------------------------------------------------------------+ (* +-----------------------------------------------------------------+
| The read-line class | | The read-line class |
@ -127,8 +161,8 @@ let parse_and_check input eos_is_error =
match UTop.check_phrase phrase with match UTop.check_phrase phrase with
| None -> | None ->
UTop.Value phrase UTop.Value phrase
| Some (locs, msg) -> | Some (locs, msg, lines) ->
UTop.Error (convert_locs input locs, msg) UTop.Error (convert_loc_line input locs lines, msg)
with Pparse.Error error -> with Pparse.Error error ->
Pparse.report_error Format.str_formatter error; Pparse.report_error Format.str_formatter error;
UTop.Error ([], "Error: " ^ Format.flush_str_formatter () ^ "\n")) UTop.Error ([], "Error: " ^ Format.flush_str_formatter () ^ "\n"))
@ -1051,7 +1085,7 @@ module Emacs(M : sig end) = struct
let typecheck phrase = let typecheck phrase =
match UTop.check_phrase phrase with match UTop.check_phrase phrase with
| None -> None | None -> None
| Some (locs, msg) -> Some (convert_locs input locs, msg) (* FIXME *) | Some (locs, msg, lines) -> Some (convert_loc_line input locs lines, msg)
in in
match result with match result with
| UTop.Value phrases -> | UTop.Value phrases ->