Skip to content

Commit

Permalink
Use contents prefix to improve the detection of copybooks
Browse files Browse the repository at this point in the history
Also makes use of configured copybook search path to deal file names
without any extension (as a best-effort approach).
  • Loading branch information
nberth committed Oct 21, 2024
1 parent ed344b4 commit ad9d9a2
Show file tree
Hide file tree
Showing 37 changed files with 311 additions and 152 deletions.
8 changes: 4 additions & 4 deletions .drom

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
## [0.1.4] Next release

### Added
- Detection of copybooks based on contents prefix and configured search path [373](https://github.com/OCamlPro/superbol-studio-oss/pull/373)
- Support for connecting to the LSP server remotely (TCP only) [#102](https://github.com/OCamlPro/superbol-studio-oss/pull/102)
- Support for Symbol Renaming command [#351](https://github.com/OCamlPro/superbol-studio-oss/pull/351)
- Show documentation comments on hover information [#350](https://github.com/OCamlPro/superbol-studio-oss/pull/350)
Expand Down
1 change: 1 addition & 0 deletions dune-project

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

1 change: 1 addition & 0 deletions opam/osx/superbol_project-osx.opam
Original file line number Diff line number Diff line change
Expand Up @@ -53,6 +53,7 @@ depends: [
"ocplib_stuff-osx" {>= "0.4.0" & < "1.0.0"}
"ezr_toml-osx" {= version}
"ez_file-osx" {>= "0.3.0" & < "1.0.0"}
"cobol_preproc-osx" {= version}
"cobol_config-osx" {= version}
"cobol_common-osx" {= version}
"odoc" {with-doc}
Expand Down
1 change: 1 addition & 0 deletions opam/superbol_project.opam
Original file line number Diff line number Diff line change
Expand Up @@ -51,6 +51,7 @@ depends: [
"ocplib_stuff" {>= "0.4.0" & < "1.0.0"}
"ezr_toml" {= version}
"ez_file" {>= "0.3.0" & < "1.0.0"}
"cobol_preproc" {= version}
"cobol_config" {= version}
"cobol_common" {= version}
"odoc" {with-doc}
Expand Down
1 change: 1 addition & 0 deletions opam/windows/superbol_project-windows.opam
Original file line number Diff line number Diff line change
Expand Up @@ -53,6 +53,7 @@ depends: [
"ocplib_stuff-windows" {>= "0.4.0" & < "1.0.0"}
"ezr_toml-windows" {= version}
"ez_file-windows" {>= "0.3.0" & < "1.0.0"}
"cobol_preproc-windows" {= version}
"cobol_config-windows" {= version}
"cobol_common-windows" {= version}
"odoc" {with-doc}
Expand Down
3 changes: 2 additions & 1 deletion package.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

15 changes: 8 additions & 7 deletions src/lsp/cobol_common/copybook.ml
Original file line number Diff line number Diff line change
Expand Up @@ -28,15 +28,16 @@ type lookup_error =
lookup_config: lookup_config;
}

(** Filename extensions that we should treat as copybooks and not main
programs. *)
let copybook_extensions = (* this must be a subset of {!libfile_extensions}. *)
["cpy"; "cbx"]
(** Filename extensions that are searched when looking up copybooks. *)
let copybook_extensions =
["cpy"; "cbl"; "cob"]

let libfile_extensions =
["cpy"; "cbl"; "cob"; "cbx"]
(** Filename extensions that we should not treat as main programs, but as
copybooks instead. *)
let copybookonly_extensions =
["cpy"; "copy"; "copybook"]

let lookup_config ?(libexts = libfile_extensions) libpath =
let lookup_config ?(libexts = copybook_extensions) libpath =
{
lookup_path = libpath;
lookup_exts = List.map String.lowercase_ascii libexts;
Expand Down
1 change: 1 addition & 0 deletions src/lsp/cobol_common/copybook.mli
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,7 @@ val pp_lookup_error: lookup_error Pretty.printer
(* --- *)

val copybook_extensions: string list
val copybookonly_extensions: string list

(** [find_lib ~lookup_config ?fromfile ?libname txtname] attempts to locate a
file containing the copybook [txtname], which is a file named [txtname],
Expand Down
3 changes: 1 addition & 2 deletions src/lsp/cobol_indent_old/indenter.ml
Original file line number Diff line number Diff line change
Expand Up @@ -40,5 +40,4 @@ let indent_range ~dialect ~source_format ~indent_config ~range ~filename ~conten
; acc = []
; range }
in
(* NB: note here we ignore diagnostics *)
state.result.acc
state.acc
16 changes: 9 additions & 7 deletions src/lsp/cobol_lsp/lsp_document.ml
Original file line number Diff line number Diff line change
Expand Up @@ -164,12 +164,14 @@ let rec inspect_at ~position ({ copybook; rewinder; textdoc; _ } as doc) =
None

(** Creates a record for a document that is not yet parsed or analyzed. *)
let blank ~project ?copybook textdoc =
let copybook = match copybook with
| Some p -> p
| None -> Lsp_project.detect_copybook project
~uri:(Lsp.Text_document.documentUri textdoc)
let blank ~project textdoc =
let uri = Lsp.Text_document.documentUri textdoc in
let copybook =
Lsp_project.detect_copybook project ~uri
~contents:(Lsp.Text_document.text textdoc)
in
if copybook then
Lsp_io.log_debug "%s appears to be a copybook" (Lsp.Uri.to_string uri);
{
project;
textdoc;
Expand All @@ -183,9 +185,9 @@ let blank ~project ?copybook textdoc =

let position_encoding = `UTF8

let load ~project ?copybook doc =
let load ~project doc =
let textdoc = Lsp.Text_document.make ~position_encoding doc in
let doc = blank ~project ?copybook textdoc in
let doc = blank ~project textdoc in
try parse_and_analyze doc
with e -> raise @@ Internal_error (doc, e, Printexc.get_raw_backtrace ())

Expand Down
5 changes: 3 additions & 2 deletions src/lsp/cobol_lsp/lsp_project.ml
Original file line number Diff line number Diff line change
Expand Up @@ -71,8 +71,9 @@ let copybook_lookup_config_for ~uri project =
Superbol_project.copybook_lookup_config_for ~filename:(Lsp.Uri.to_path uri)
project

let detect_copybook ~uri project =
Superbol_project.detect_copybook ~filename:(Lsp.Uri.to_path uri) project
let detect_copybook ~uri ?contents project =
Superbol_project.detect_copybook ~filename:(Lsp.Uri.to_path uri)
?contents project

let relative_path_for ~uri project =
Superbol_project.relative_path_for ~filename:(Lsp.Uri.to_path uri) project
Expand Down
6 changes: 3 additions & 3 deletions src/lsp/cobol_lsp/lsp_project.mli
Original file line number Diff line number Diff line change
Expand Up @@ -57,9 +57,9 @@ val rootdir_for: uri:Lsp.Uri.t -> layout:layout -> rootdir
val copybook_lookup_config_for
: uri:Lsp.Uri.t -> t -> Cobol_common.Copybook.lookup_config

(** [detect_copybook ~uri project] indicates whether a document at the given URI
for [project] should be treated as a copybook. *)
val detect_copybook: uri:Lsp.Uri.t -> t -> bool
(** [detect_copybook ~uri ?contents project] indicates whether a document at the
given URI for [project] should be treated as a copybook. *)
val detect_copybook: uri:Lsp.Uri.t -> ?contents:string -> t -> bool

(** {1 Cached representation} *)

Expand Down
8 changes: 4 additions & 4 deletions src/lsp/cobol_lsp/lsp_server.ml
Original file line number Diff line number Diff line change
Expand Up @@ -663,10 +663,10 @@ let on_change_workspace_folders


let add ~doc:(DidOpenTextDocumentParams.{ textDocument = { uri; _ }; _ } as doc)
?copybook registry =
registry =
let add_in_project project registry =
try
let doc = Lsp_document.load ~project ?copybook doc in
let doc = Lsp_document.load ~project doc in
let registry = dispatch_diagnostics doc registry in
add_or_replace_doc doc registry
with Lsp_document.Internal_error (doc, e, backtrace) ->
Expand All @@ -677,7 +677,7 @@ let add ~doc:(DidOpenTextDocumentParams.{ textDocument = { uri; _ }; _ } as doc)


let did_open (DidOpenTextDocumentParams.{ textDocument = { uri; text; _ };
_ } as doc) ?copybook registry =
_ } as doc) registry =
(* Try first with a lookup for the project in a cache, and then by
creating/loading the project. *)
let rec aux ~try_cache registry =
Expand All @@ -690,7 +690,7 @@ let did_open (DidOpenTextDocumentParams.{ textDocument = { uri; text; _ };
retrieve_project_for ~uri registry |>
aux ~try_cache:false (* try again without the cache *)
| None | Some _ ->
add ~doc ?copybook registry
add ~doc registry
in
aux ~try_cache:true registry

Expand Down
8 changes: 3 additions & 5 deletions src/lsp/cobol_lsp/lsp_server.mli
Original file line number Diff line number Diff line change
Expand Up @@ -79,12 +79,10 @@ val init
val on_change_workspace_folders
: Lsp.Types.DidChangeWorkspaceFoldersParams.t -> t -> t

(** When given, [copybook] indicates whether the document is a copybook (in
which case it is not parsed directly as a normal program). When absent,
copybook detection is performed via project configuration (see
{!Project.detect_copybook}). *)
(** Copybook detection is performed via project configuration (see
{!Lsp_project.detect_copybook}). *)
val did_open
: Lsp.Types.DidOpenTextDocumentParams.t -> ?copybook: bool -> t -> t
: Lsp.Types.DidOpenTextDocumentParams.t -> t -> t

val did_change
: Lsp.Types.DidChangeTextDocumentParams.t -> t -> t
Expand Down
41 changes: 40 additions & 1 deletion src/lsp/cobol_preproc/preproc_engine.ml
Original file line number Diff line number Diff line change
Expand Up @@ -568,10 +568,49 @@ let fold_source_lines ~dialect ~source_format ?on_initial_source_format
| Some f -> f (Src_reader.source_format reader) acc
| None -> acc
in
OUT.result @@
Src_reader.fold_lines ~dialect ~f reader
?skip_compiler_directives_text ?on_compiler_directive acc

let fold_source_words ~dialect ~source_format ~f input acc =
fold_source_lines ~dialect ~source_format input acc
~skip_compiler_directives_text:true
~f:begin fun _ line acc ->
ListLabels.fold_left line ~init:acc ~f:(fun acc word -> f word acc)
end

let scan_prefix_for_copybook ~dialect ~source_format input =
let open struct
exception Res of [`Program | `Copybook]
type copybook_prefix_state =
| Expect_first_digits
| Expect_word
let digit_chars s =
let rec aux i =
i < 0 || match s.[i] with '0'..'9' -> aux (pred i) | _ -> false
in
aux (String.length s - 1)
let is_digits = function
| Text.TextWord s -> digit_chars s
| _ -> false
let is_word = function
| Text.TextWord _ as w -> not (is_digits w)
| _ -> false
end in
match
fold_source_words ~dialect ~source_format input Expect_first_digits
~f:begin fun word -> function
| Expect_first_digits when is_digits ~&word ->
Expect_word
| Expect_word when is_word ~&word ->
raise @@ Res `Copybook
| _ ->
raise @@ Res `Program
end
with
| exception Res res -> res
| Expect_first_digits -> `Program (* maybe? *)
| Expect_word -> `Copybook (* maybe? *)

let text_of_input ?options input =
let text, pp = full_text ~item:"file" @@ preprocessor ?options input in
OUT.result text ~diags:(diags pp)
Expand Down
8 changes: 7 additions & 1 deletion src/lsp/cobol_preproc/preproc_engine.mli
Original file line number Diff line number Diff line change
Expand Up @@ -90,7 +90,13 @@ val fold_source_lines
-> f:(int -> Text.text -> 'a -> 'a)
-> Src_input.t
-> 'a
-> 'a Preproc_outputs.with_diags
-> 'a

val scan_prefix_for_copybook
: dialect: Cobol_config.dialect
-> source_format: Cobol_config.source_format_spec
-> Src_input.t
-> [`Program | `Copybook]

val preprocess_input
: ?options: Preproc_options.preproc_options
Expand Down
2 changes: 1 addition & 1 deletion src/lsp/superbol_free_lib/vscode_extension.ml
Original file line number Diff line number Diff line change
Expand Up @@ -70,7 +70,7 @@ let package =
"vscode-languageclient", "8.0.2";
"polka", "^1.0.0-next.22";
"sirv", "^2.0.2";

(* for the debug extension: *)
"n-readlines", "^1.0.0";
]
Expand Down
2 changes: 1 addition & 1 deletion src/lsp/superbol_project/dune

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

1 change: 1 addition & 0 deletions src/lsp/superbol_project/package.toml
Original file line number Diff line number Diff line change
Expand Up @@ -54,6 +54,7 @@ skip = ["index.mld"]
# base-unix = { libname = "unix", version = ">=base" }
[dependencies]
cobol_common = "version"
cobol_preproc = "version"
cobol_config = "version"
ocplib_stuff = "0.4.0"
ez_file = "0.3.0"
Expand Down
36 changes: 33 additions & 3 deletions src/lsp/superbol_project/project.ml
Original file line number Diff line number Diff line change
Expand Up @@ -103,9 +103,6 @@ let for_ ~rootdir ~layout =
let copybook_lookup_config_for ~filename { config; _ } =
Project_config.copybook_lookup_config_for ~filename config

let detect_copybook ~filename { config; _ } =
Project_config.detect_copybook ~filename config

let relative_path_for ~filename { rootdir; _ } =
try Project_utils.relative_path ~filename rootdir
with Invalid_argument _ -> filename (* if not in project rootdir *)
Expand All @@ -115,6 +112,39 @@ let absolute_path_for ~filename { rootdir; _ } =
then filename (* in case the file is not within its project directory *)
else rootdir // filename

let file_contents_looks_like_a_copybook ~filename ?contents { config; _ } =
let decide input =
Cobol_preproc.scan_prefix_for_copybook input
~dialect:(Cobol_config.dialect config.cobol_config)
~source_format:config.source_format = `Copybook
in
match contents with
| None ->
Cobol_preproc.Input.from ~filename ~f:decide
| Some c ->
decide @@ Cobol_preproc.Input.string ~filename c

let is_a_copybook_extension ext =
List.mem (String.lowercase_ascii (EzString.after ext 1)) (* trim the `.` *)
Cobol_common.Copybook.copybookonly_extensions

let file_is_in_libpath ~filename ({ config; _ } as project) =
let filename = relative_path_for ~filename project in
List.exists begin function
| Project_config.RelativeToProjectRoot prefix ->
EzString.starts_with ~prefix filename
| RelativeToFileDir suffix ->
EzString.ends_with ~suffix (Filename.dirname filename)
end config.libpath

let detect_copybook ~filename ?contents project =
let ext = Filename.extension filename in
(if ext = "" (* assume files with no extension that appear in copybook paths
are copybooks *)
then file_is_in_libpath ~filename project
else is_a_copybook_extension ext) ||
file_contents_looks_like_a_copybook ~filename ?contents project

let save_config ?verbose { config_filename; config; _ } =
Project_config.save ?verbose ~config_filename config

Expand Down
6 changes: 3 additions & 3 deletions src/lsp/superbol_project/project.mli
Original file line number Diff line number Diff line change
Expand Up @@ -95,9 +95,9 @@ val copybook_lookup_config_for
-> t
-> Cobol_common.Copybook.lookup_config

(** [detect_copybook ~filename project] indicates whether a file name should be
treated as a copybook within [project]. *)
val detect_copybook: filename:string -> t -> bool
(** [detect_copybook ~filename project] indicates whether a document with the
given [filename] should be treated as a copybook in [project]. *)
val detect_copybook: filename:string -> ?contents:string -> t -> bool

(** {1 Cached representation} *)

Expand Down
Loading

0 comments on commit ad9d9a2

Please sign in to comment.