From fbf6417def1beca16fe3e1b00f2d00168992ae23 Mon Sep 17 00:00:00 2001 From: Keith Hall Date: Mon, 11 Jun 2018 10:59:21 +0300 Subject: [PATCH 1/6] keep with_prototype when switching contexts with `set` --- src/parsing/parser.rs | 60 +++++++++++++++++++++++++++++++++++++++---- 1 file changed, 55 insertions(+), 5 deletions(-) diff --git a/src/parsing/parser.rs b/src/parsing/parser.rs index 470578dd..e00eb27f 100644 --- a/src/parsing/parser.rs +++ b/src/parsing/parser.rs @@ -595,11 +595,11 @@ impl ParseState { /// Returns true if the stack was changed fn perform_op(&mut self, line: &str, regions: &Region, pat: &MatchPattern) -> bool { - let ctx_refs = match pat.operation { - MatchOperation::Push(ref ctx_refs) => ctx_refs, + let (ctx_refs, old_proto) = match pat.operation { + MatchOperation::Push(ref ctx_refs) => (ctx_refs, None), MatchOperation::Set(ref ctx_refs) => { - self.stack.pop(); - ctx_refs + let old_proto = self.stack.pop().and_then(|s| s.prototype); + (ctx_refs, old_proto) } MatchOperation::Pop => { self.stack.pop(); @@ -613,7 +613,9 @@ impl ParseState { // top most on the stack after all the contexts are pushed - this is also // referred to as the "target" of the push by sublimehq - see // https://forum.sublimetext.com/t/dev-build-3111/19240/17 for more info - let proto = if i == ctx_refs.len() - 1 { + let proto = if i == 0 && pat.with_prototype.is_none() { + old_proto.clone() + } else if i == ctx_refs.len() - 1 { pat.with_prototype.clone() } else { None @@ -1257,6 +1259,54 @@ contexts: expect_scope_stacks_with_syntax("/** * */", &[", ", ", , ", ", "], syntax); } + #[test] + fn can_parse_with_prototype_set() { + let syntax = r#"%YAML 1.2 +--- +scope: source.test-set-with-proto +contexts: + main: + - match: a + scope: a + set: next1 + with_prototype: + - match: '1' + scope: '1' + - match: '2' + scope: '2' + - match: '3' + scope: '3' + - match: '4' + scope: '4' + - match: '5' + scope: '5' + next1: + - match: b + scope: b + set: next2 + next2: + - match: c + scope: c + push: next3 + - match: e + scope: e + pop: true + next3: + - match: d + scope: d + - match: (?=e) + pop: true +"#; + + let syntax = SyntaxDefinition::load_from_str(&syntax, true, None).unwrap(); + expect_scope_stacks_with_syntax( + "a1b2c3d4e5", + &[ + "", "<1>", "", "<2>", "", "<3>", "", "<4>", "", "<5>" + ], syntax + ); + } + fn expect_scope_stacks(line_without_newline: &str, expect: &[&str], syntax: &str) { println!("Parsing with newlines"); let line_with_newline = format!("{}\n", line_without_newline); From 74eda916f8612c4371e0be8f8401bcaf9fcaa40a Mon Sep 17 00:00:00 2001 From: Keith Hall Date: Mon, 11 Jun 2018 11:40:12 +0300 Subject: [PATCH 2/6] add comment explaining `set` `with_prototype` behavior --- src/parsing/parser.rs | 13 ++++++++----- 1 file changed, 8 insertions(+), 5 deletions(-) diff --git a/src/parsing/parser.rs b/src/parsing/parser.rs index e00eb27f..2ca27241 100644 --- a/src/parsing/parser.rs +++ b/src/parsing/parser.rs @@ -608,14 +608,17 @@ impl ParseState { MatchOperation::None => return false, }; for (i, r) in ctx_refs.iter().enumerate() { - // if a with_prototype was specified, and multiple contexts were pushed, - // then the with_prototype applies only to the last context pushed, i.e. - // top most on the stack after all the contexts are pushed - this is also - // referred to as the "target" of the push by sublimehq - see - // https://forum.sublimetext.com/t/dev-build-3111/19240/17 for more info let proto = if i == 0 && pat.with_prototype.is_none() { + // a `with_prototype` stays active when the context is `set` + // until the context layer in the stack (where the `with_prototype` + // was initially applied) is popped off. old_proto.clone() } else if i == ctx_refs.len() - 1 { + // if a with_prototype was specified, and multiple contexts were pushed, + // then the with_prototype applies only to the last context pushed, i.e. + // top most on the stack after all the contexts are pushed - this is also + // referred to as the "target" of the push by sublimehq - see + // https://forum.sublimetext.com/t/dev-build-3111/19240/17 for more info pat.with_prototype.clone() } else { None From 37533e10025329e2b2c85c8736e45de1db9b89b9 Mon Sep 17 00:00:00 2001 From: Keith Hall Date: Mon, 11 Jun 2018 12:21:09 +0300 Subject: [PATCH 3/6] correctly keep with_prototype on `set` for top context only --- src/parsing/parser.rs | 17 ++++++++++++++--- 1 file changed, 14 insertions(+), 3 deletions(-) diff --git a/src/parsing/parser.rs b/src/parsing/parser.rs index 2ca27241..95e2e48e 100644 --- a/src/parsing/parser.rs +++ b/src/parsing/parser.rs @@ -608,7 +608,7 @@ impl ParseState { MatchOperation::None => return false, }; for (i, r) in ctx_refs.iter().enumerate() { - let proto = if i == 0 && pat.with_prototype.is_none() { + let proto = if i == ctx_refs.len() - 1 && pat.with_prototype.is_none() { // a `with_prototype` stays active when the context is `set` // until the context layer in the stack (where the `with_prototype` // was initially applied) is popped off. @@ -1283,6 +1283,10 @@ contexts: scope: '4' - match: '5' scope: '5' + set: [next3, next2] + with_prototype: + - match: c + scope: cwith next1: - match: b scope: b @@ -1299,14 +1303,21 @@ contexts: scope: d - match: (?=e) pop: true + - match: c + scope: cwithout "#; - let syntax = SyntaxDefinition::load_from_str(&syntax, true, None).unwrap(); expect_scope_stacks_with_syntax( "a1b2c3d4e5", &[ "", "<1>", "", "<2>", "", "<3>", "", "<4>", "", "<5>" - ], syntax + ], SyntaxDefinition::load_from_str(&syntax, true, None).unwrap() + ); + expect_scope_stacks_with_syntax( + "5cedcea", + &[ + "<5>", "", "", "", "", "" + ], SyntaxDefinition::load_from_str(&syntax, true, None).unwrap() ); } From 250584fa4dc5ffc49e5971a0954650af32d3d0bb Mon Sep 17 00:00:00 2001 From: Keith Hall Date: Mon, 11 Jun 2018 13:24:50 +0300 Subject: [PATCH 4/6] test set multiple contexts when with_prototype already active --- src/parsing/parser.rs | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/src/parsing/parser.rs b/src/parsing/parser.rs index 95e2e48e..3884ea22 100644 --- a/src/parsing/parser.rs +++ b/src/parsing/parser.rs @@ -1298,6 +1298,9 @@ contexts: - match: e scope: e pop: true + - match: f + scope: f + set: [next1, next2] next3: - match: d scope: d @@ -1314,9 +1317,9 @@ contexts: ], SyntaxDefinition::load_from_str(&syntax, true, None).unwrap() ); expect_scope_stacks_with_syntax( - "5cedcea", + "5cfcecbedcdea", &[ - "<5>", "", "", "", "", "" + "<5>", "", "", "", "", "", "", "" ], SyntaxDefinition::load_from_str(&syntax, true, None).unwrap() ); } From 6d963fb7ef704068dae5dac094e7f0336797f4cc Mon Sep 17 00:00:00 2001 From: Keith Hall Date: Mon, 11 Jun 2018 17:20:53 +0300 Subject: [PATCH 5/6] simplify if statement --- src/parsing/parser.rs | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/src/parsing/parser.rs b/src/parsing/parser.rs index 3884ea22..c743b1d0 100644 --- a/src/parsing/parser.rs +++ b/src/parsing/parser.rs @@ -608,18 +608,16 @@ impl ParseState { MatchOperation::None => return false, }; for (i, r) in ctx_refs.iter().enumerate() { - let proto = if i == ctx_refs.len() - 1 && pat.with_prototype.is_none() { + let proto = if i == ctx_refs.len() - 1 { // a `with_prototype` stays active when the context is `set` // until the context layer in the stack (where the `with_prototype` // was initially applied) is popped off. - old_proto.clone() - } else if i == ctx_refs.len() - 1 { // if a with_prototype was specified, and multiple contexts were pushed, // then the with_prototype applies only to the last context pushed, i.e. // top most on the stack after all the contexts are pushed - this is also // referred to as the "target" of the push by sublimehq - see // https://forum.sublimetext.com/t/dev-build-3111/19240/17 for more info - pat.with_prototype.clone() + pat.with_prototype.clone().or(old_proto.clone()) } else { None }; From c0e1a2c08c8c7d83e688a0df6dcfaf6450a30235 Mon Sep 17 00:00:00 2001 From: Keith Hall Date: Tue, 12 Jun 2018 07:02:10 +0300 Subject: [PATCH 6/6] use closure for efficiency --- src/parsing/parser.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/parsing/parser.rs b/src/parsing/parser.rs index c743b1d0..62d6abfb 100644 --- a/src/parsing/parser.rs +++ b/src/parsing/parser.rs @@ -617,7 +617,7 @@ impl ParseState { // top most on the stack after all the contexts are pushed - this is also // referred to as the "target" of the push by sublimehq - see // https://forum.sublimetext.com/t/dev-build-3111/19240/17 for more info - pat.with_prototype.clone().or(old_proto.clone()) + pat.with_prototype.clone().or_else(|| old_proto.clone()) } else { None };