Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions design/DFX-Interface.md
Original file line number Diff line number Diff line change
Expand Up @@ -138,6 +138,7 @@ prints to the standard output all URLs _directly_ imported by
mo:other_package/Some/Module
ic:ABCDE01A7
ic:alias
relative/path

This _reads_ only `some/path/input.mo`, and writes no files.

Expand Down
2 changes: 1 addition & 1 deletion samples/ListClient.mo
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import List = "ListLib.mo"; // private, so we don't re-export List
import List = "ListLib"; // private, so we don't re-export List

module {

Expand Down
2 changes: 1 addition & 1 deletion samples/app/client.mo
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import Prim "mo:prim";
import S "server.mo";
import S "server";

actor class Client() = this {
// TODO: these should be constructor params once we can compile them
Expand Down
4 changes: 2 additions & 2 deletions samples/app/main.mo
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import Server "server.mo";
import Client "client.mo";
import Server "server";
import Client "client";

let server = Server.Server();
let bob = Client.Client();
Expand Down
4 changes: 2 additions & 2 deletions samples/app/server.mo
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import Prim "mo:prim";
import L "list.mo";
import T "types.mo";
import L "list";
import T "types";

type ClientData = {
id : Nat;
Expand Down
4 changes: 2 additions & 2 deletions src/languageServer/completion.ml
Original file line number Diff line number Diff line change
Expand Up @@ -134,7 +134,7 @@ let completions index logger project_root file_path file_contents line column =
let current_module_decls =
current_uri_opt
|> opt_bind (fun uri -> DI.lookup_module uri index)
|> Fun.flip Lib.Option.get [] in
|> Option.fold ~none:[] ~some:snd in
current_module_decls
in
let module_alias_completion_item alias =
Expand Down Expand Up @@ -165,7 +165,7 @@ let completions index logger project_root file_path file_contents line column =
match module_path with
| Some mp ->
(match DI.lookup_module (snd mp) index with
| Some decls ->
| Some (_, decls) ->
decls
|> List.filter (has_prefix prefix)
|> List.map item_of_ide_decl
Expand Down
6 changes: 3 additions & 3 deletions src/languageServer/completion_test.ml
Original file line number Diff line number Diff line change
Expand Up @@ -77,7 +77,7 @@ List.|
let%test "it handles a full module" =
prefix_test_case
{|module {
private import List = "./ListLib.mo";
private import List = "./ListLib";

func singleton<T>(x: T): List.List<T> =
List.cons<T>(x, Test.|<T>());
Expand All @@ -89,8 +89,8 @@ let%test "it handles a full module" =
let%test "it doesn't fall through to the next valid prefix" =
prefix_test_case
{|module {
private import List = "lib/ListLib.mo"; // private, so we don't re-export List
private import ListFns = "lib/ListFuncs.mo"; // private, so we don't re-export List
private import List = "lib/ListLib"; // private, so we don't re-export List
private import ListFns = "lib/ListFuncs"; // private, so we don't re-export List
type Stack = List.List<Int>;
func push(x : Int, s : Stack) : Stack = List.cons<Int>(x, s);
func empty():Stack = List.nil<Int>();
Expand Down
20 changes: 16 additions & 4 deletions src/languageServer/declaration_index.ml
Original file line number Diff line number Diff line change
Expand Up @@ -65,20 +65,32 @@ let add_module : string -> ide_decl list -> declaration_index -> declaration_ind
let lookup_module
(path : string)
(index : t)
: ide_decl list option =
: (string * ide_decl list) option =
let open Pipeline.URL in
let make_absolute = Lib.FilePath.make_absolute (Sys.getcwd ()) in
match parse path with
| Ok (Relative path) ->
let path =
Pipeline.ResolveImport.append_extension Sys.file_exists path
|> Option.value ~default:path in
Index.find_opt (make_absolute path) index.modules
|> Option.map (fun decls -> (path, decls))
| Ok (Package (pkg, path)) ->
Option.bind
(Flags.M.find_opt pkg index.package_map)
(fun pkg_path ->
let path =
Pipeline.ResolveImport.append_extension
Sys.file_exists (Filename.concat pkg_path path)
|> Option.value ~default:path in
Index.find_opt
(make_absolute (Filename.concat pkg_path path))
index.modules)
| Ok Prim -> Index.find_opt "@prim" index.modules
(make_absolute path)
index.modules
|> Option.map (fun decls -> (path, decls))
)
| Ok Prim ->
Index.find_opt "@prim" index.modules
|> Option.map (fun decls -> ("@prim", decls))
| Ok (Ic _ | IcAlias _) -> (* TODO *) None
| Error _ -> None

Expand Down
2 changes: 1 addition & 1 deletion src/languageServer/declaration_index.mli
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,6 @@ type ide_decl =
val string_of_ide_decl : ide_decl -> string
val name_of_ide_decl : ide_decl -> string

val lookup_module : string -> t -> ide_decl list option
val lookup_module : string -> t -> (string * ide_decl list) option

val make_index : (string -> string -> unit) -> Vfs.t -> string list -> t Diag.result
11 changes: 7 additions & 4 deletions src/languageServer/definition.ml
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,11 @@ let range_of_region (at : Source.region) : Lsp.range = Lsp.
range_end_ = position_of_pos at.Source.right;
}

let find_named (name : string) : DI.ide_decl list -> Source.region option =
let find_named
: string ->
(string * DI.ide_decl list) ->
(string * Source.region) option =
fun name (path, decls) ->
Lib.List.first_opt (function
| DI.ValueDecl value ->
if String.equal value.DI.name name
Expand All @@ -21,7 +25,8 @@ let find_named (name : string) : DI.ide_decl list -> Source.region option =
| DI.TypeDecl typ ->
if String.equal typ.DI.name name
then typ.DI.definition
else None)
else None) decls
|> Option.map (fun x -> (path, x))

let opt_bind f = function
| Some x -> f x
Expand All @@ -47,13 +52,11 @@ let definition_handler
| Resolved resolved ->
DI.lookup_module resolved.path index
|> opt_bind (find_named resolved.ident)
|> Option.map (fun loc -> (resolved.path, loc))
| Ident ident ->
Lib.FilePath.relative_to project_root file_path
|> opt_bind (fun uri ->
DI.lookup_module uri index
|> opt_bind (find_named ident)
|> Option.map (fun loc -> (uri, loc))
)) in
let location =
Option.map (fun (path, region) ->
Expand Down
4 changes: 2 additions & 2 deletions src/languageServer/hover.ml
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@ let hover_handler
let current_module_decls =
current_uri_opt
|> Fun.flip Option.bind (fun uri -> lookup_module uri index)
|> Fun.flip Lib.Option.get [] in
|> Option.fold ~none:[] ~some:snd in
current_module_decls in
let mk_hover_result ide_decl =
Lsp.{ hover_result_contents = markup_content (hover_detail ide_decl) } in
Expand All @@ -42,7 +42,7 @@ let hover_handler
Some Lsp.{ hover_result_contents = markup_content path }
| Source_file.Resolved resolved ->
lookup_module resolved.Source_file.path index
|> Fun.flip Option.bind (fun decls ->
|> Fun.flip Option.bind (fun (_, decls) ->
List.find_opt
(fun d -> name_of_ide_decl d = resolved.Source_file.ident)
decls)
Expand Down
6 changes: 5 additions & 1 deletion src/languageServer/source_file.ml
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,10 @@ type cursor_target =
| CIdent of string
| CQualified of string * string

let string_of_cursor_target = function
| CIdent i -> "(CIdent " ^ i ^ ")"
| CQualified (q, i) -> "CQUalified (" ^ q ^ ", " ^ i ^ ")"

let cursor_target_at_pos
(position : Lsp.position)
(file_contents : string)
Expand Down Expand Up @@ -118,7 +122,7 @@ let identifier_at_pos project_root file_path file_contents position =
file_path
file_contents in
cursor_target_at_pos position file_contents
|> Option.map (function
|> Option.map (function
| CIdent s ->
(match List.find_opt (fun (alias, _) -> alias = s) imported with
| None -> Ident s
Expand Down
16 changes: 8 additions & 8 deletions src/languageServer/source_file_tests.ml
Original file line number Diff line number Diff line change
Expand Up @@ -67,8 +67,8 @@ let%test "it parses a simple module header" =
parse_module_header_test_case
"/project"
"/project/src/Main.mo"
"import P \"lib/prelude.mo\""
["P", "src/lib/prelude.mo"]
"import P \"lib/prelude\""
["P", "src/lib/prelude"]

let%test "it parses a simple module header that contains a prim import" =
parse_module_header_test_case
Expand All @@ -81,8 +81,8 @@ let%test "it parses a simple module header with package paths" =
parse_module_header_test_case
"/project"
"/project/src/Main.mo"
"import P \"mo:stdlib/prelude.mo\""
["P", "mo:stdlib/prelude.mo"]
"import P \"mo:stdlib/prelude\""
["P", "mo:stdlib/prelude"]

let%test "it parses a simple module header" =
parse_module_header_test_case
Expand All @@ -91,8 +91,8 @@ let%test "it parses a simple module header" =
{|
module {

private import List "lib/ListLib.mo";
private import ListFuncs "lib/ListFuncs.mo";
private import List "lib/ListLib";
private import ListFuncs "lib/ListFuncs";

type Stack = List.List<Int>;

Expand All @@ -106,6 +106,6 @@ func singleton(x: Int): Stack =
ListFuncs.doubleton<Int>(x, x);
}
|}
[ ("List", "lib/ListLib.mo")
; ("ListFuncs", "lib/ListFuncs.mo")
[ ("List", "lib/ListLib")
; ("ListFuncs", "lib/ListFuncs")
]
41 changes: 30 additions & 11 deletions src/pipeline/resolve_import.ml
Original file line number Diff line number Diff line change
Expand Up @@ -47,10 +47,16 @@ type package_map = filepath M.t
open Syntax
open Source

let append_lib_if_needed f =
if Sys.file_exists f && Sys.is_directory f
then Filename.concat f "lib.mo"
else f
let append_extension file_exists f =
if Option.is_some (Lib.String.chop_suffix "/" f) then
Some (Filename.concat f "lib.mo")
else if Filename.extension f = "" then
if file_exists (f ^ ".mo") then
Some (f ^ ".mo")
else
Some (Filename.concat f "lib.mo")
else
None

let err_unrecognized_url msgs at url msg =
let open Diag in
Expand Down Expand Up @@ -88,6 +94,15 @@ let err_file_does_not_exist msgs at full_path =
text = Printf.sprintf "file \"%s\" does not exist" full_path
}

let err_import_musnt_have_extension msgs at full_path =
let open Diag in
add_msg msgs {
sev = Error;
at;
cat = "import";
text = Printf.sprintf "an import must not have an extension, try importing %s as %s instead" full_path (Filename.chop_extension full_path)
}

let err_package_not_defined msgs at pkg =
let open Diag in
add_msg msgs {
Expand Down Expand Up @@ -135,13 +150,17 @@ let err_prim_pkg msgs =
}

let add_lib_import msgs imported ri_ref at full_path =
let full_path = append_lib_if_needed full_path in
if Sys.file_exists full_path
then begin
ri_ref := LibPath full_path;
imported := RIM.add (LibPath full_path) at !imported
end else
err_file_does_not_exist msgs at full_path
match append_extension Sys.file_exists full_path with
| Some full_path ->
if Sys.file_exists full_path
then begin
ri_ref := LibPath full_path;
imported := RIM.add (LibPath full_path) at !imported
end else
err_file_does_not_exist msgs at full_path
| None ->
err_import_musnt_have_extension msgs at full_path


let add_idl_import msgs imported ri_ref at full_path bytes =
if Sys.file_exists full_path
Expand Down
3 changes: 3 additions & 0 deletions src/pipeline/resolve_import.mli
Original file line number Diff line number Diff line change
Expand Up @@ -31,3 +31,6 @@ type resolved_flags = {
val resolve_flags : flags -> resolved_flags Diag.result

val resolve : flags -> Syntax.prog -> filepath -> resolved_imports Diag.result

(* Exported for tests *)
val append_extension : (filepath -> bool) -> filepath -> filepath option
25 changes: 25 additions & 0 deletions src/pipeline/resolve_import_test.ml
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
(** Given a list of filenames that should be reported as existing
tests what a given import path resolves to *)
let import_relative_test_case files import expected =
let actual = Resolve_import.append_extension (fun x -> List.mem x files) import in
let show = function
| None -> "None"
| Some s -> "Some " ^ s in
Option.equal String.equal actual expected ||
(Printf.printf
"\nExpected: %s\nActual: %s\n"
(show expected)
(show actual);
false)

let%test "it resolves a relative file import" =
import_relative_test_case ["list.mo"] "list" (Some "list.mo")

let%test "it resolves a relative directory import" =
import_relative_test_case [] "list/" (Some "list/lib.mo")

let%test "it resolves to a relative directory import if no .mo file is found" =
import_relative_test_case [] "list" (Some "list/lib.mo")

let%test "it fails on a relative import with an extension" =
import_relative_test_case [] "list.mo" None
2 changes: 1 addition & 1 deletion stdlib/examples/actorspec/demo/Demo.mo
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import ActorSpec "../src/ActorSpec.mo";
import ActorSpec "../src/ActorSpec";

type Group = ActorSpec.Group;

Expand Down
8 changes: 4 additions & 4 deletions stdlib/examples/actorspec/src/ActorSpec.mo
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import Debug "mo:stdlib/debug.mo";
import Array "mo:stdlib/array.mo";
import Int "mo:stdlib/int.mo";
import Text "mo:stdlib/text.mo";
import Debug "mo:stdlib/debug";
import Array "mo:stdlib/array";
import Int "mo:stdlib/int";
import Text "mo:stdlib/text";

module {
public type Group = {
Expand Down
4 changes: 2 additions & 2 deletions stdlib/examples/produce-exchange/src/docTable.mo
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import Hash "mo:stdlib/hash.mo";
import Hash "mo:stdlib/hash";

import Trie "mo:stdlib/trie.mo";
import Trie "mo:stdlib/trie";

module {
/**
Expand Down
20 changes: 10 additions & 10 deletions stdlib/examples/produce-exchange/src/serverActor.mo
Original file line number Diff line number Diff line change
Expand Up @@ -4,16 +4,16 @@
--------------------
*/

import Debug "mo:stdlib/debug.mo";
import P = "mo:stdlib/prelude.mo";
import Option = "mo:stdlib/option.mo";
import T = "serverTypes.mo";
import L = "serverLang.mo";
import Model = "serverModel.mo";
import Result = "mo:stdlib/result.mo";

import Trie = "mo:stdlib/trie.mo";
import List = "mo:stdlib/list.mo";
import Debug "mo:stdlib/debug";
import P = "mo:stdlib/prelude";
import Option = "mo:stdlib/option";
import T = "serverTypes";
import L = "serverLang";
import Model = "serverModel";
import Result = "mo:stdlib/result";

import Trie = "mo:stdlib/trie";
import List = "mo:stdlib/list";

type List<T> = List.List<T>;

Expand Down
4 changes: 2 additions & 2 deletions stdlib/examples/produce-exchange/src/serverLang.mo
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import Result = "mo:stdlib/result.mo";
import T = "serverTypes.mo";
import Result = "mo:stdlib/result";
import T = "serverTypes";


module {
Expand Down
Loading