diff --git a/src/loading/fs.rs b/src/loading/fs.rs index e2654a2e5..a37d96300 100644 --- a/src/loading/fs.rs +++ b/src/loading/fs.rs @@ -194,9 +194,9 @@ fn load(cache: &mut FileCache, path: &Path) -> Option { /// Create a hash that is the same for all paths pointing to the same file. fn hash(path: &Path) -> Option { - Handle::from_path(path) - .map(|handle| FileHash(fxhash::hash64(&handle))) - .ok() + let file = File::open(path).ok()?; + let handle = Handle::from_file(file).ok()?; + Some(FileHash(fxhash::hash64(&handle))) } #[cfg(test)] diff --git a/src/parse/mod.rs b/src/parse/mod.rs index 9104b9247..5bda818fd 100644 --- a/src/parse/mod.rs +++ b/src/parse/mod.rs @@ -563,8 +563,8 @@ fn expr_import(p: &mut Parser) -> Option { let mut expr_import = None; if let Some(path) = expr(p) { - if p.expect(Token::Using) { - let imports = if p.eat_if(Token::Star) { + let imports = if p.expect(Token::Using) { + if p.eat_if(Token::Star) { // This is the wildcard scenario. Imports::Wildcard } else { @@ -578,14 +578,16 @@ fn expr_import(p: &mut Parser) -> Option { let idents = idents(p, items); p.end_group(); Imports::Idents(idents) - }; + } + } else { + Imports::Idents(vec![]) + }; - expr_import = Some(Expr::Import(ImportExpr { - span: p.span(start), - imports, - path: Box::new(path), - })); - } + expr_import = Some(Expr::Import(ImportExpr { + span: p.span(start), + imports, + path: Box::new(path), + })); } expr_import diff --git a/tests/ref/code/import.png b/tests/ref/code/import.png new file mode 100644 index 000000000..0a8114955 Binary files /dev/null and b/tests/ref/code/import.png differ diff --git a/tests/ref/code/include.png b/tests/ref/code/include.png new file mode 100644 index 000000000..909a36433 Binary files /dev/null and b/tests/ref/code/include.png differ diff --git a/tests/typ/code/block-scoping.typ b/tests/typ/code/block-scoping.typ index 7bb98969a..ed689f3d3 100644 --- a/tests/typ/code/block-scoping.typ +++ b/tests/typ/code/block-scoping.typ @@ -18,6 +18,16 @@ // Error: 2-3 unknown variable {b} +--- +// Double block creates a scope. +{{ + import "target.typ" using b + test(b, 1) +}} + +// Error: 2-3 unknown variable +{b} + --- // Multiple nested scopes. { diff --git a/tests/typ/code/import.typ b/tests/typ/code/import.typ new file mode 100644 index 000000000..30dc556c7 --- /dev/null +++ b/tests/typ/code/import.typ @@ -0,0 +1,119 @@ +// Test import statements. + +--- +// Test importing semantics. + +// A named import. +#import "target.typ" using item +#test(item(1, 2), 3) + +// Test that this will be overwritten. +#let value = [foo] + +// Import multiple things. +// Error: 28-29 expected expression, found comma +#import "target.typ" using ,fn, value +#fn[Like and Subscribe!] +#value + +// Code mode +{ + import "target.typ" using b + test(b, 1) +} + +#test(b, 1) + +// This should not exist yet +// Error: 1-3 unknown variable +#d + +// A wildcard import. +#import "target.typ" using * + +// It exists now! +#d + +--- +// Test bad imports. +// Ref: false + +// Error: 9-11 file not found +#import "" using name + +// Error: 9-20 file not found +#import "lib/0.2.1" using * + +// Error: 9-20 file not found +#import "lib@0.2.1" using * + +// Some non-text stuff. +// Error: 9-30 file is not valid utf-8 +#import "../../res/rhino.png" using * + +// Unresolved import. +// Error: 28-40 unresolved import +#import "target.typ" using non_existing + +// Cyclic import. +// Error: 9-34 cyclic import +#import "./importable/cycle1.typ" using * + +--- +// Test syntax. + +// Missing file. +// Error: 9-10 expected expression, found star +#import * + +// Should output `"target.typ"`. +// Error: 1-7 unexpected keyword `using` +#using "target.typ" + +// Should output `target`. +// Error: 3:9-4:8 file not found +// Error: 3:8 expected semicolon or line break +// Error: 2:8 expected keyword `using` +#import "target.typ +using "target + +// Should output `@ 0.2.1 using`. +// Error: 2:21 expected semicolon or line break +// Error: 1:21 expected keyword `using` +#import "target.typ" @ 0.2.1 using * + +// Error: 3:21 expected keyword `using` +// Error: 2:21 expected semicolon or line break +// Error: 1:22-1:28 unexpected keyword `using` +#import "target.typ" #using * + +// Error: 2:21 expected semicolon or line break +// Error: 1:21 expected keyword `using` +#import "target.typ" usinga,b,c + +// Error: 27 expected import items +#import "target.typ" using + +// Error: 2:28-2:29 expected expression, found assignment operator +// Error: 1:29 expected import items +#import "target.typ" using = + +// Allow the trailing comma. +#import "target.typ" using a, c, + +// An additional trailing comma. +// Error: 36-37 expected expression, found comma +#import "target.typ" using a, b, c,, + +// Star in the list. +// Error: 2:31-2:32 expected expression, found star +// Error: 32-33 expected expression, found comma +#import "target.typ" using a, *, b + +// Stop at semicolon. +#import "target.typ" using a, c;Hi + +// Who needs whitespace anyways? +#import "target.typ"using * +#import"target.typ"using* +#import "target.typ"using * diff --git a/tests/typ/code/importable/chap1.typ b/tests/typ/code/importable/chap1.typ new file mode 100644 index 000000000..06a4c1a1a --- /dev/null +++ b/tests/typ/code/importable/chap1.typ @@ -0,0 +1,9 @@ +// Ref: false + +#let name = "Klaus" + +== Chapter 1 +#name stood in a field of wheat. There was nothing of particular interest about +the field #name just casually surveyed for any paths on which the corn would not +totally ruin his semi-new outdorsy jacket but then again, most of us spend +considerable time in non-descript environments. diff --git a/tests/typ/code/importable/chap2.typ b/tests/typ/code/importable/chap2.typ new file mode 100644 index 000000000..d4aedc60d --- /dev/null +++ b/tests/typ/code/importable/chap2.typ @@ -0,0 +1,11 @@ +// Ref: false + +#let name = "Klaus" + +== Chapter 2 +Their motivations, however, were pretty descript, so to speak. #name had not yet +conceptualized their consequences, but that should change pretty quickly. #name +approached the center of the field and picked up a 4-foot long disk made from +what could only be cow manure. The hair on the back of #name' neck bristled as +he stared at the unusual sight. After studying the object for a while, he +promptly popped the question, "How much?" diff --git a/tests/typ/code/importable/cycle1.typ b/tests/typ/code/importable/cycle1.typ new file mode 100644 index 000000000..e251686d8 --- /dev/null +++ b/tests/typ/code/importable/cycle1.typ @@ -0,0 +1,7 @@ +// Ref: false + +// Error: 9-21 cyclic import +#import "cycle2.typ" using * +#let inaccessible = "wow" + +This is the first element of an import cycle. diff --git a/tests/typ/code/importable/cycle2.typ b/tests/typ/code/importable/cycle2.typ new file mode 100644 index 000000000..8071ec6b2 --- /dev/null +++ b/tests/typ/code/importable/cycle2.typ @@ -0,0 +1,7 @@ +// Ref: false + +// Error: 9-21 cyclic import +#import "cycle1.typ" using * +#let val = "much cycle" + +This is the second element of an import cycle. diff --git a/tests/typ/code/include.typ b/tests/typ/code/include.typ new file mode 100644 index 000000000..9fd028f98 --- /dev/null +++ b/tests/typ/code/include.typ @@ -0,0 +1,23 @@ +// Test include statements. + +--- += Document + +// Include a file +#include "importable/chap1.typ" + +// The variables of the file should not appear in this scope. +// Error: 1-6 unknown variable +#name + +// Expression as a file name. +#let chap2 = include "import" + "able/chap" + "2.typ" + +_ -- Intermission -- _ +#chap2 + +{ + // Expressions, code mode. + // Error: 21-43 file not found + let x = include "importable/chap3.typ" +} diff --git a/tests/typ/code/target.typ b/tests/typ/code/target.typ new file mode 100644 index 000000000..12a5ff8b5 --- /dev/null +++ b/tests/typ/code/target.typ @@ -0,0 +1,12 @@ +// A file to import in import / include tests. +// Ref: false + +#let a +#let b = 1 +#let c = 2 +#let d = 3 +#let value = [hi] +#let item(a, b) = a + b +#let fn(body) = rect(fill: conifer, pad(5pt, body)) + +Some _includable_ text.