Allow negation of if let

The RFC for if let was accepted with the rationale that it lowers the boilerplate and improves ergonomics over the equivalent match statement in certain cases, and I wholeheartedly agree.
However, some cases still remain exceedingly awkward; an example of which is attempting to match the "class" of a record enum variant, e.g. given the following enum:
enum Foo {
    Bar(u32),
    Baz(u32),
    Qux
}

and an instance foo: Foo, with behavior predicated on foo not being Bar and a goal of minimizing nesting/brace creep (e.g. for purposes of an early return), the only choice is to type out something like this:
    // If we are not explicitly using Bar, just return now
    if let Foo::Bar(_) = self.integrity_policy {
    } else {
        return Ok(());
    }

    // flow resumes here

or the equally awkward empty match block:
    // If we are not explicitly using Bar, just return now
    match self.integrity_policy {
        Foo::Bar(_) => return Ok(());
        _ => {}
    }

    // flow resumes here

It would be great if this were allowed:
    // If we are not explicitly using Bar, just return now
    if !let Foo::Bar(_) = self.integrity_policy {
        return Ok(());
    }

    // flow resumes here

or perhaps a variation on that with slightly less accurate mathematical connotation but far clearer in its intent (you can't miss the ! this time):
    // If we are not explicitly using Bar, just return now
    if let Foo::Bar(_) != self.integrity_policy {
        return Ok(());
    }

    // flow resumes here

(although perhaps it is a better idea to tackle this from an entirely different perspective with the goal of greatly increasing overall ergonomics with some form of is operator, e.g. if self.integrity_policy is Foo::Bar ..., but that is certainly a much more contentious proposition.)

We've seen numerous proposals for this, so maybe review them before attempting to push any serious discussion.
If the variants contain data then you normally require said data, but you cannot bind with a non-match, so is_[property] methods are frequently best at covering the actual station.  If is_[property] methods do not provide a nice abstraction, then maybe all those empty match arms actually serve some purpose, but if not then you can always use a wild card:
let x = match self.integrity_policy {
    Foo::Bar(x) => x,
    _ => return Ok(()),
}

Also, one can always do very fancy comparisons like:
use std::mem::discriminant;
let d = discriminant(self.integrity_policy);
while d != discriminant(self.integrity_policy) {  // loop until our state machine changes state
    ...
}

I believe the only "missing" syntax around bindings is tightly related to refinement types, which require a major type system extension.  In my opinion, we should first understand more what design by contract and formal verification might look like in Rust because formal verification is currently the major use case for refinement types.  Assuming no problems, there is a lovely syntax in which some refine keyword works vaguely like a partial match.  As an example, let y = x? would be equivalent to
let Ok(y) = refine x { Err(z) => return z.into(); }

In this let Ok(y) = .. could type checks because refine returns a refinement type, specifically an enum variant type ala #2593.  I suppose refinement is actually kinda the opposite of what you suggest here, but figured I'd mention it.

I looked around for past proposals regarding expanding the if let syntax, starting with the original if let rfc and issues referenced in the comments before opening this.
@burdges Thanks for sharing that info, I have some reading to do. But with regards to

if is_[property] methods do not provide a nice abstraction, then maybe all those empty match arms actually serve some purpose

No, those are exactly what I would like, except they're not available for enum variants, i.e. for enum Foo { Bar(i8), Baz } there is no (automatically-created) Foo::is_bar(&self) method. It is in fact the absence of such an interface that required the use of if let or match.
One problem with the existing if let syntax is that it is a wholly unique expression _masquerading as an if statement. It isn't natural to have an ifstatement you can't negate - the default expectation is that this is a boolean expression that may be treated the same as any other predicate in anif` clause.

No, those are exactly what I would like, except they're not available for enum variants.

Oh, I think that can be fixed quite easily. They could be #[derive]d easily based on naming conventions. I'll try to write up an example implementation soon.

I'd suggest #[allow(non_snake_case)] for autogenerated one, so they look like
    #[allow(non_snake_case)]
    pub fn is_A(&self) { if let MyEnum::A = self { true } else { false } }

That said, if you have more than a couple variants then is_ methods might describe meaningful variants collections, not individual variants.  We're talking about situations where you do not care about the variant's data after all.
In fact, you'd often want the or/| pattern like
let x = match self.integrity_policy {
    Foo::A(x) | Foo::B(x,_) => x,
    Foo::C { .. } | Foo::D => return Ok(()),
    Foo::E(_) => panic!(),
}

I made a PoC implementation of the "generate is_XXX methods automatically" approach.

@H2CO3 This already exists on crates.io: https://crates.io/crates/derive_is_enum_variant and there's likely more complex variants too involving prisms/lenses and whatnot.
It seems to me however that generating a bunch of .is_$variant() methods is not a solution that scales well and that the need for these are due to the lack of a bool typed operation such as expr is pat (which could be let pat = expr... -- color of bikeshed...).
Fortunately, given or-patterns (https://github.com/rust-lang/rust/issues/54883) let chains (https://github.com/rust-lang/rust/issues/53667),  and a trivial macro defined like so:
macro_rules! is { ($(x:tt)+) => { if $(x)+ { true } else { false } } }

we can write:
is!(let Some(E::Bar(x)) && x.some_condition() && let Other::Stuff = foo(x));

and the simpler version of this is:
is!(let E::Bar(..) = something)
// instead of: something.is_bar()

@H2CO3

@Centril I'm glad it already exists in a production-ready version. Then OP can just use it without needing to wait for an implementation.

🎉

What do you exactly mean by this? AFAIK there aren't many kinds of enum variants in Rust. There are only unit, tuple, and struct variants. I'm unaware of special prism and/or lens support in Rust that would lead to the existence of other kinds of variants.

I found this a while back: https://docs.rs/refraction/0.1.2/refraction/ and it seemed like an interesting experiment; other solutions might be to generate .extract_Bar(): Option<TupleOfBarsFieldTypes> and then use ? + try { .. } + .and_then() pervasively. You can use .is_some() on that to get the answer to "was it of the expected variant".

I beg to differ. Since they are generated automatically, there's not much the user has to do… moreover the generated functions are trivial, there are O(number of variants) of them, and they can be annotated with #[inline] so that the binary size won't be bigger compared to manually-written matching or negated if-let.

In a small crate I don't think it would pay off to use a derive macro like this; just compiling syn and quote seems too costly to get anyone to accept it. Another problem with the deriving strategy is that when you are working on a codebase (say rustc) and you want to check whether a value is of a certain variant, then you first need to go to the type's definition and add the derive macro; that can inhibit flow.

Those are very nice, and seem fairly powerful. I would be glad if we could indeed reuse these two new mechanisms plus macros in order to avoid growing the language even more.

Sure; this is indeed nice; but imo, it seems natural and useful to think of let pat = expr as an expression typed at bool which is true iff pat matches expr and false otherwise. In that case, if !let pat = expr { ... } is merely the composition of if !expr { ... } and let pat = expr.

I'm for this. It arguably reduces the semantic complexity of the language, and improves consistency, especially with let-chaining coming in.
Has @rust-lang/libs thought of bundling the is! macro with the language, given how common it is?

@mark-i-m I dislike them when they're just sugar for !, but if there's a difference in what they do (like the unless block must be : !), then it might be plausible.  The negation, given the !-typed block, is consistent with things like assert!, so I don't think it's fundamentally confusing.

I feel like that would be a bit unexpected for most people. There is conceptually no reason unless foo {...} would not be a normal expression like if or match.

See https://github.com/rust-lang/rfcs/pull/1303

I feel like that would be a bit unexpected for most people. There is conceptually no reason unless foo {...} would not be a normal expression like if or match.

AFAIK nobody is confused about guard statements in Swift.

The usage of a pattern in if !let Foo::Bar(_) = self.integrity_policy is extremely confusing to me. What if I don't ignore the content but it use a name pattern? It wouldn't be visible in the block the follows as that is executed when the destructuring failed but the if also makes this a single pattern.
if !let Foo::Bar(policy) = self.integrity_policy else {
    // Wait, `policy` is not a name here.
    return Ok(policy);
}

Guard blocks as in #1033 don't have this problem.

@HeroicKatora I think you linked to the wrong RFC (I don't think you mean to link to supporting comments in rustdoc)

@KrishnaSannasi Indeed. Thanks.

I don't really like the different proposition of the if !let ..  or  if let Some(_) != ... they feel wrong to me but it still think there is a need for something like this.
Here my use case. it's a fake one but I have often encounter similar situation:
fn strange_function(real_unsecure_value:SomeType) -> u32
{
	let x = real_unsecure_value;

	if let Some(foo) = x.getFoo()
	{
		if let Some(bar) = foo.getBar()
		{
			if let Some(qux) = bar.getQux()
			{
				if qux.some_test()
				{
					return qux.some_calculus();
				}
				else
				{
					return 0;
				}
			}
			else
			{
				return 1;
			}
		}
		else
		{
			return 2;
		}
	}
	else
	{
		return 3;
	}
}

We can all agree this does not look pretty neither really readdable. What failed unwrapping does return 2 ? And we only have 4 tests. match will not really help us here.
Some general idea is fail fast. So instead of branching on success we do the opposite.
fn strange_function(real_unsecure_value:SomeType) -> u32
{
	let x = real_unsecure_value;

	if x.getFoo()     == None  { return 3; }
	let foo = x.getFoo().unwrap();
	if foo.getBar()   == None  { return 2; }
	let bar = foo.getBar().unwrap();
	if bar.getQux()   == None  { return 1; }
	let qux = bar.getQux().unwrap();
	if qux.someTest() == false { return 0; }

	return qux.some_calculus();
}

This is much more readable, but there is useless unwrapping in between.
Here what I propose :
fn strange_function(real_unsecure_value:SomeType) -> u32
{
	let x = real_unsecure_value;

	guard let Some(foo) = x.getFoo()    { return 3; }
	guard let Some(bar) = foo.getBar()  { return 2; }
	guard let Some(qux) = bar.getQux()  { return 1; }
	if qux.someTest() == false { return 0; }

	return qux.some_calculus();
}

I actually don't see why any special syntax is required here.
This is a perfectly valid C# code
int? obj = null;

if (!(obj is int i)) {
	// int j = i; // CS0165 Use of unassigned local variable 'i'
	return;
}
Console.WriteLine(i);

It's not very surprising that i is only allowed outside the block since you negated the condition. I think considering it confusing is a bit overthought.

I don't really like the different proposition of the if !let .. or  if let Some(_) != ... they feel wrong to me but it still think there is a need for something like this.
Here my use case. it's a fake one but I have often encounter similar situation:
fn strange_function(real_unsecure_value:SomeType) -> u32
{
	let x = real_unsecure_value;

	if let Some(foo) = x.getFoo()
	{
		if let Some(bar) = foo.getBar()
		{
			if let Some(qux) = bar.getQux()
			{
				if qux.some_test()
				{
					return qux.some_calculus();
				}
				else
				{
					return 0;
				}
			}
			else
			{
				return 1;
			}
		}
		else
		{
			return 2;
		}
	}
	else
	{
		return 3;
	}
}

We can all agree this does not look pretty neither really readdable. What failed unwrapping does return 2 ? And we only have 4 tests. match will not really help us here.
Some general idea is fail fast. So instead of branching on success we do the opposite.
fn strange_function(real_unsecure_value:SomeType) -> u32
{
	let x = real_unsecure_value;

	if x.getFoo()     == None  { return 3; }
	let foo = x.getFoo().unwrap();
	if foo.getBar()   == None  { return 2; }
	let bar = foo.getBar().unwrap();
	if bar.getQux()   == None  { return 1; }
	let qux = bar.getQux().unwrap();
	if qux.someTest() == false { return 0; }

	return qux.some_calculus();
}

This is much more readable, but there is useless unwrapping in between.
Here what I propose :
fn strange_function(real_unsecure_value:SomeType) -> u32
{
	let x = real_unsecure_value;

	guard let Some(foo) = x.getFoo()    { return 3; }
	guard let Some(bar) = foo.getBar()  { return 2; }
	guard let Some(qux) = bar.getQux()  { return 1; }
	if qux.someTest() == false { return 0; }

	return qux.some_calculus();
}

I know someone who would like to have monads in Rust… :trollface:

Since it's bikeshedding time, I would like a blue one!
fn strange_function(real_unsecure_value:SomeType) -> u32
{
	let x = real_unsecure_value;

	let Some(foo) = x.getFoo()   otherwise { return 3; }
	let Some(bar) = foo.getBar() otherwise { return 2; }
	let Some(qux) = bar.getQux() otherwise { return 1; }
	if qux.someTest() == false { return 0; }

	return qux.some_calculus();
}

It can also be chained easily
fn strange_function(real_unsecure_value:SomeType) -> u32
{
	let x = real_unsecure_value;

	let Some(foo) = x.getFoo() && Some(bar) = foo.getBar() otherwise { return 1; }

	return bar.some_calculus();
}

In practice, your function always returns -> Result<u32,u32> so the idiomatic solution is .ok_or(1)? or .ok_or_else(..)?.
Also try blocks or nested fns give precisely your style but with fewer bugs thanks to #[must_use].
#![feature(try_blocks)]

fn strange_function(real_unsecure_value:SomeType) -> u32
{
        try {
            let qux = real_unsecure_value
                .getFoo().ok_or(3)?
                .getBar().ok_or(2)?
                .getQux().ok_or(1)?;
            if qux.someTest() { 0 } else { qux.some_calculus() }
        }.map_or_else(|x| x, |x| x)
}

At present, rustfmt cannot correctly group long method chains like this, but either do not use rustfmt, or else if you mut use rustfmt then use temporaries, ala let foo = ..; let bar = ..;.

@burdges That's great to know, thanks.
As a side node, if you want syntax coloration in your snippet, you can do it like so:
    ``` rust
     // some rust code
    ```

@phaazon I cannot be the only one 😊
@burdges I like your .or_ok(_) but syntaxically speaking I dont see the point on have the try. Is it just a compilation or parsing stuff that I don't see or is there more to know ?

All those ? exist the try block early with different values, but actually you're correct that .and_then accomplishes this on stable:
fn strange_function(real_unsecure_value:SomeType) -> u32
{
    real_unsecure_value.getFoo().ok_or(3)
        .and_then(|foo| foo.getBar().ok_or(2))
        .and_then(|bar| bar.getQux().ok_or(1))
        .and_then(|qux| if qux.someTest() { 0 } else { qux.some_calculus() })
        .unwrap_or_else(|x| x)
}

You might however want the try with ? if you've convoluted logic inside.. or just because you like monads.

What's about performing some specific logic?
if !let Some(f) = get_some_int() {
   f = 10;
}
println!("I've got {}", f);
if !let Some(x) = another_maybe_int() {
    println!("Something bad happened");
    return 20;
}
f + x

Anyway, combinators are really a mess, especially when you add lifetimes and generics in mixin. This is why people are so exited to hear about async/await. And this is why and_then chains are not usable comparing to if let chaining.

It becomes cleaner with combinators
let f = get_some_int().unwrap_or(10);
println!("I've got {}", f);
f + another_maybe_int().unwrap_or_else(|| {
    println!("Something bad happened");
    20
})

Also, your syntax should never work because f has an ambiguous binding, which requires being both moved and not moved.

And here you introduced a bug. Previosely if another_maybe_int returned None then function result were 20, but not it's f + 20 instead.

@Pzixel no, @burdges's combinators do the same as your if !let. You also return 20 on None

I just ran into this today; I am using char_indicies, and don't care about the index, only want to match if the char is not a specific one.

I think that having an if in an expression that can not be negated nor chained is misleading for many programmers coming from other major languages.
As @mqudsi said,

One problem with the existing if let syntax is that it is a wholly unique expression masquerading as an if statement. It isn't natural to have an if statement you can't negate - the default expectation is that this is a boolean expression that may be treated the same as any other predicate in an if clause.

But this is changing - as some have mentioned above -   the let- and while-let chaining is coming, and it allows the if let to behave a bit more like most people could expect : like a boolean.
From the Rust by Example book :
// For some use cases, when matching enums, match is awkward. For example:
match optional {
    Some(i) => {
        println!("This is a really long string and `{:?}`", i);
        // ^ Needed 2 indentations just so we could destructure
        // `i` from the option.
    },
    _ => {},
    // ^ Required because `match` is exhaustive. Doesn't it seem
    // like wasted space?
};

if let is cleaner for this use case and in addition allows various failure options to be specified

So it looks like the main intent of this feature was to make code easier to read. Without the ability to negate the if let, we have to use :
if let Some(i) = optional {} else {
   // The "negation" is now in this strange place, and once again we have an empty block
    ...
}

This syntax is clearly not ideal, as was originally using a match expression with an empty block ; as such I think that it would be a great thing to allow "if let" negation.

I think .is_none() etc. has already become idiomatic, so you cannot make examples with Option or Result without then arguing to remove .is_none() etc.  You need examples with other enums.

@burdges this example is completely fine:
if let Some(i) = optional {} else {
   panic!("Very very bad optional");
}
// how do I get access to `i` without unwraps here?

Is this issue also appropriate to discuss the negation of while let?
I just ran into the following today (conceptually). I want to let the user edit
a file and then process it, returning a result. The processing can fail if the
file is not valid in some way.
I would like to write
edit(file);

while !let Ok(x) = process(file) {
    edit(file);
}

// use `x`

But due to the lack of while-not-let, I have to duplicate the processing lines
and unwrap the result:
edit(file);

let mut y = process(file);
while y.is_err() {
    edit(file)
    y = process(file);
}

let x = y.unwrap();

// use `x`

I also thought of the following, but I'm not sure it's any better.
let mut x;

loop {
    edit(file);

    if let Ok(y) = process(file) {
        x = y;
        break;
    }
}

// use `x`

@dkasak you can rewrite it as following
let x = loop {
    edit(file);

    if let Ok(y) = process(file) {
        break y;
    }
};

@Pzixel Ah thanks, I forgot that loop can break with value. But I agree, while !let is still clearer.

let x = repeat(()).filter_map(|_| {
    edit(file);
    process(file).err()
}).next().unwrap();

or
let mut x;
while {
    edit(file);
    x = process(file);
    x
} { }

I think matches macro covers the case somewhat:
if !matches!(Foo::Bar(_), self.integrity_policy) {
    return Ok(());
}

I personally prefer
if let pattern != expr {
    expr2
} else {
    expr3
}

which we can desugar to
match expr {
    pattern => expr3,
    _ => expr2,
}

(Of course we could omit the else block so expr3 is ())
Compared to changing let to a boolean expression so we can use !let, or use matches!, this has an advantage that we could use bindings in pattern in the else block, and it is very easy to implement in rustc.

As a user,
if let pattern = expr {

is already mildly confusing.
Taking it to
if let pattern != expr {

is just asking for a lot of confused users.

my two cents, = is assignment; != is inequality so != doesn't make sense at all

How about adding a not keyword there. Binding has to be forbidden I guess.
if not let Foo::Bar(_) = self.integrity_policy {
    return Ok(());
}

That's not very useful. As I said I need bindings outside it. For example:
fn get_sum(maybe_x: Option<i32>, maybe_y : Option<i32>) : Result<i32, &'static str) {
  if not let Some(x) = maybe_x {
    return Err("Bad x");
  }
  if not let Some(y) = maybe_y {
    return Err("This time something wrong with y");
  }
  return Ok(x + y);
}

But you can just use comibna..

Yes, I can use combinators, but you can also write any code with results using and_then only, but there is areason why try operator exist.
Negative bound allows to create much clearer happy paths with early returns, when passed check binds unwrapped value to something. I have tons of this code in my C# codebase which allows it.

I didn't realize that the binding can be used afterwards. Agree with that.

Just for reference. The following works if you just want to return an error
fn get_sum(maybe_x: Option<i32>, maybe_y: Option<i32>) -> Result<i32, &'static str> {
    let x = maybe_x.ok_or("Bad x")?;
    let y = maybe_y.ok_or_else(|| {"This time something wrong with y"})?;
    return Ok(x + y);
}

I would suggest supporting (looks best)
if let Foo::Bar(_) != self.integrity_policy {
        return Ok(());
    }

I came across the same situation where I had to write an if statement that flows into the if scope when a certain value IS NOT Foo:Bar, wished I could write !=

@rudolfschmidt See the comments above. That syntax does not make semantic sense, and it's confusing.

my two cents, = is assignment; != is inequality so != doesn't make sense at all

!= express inequality and the semantic behind if let Some(x) != var {} would be that var is not the pattern "Some(x)".
if !let looks ugly because its easy to oversee the ! before the let.

@rudolfschmidt See the comments above. That syntax does not make semantic sense, and it's confusing.
I have read the thread and do not see any good argument against if let !=, if you referring to something please quote it for me.

One critique on rust that Golang users like to use is the multiple ways to write rust code. Even I disagree it because rust has many more features than go (probably will ever have) there is something true behind this critique. Rust should avoid to introduce new keywords like not or unless and try to use existing keywords unless there is a really good reason to not do so. Introducing new operators and keywords would make the language more complex and more difficult to learn. I had my struggles to learn the ownership model so Rust is complex enough and does not need more to learn.
Rust is not Python and Python'ers should not try to introduce their concepts into Rust.

See https://github.com/rust-lang/rfcs/issues/2616#issuecomment-645347572

= is assignment; != is inequality so != doesn't make sense at all

I personally prefer
if let pattern != expr {
    expr2
} else {
    expr3
}

which we can desugar to
match expr {
    pattern => expr3,
    _ => expr2,
}

(Of course we could omit the else block so expr3 is ())
Compared to changing let to a boolean expression so we can use !let, or use matches!, this has an advantage that we could use bindings in pattern in the else block, and it is very easy to implement in rustc.

It is generally regarded as an antipattern to have the negation condition in the if block if you have an else block.
Other than that, it seems matches! already solves the whole need of the RFC. Now that it has been stabilized, is there still a need for this RFC to get merged?

@SOF3  matches! also solves if let x = y in the same manner so we can deprecate it either? Or you can see that matches's ergonomic is much worse. You can rewrite every if let with match although the former exists in the language for some reason.

You can rewrite every if let with match

No, if let supports bindings.
But I agree that the ergonomics are not great with !matches

Could you provide any example of if let that cannot be rewritten as match?

if let Some(b) = buf_b.as_ref() {
    let b = b.clone();
    drop(buf_b);
    observer(ctx, &func.call_mut(ctx, &(a.clone(), b)));
}

here b is being bound. Of course you can assign it to a variable manually in the body of the if block, but we can see that if let allows more terse code.

how is that different of
match buf_b.as_ref() {
  Some(b) => {
    let b = b.clone();
    drop(buf_b);
    observer(ctx, &func.call_mut(ctx, &(a.clone(), b)));
  },
  _ => {}
}

?

@Pzixel If you read the original RFC for if let it's designed to solve exactly this case. Removing boilerplate code.

I agree that if let is terser. I just said it completely support all features if let has like binding or anything else.

You can rewrite every if let with match

No, if let supports bindings.
But I agree that the ergonomics are not great with !matches

Do you mean !matches!()? Can you explain what the problem is, other than the potentially confusing ! operator (which applies to negation of all Boolean expressions in general)?

The critical thing that almost any approach using match lacks is the ability to extend a binding into the outer scope, and therefore avoid indentation.
If you're performing validation on some type that is four nested layers deep, for instance, with if left it ends up looking like this:
if let Some(a) = foo() {
  if let Some(b) = a.b {
    if let Some(c) = b.c {
      return Ok(c);
    } else {
      return Err("no c");
    }
  } else {
    return Err("no b");
  }
} else {
  return Err("no a");
}

This requires both a reader and writer of the code to keep track of the various levels of nesting and what condition was checked at each stage. It is quantifiable more difficult to work with code that has deep nested logic like this; it requires keeping more state in your head as you read and it will slow development down.
With enough layers of nesting (and anyone who doesn't believe that they can get this deep should look at rustc source), you make more readable code by ignoring the ergonomic convenience of pattern destructuring:
let f = foo();
if f.is_none() {
  return Err("no a");
}
let a = f.unwrap();
if a.b.is_none() {
  return Err("no b");
}
let c = a.b.unwrap();
// etc.

This code is far easier to work with. But better still would be:
unless let Some(a) = foo() {
  return Err("no a");
}
unless let Some(b) = a.b {
  return Err("no b");
}
// etc.

I used unless just because I think it's more readable than if !let, but I don't really care what colour the shed is.
However, I realized today, and the reason I'm poking in, is that I think it's maybe doable if you allow the use of the never type as a way to statically assert that a block can never return control flow normally.
Specifically, a macro where unless_let!((Some(a), Some(b)) = foo; { return; }) is expanded out to something like this: https://play.rust-lang.org/?version=nightly&mode=debug&edition=2018&gist=a9a29e6449207f569d52c1a43f9275fc
Unfortunately it requires a proc macro to get the extra names to bind to, but otherwise I think it could work pretty well.

However, I realized today, and the reason I'm poking in, is that I think it's maybe doable if you allow the use of the never type as a way to statically assert that a block can never return control flow normally.

I'm not sure if we should force "never return". We could support, for instance
let Some(a) = foo() else {
    a = 1234;
}
// meaning: let a = foo().unwrap_or(1234);
use_(a);

If we don't diverge or assign a inside the else branch, all future usage will just cause the "use of uninitialized variable" error, so no additional check is necessary.

Unfortunately it requires a proc macro to get the extra names to bind to, but otherwise I think it could work pretty well.

The biggest problem is how do you determine whether an identifier is a name or a constant.
#[cfg(wut)]
const B: u64 = 1u64;

let (Some(A), B) = foo() else { return };
dbg!(A, B);

For the first comment, doing alternative bindings like that is definitely
cool, but keeping it simple to start with is good, I think.
If we want to go further down the rabbit hole, I could even imagine, say,
^ being a pattern metacharacter that binds into an outer scope:
if !let Some(^a) = ... {
} else {
  let ^a = ...;
}

No reason this couldn't work if you were doing bindings outside the if let, too.
And this isn't even its final form:
'label: {
  if foo {
    if bar {
      let ^'label a = 1;
    }
  //...
  }
}

For the second, I have no answer :(. The macro could accept a metatoken to
denote a constant, perhaps, to allow the user to disambiguate.

The macro could accept a metatoken to
denote a constant, perhaps, to allow the user to disambiguate.

Well we do have const in pattern, but that means we need to add it on everything variable-like even including None 🤔
let (Some(a), const { None }) = ...

We have const in patterns? I can't find any reference to it anywhere...
I implemented this over the weekend as a proof-of-concept, requiring the use of a@_ to disambiguate to the macro's parser that you are doing a binding and not a const. Not great, but it works. I'll look into posting it soon.
Meanwhile, an alternative syntactic suggestion from @strega-nil, that I quite like:
let Some(a) = opt else {
  return err;
}

I like this one in that it works naturally with chaining, too:
let Some(a) = opt &&
    Some(b) = a.field else {
 return err;
}

@alercah Here's the RFC that contains const in patterns: https://github.com/ecstatic-morse/rfcs/blob/inline-const/text/0000-inline-const.md#patterns

can we close this now, given #3137?

That seems reasonable. The use case likely still exists, but is drastically reduced at this point.

The usecases outlined here get even worse with while lets. Do they have any #3137 counterpart?

The usecases outlined here get even worse with while lets. Do they have any #3137 counterpart?

You can trivially implement them as a macro by example using let..else

@goose121 Yes, this would work, but this is anything but "trivial". I'd rather rewrite the whole loop and the code around it. Using a macro makes such code a lot less readable and doesn't benefit from language orthogonality.
If I have a feature that negates if let then I can apply the same intuition to while let and don't have to spend brain cycles on it too much. If I need to use a macro, I need to know that particular implementation of that macro, remember how to use it, etc.

Looks like this idea is implemented via let-else expressions, which had just landed on stable.
Maybe we can close this issue?

came here, suprised this does not work^^
I actually need while ! let in my case, but it coilds down to the same thing.
In my usecase I am polling a process that I launched. If poll does not return the exitcode yet, I capture the output and forward it to my gui, if it is finished I want to move on.

Another option is while !matches!(expression, pattern)

Isn't it generally considered a bad code style to negate the condition of if if an else exists?

Isn't it generally considered a bad code style to negate the condition of if if an else exists?

I would say this really depends on the use case. I e.g. did not want a plain if but a condition in a while loop.
My use-case is polling a popen-object until it returns a Some() (indicating the process has terminated).

Isn't it generally considered a bad code style to negate the condition of if if an else exists?

I would say this really depends on the use case. I e.g. did not want a plain if but a condition in a while loop.
My use-case is polling a popen-object until it returns a Some() (indicating the process has terminated).

Is the object being polled an Option?

yup, subprocess crate:

I tried to play around with your use case a bit and I ran into a few "challenges".
Here is the code I used to play with the idea (let me know if I misunderstood).
playground link
fn poll(step:u8) -> Option<u8>{
    if step > 5{
        Some(0)
    } else {
        None
    }
}

fn main() {
    let mut step = 1;
    loop{
        if let Some(exit_code) = poll(step){
            println!("Exiting with exit code: {exit_code}");
            break;
        }
        println!("Step: {step}");
        step += 1;
    }
    println!("Done");
}

First off I can see why you would want to put it in a while loop but I also couldn't find a way to do it, that allowed me to still find out what the exit code was.
If you didn't need the exit code you could just do the following:
playground link
fn poll(step:u8) -> Option<u8>{
    if step > 5{
        Some(0)
    } else {
        None
    }
}

fn main() {
    let mut step = 1;
    while poll(step).is_none(){
        println!("Step: {step}");
        step += 1;
    }
    println!("Done");
}

I eventually need the code.
So what I do now is I just loop {} and then make do match and break.
This is more cumbersome than it needs to be but it works:

We kinda have this as a combination of let … else and !matches!(…) now. let … else supports bindings and !matches!(…) works for cases without bindings.


Need to add third button ('Cancel') to confirm dialog

Hello Guys,
Could you please tell me how I can extend typical confirmation dialog in Serenity? In my case need to add third button - 'Cancel' (like it is on the picture below) - instead of x sign in right top corner of window? Could you please give me a piece of advice how to do this action in some simple way?
Thanks.

Cancel is already Close button. If you have to this design, use jQuery ui dialog docs to write your own dialog, or use another bootstrap dialog

OK, Volkan, I've got your point of view. Not an issue and not related to framework. Closed.


TypeScript tries to compile @link tagged comments and occurs error

Bug Report
⏯ Demonstration Link

Repository: https://github.com/samchon/report-comment-compile-error
Github Actions Result: https://github.com/samchon/report-comment-compile-error/actions/runs/4245549822/jobs/7381209175

💻 Code
import { IBbsArticleSnapshot } from "./IBbsArticleSnapshot";
import { IBbsComment } from "./IBbsComment";

/**
 * Article of BBS.
 *
 * Note that, main content of article is stored in {@link snapshots} property.
 * The {@link IBbsArticleSnapshot} records would be accumulated whenever updating
 * current article. It is to keep the history of article.
 *
 * In the same reason, {@link IBbsComment} also stores {@link IBbsCommentSnapshot}
 * records whenever update it.
 *
 * -------------------------------------------
 *
 * Let's link that never exists
 *
 *   - {@link NeverExistDto}
 *   - {@link Nothing}
 *   - {@link NoWhere}
 *   - {@link NoOne}
 *
 * @author Samchon
 */
export interface IBbsArticle {
    ...
}
export namespace IBbsArticle {
    // THIS IMPORT STATEMENT OCCURS BUG
    export namespace Comment {
        export import IStore = IBbsComment.IStore;
    }
}

🙁 Actual behavior
I succeded to reproduce https://github.com/microsoft/TypeScript/issues/49706 bug. The bug was not occured by Korean characters. The error was occured by type import statement in a namespace declaration that has same name with a interface type.
When there's an interface that declared namespace with same name, and imports another type in the namespace. Also, the error occurs only when importing TypeScript file (await import("file") statement) after composing ts.createProgram() with ts-node.
I can't sure that this is a bug of TypeScript or ts-node. Therefore, I report this issue both in TypeScript and ts-node.
🙂 Expected behavior
No error by @link tagged comments.

 Running tsc there, I don't see any errors.


Move from lift to prompts

This supersedes #456.
This is WIP and probably @phischu wants to take over.

Current status: the only big thing missing is mutable state. @serkm maybe this branch is now a good starting point for you. How would you implement regions now on top of this? We certainly need to change the representation in machine.

Looks like there is still some memory leak... Otherwise this PR is ready to be discussed.

This PR is now ready for review.


Babylon File Loader Waiting Data Fixes

Note: No Need For What New Entry

Yo @deltakosh ... I see you changed all the waiting stuff to _waitingData and now the lods are located on _waitingData.lods
This fixes the babylonFileLoader loadDetailLevels function :)

I missed it because of the any :)
thanks!

I thought as much... so i also changed is to AbstractMesh :)


Add heterogeneity_model checks

Description
Add validations fot heterogeneity_model in causal manager.
Areas changed

npm packages changed:

[ ] responsibleai/causality
[ ] responsibleai/core-ui
[ ] responsibleai/counterfactuals
[ ] responsibleai/dataset-explorer
[ ] responsibleai/fairness
[ ] responsibleai/interpret
[ ] responsibleai/localization
[ ] responsibleai/mlchartlib
[ ] responsibleai/model-assessment

Python packages changed:

[ ] raiwidgets
[x] responsibleai
[ ] erroranalysis
[ ] rai_core_flask

Tests

[ ] No new tests required.
[x] New tests for the added feature are part of this PR.
[ ] I validated the changes manually.

Screenshots (if appropriate):
Documentation:

[ ] My change requires a change to the documentation.
[ ] I have updated the documentation accordingly.

Codecov Report

Merging #1210 (ad73fc6) into main (23435ce) will increase coverage by 8.63%.
The diff coverage is n/a.

@@            Coverage Diff             @@
##             main    #1210      +/-   ##
==========================================
+ Coverage   66.94%   75.58%   +8.63%
==========================================
  Files          91       10      -81
  Lines        4359      258    -4101
==========================================
- Hits         2918      195    -2723
+ Misses       1441       63    -1378

Flag
Coverage Δ

unittests
75.58% <ø> (+8.63%)
:arrow_up:

Flags with carried forward coverage won't be shown. Click here to find out more.

Impacted Files
Coverage Δ

...ai/responsibleai/_tools/causal/causal_constants.py

...onsibleai/responsibleai/managers/causal_manager.py

erroranalysis/erroranalysis/report/error_report.py

...ibleai/responsibleai/managers/explainer_manager.py

...ponsibleai/responsibleai/_managers/base_manager.py

responsibleai/responsibleai/utils.py

responsibleai/responsibleai/_data_validations.py

...ranalysis/erroranalysis/analyzer/error_analyzer.py

...idgets/raiwidgets/responsibleai_dashboard_input.py

erroranalysis/erroranalysis/_internal/constants.py

... and 71 more

Continue to review full report at Codecov.

Legend - Click here to learn more
Δ = absolute <relative> (impact), ø = not affected, ? = missing data
Powered by Codecov. Last update 23435ce...ad73fc6. Read the comment docs.


#6745 password beakerx widget

hitting the enter key with the cursor in this field did not submit the form.


wrong dragged item position when backdrop-filter blur is set

First of all, thanks for that amazing library, it made my life easier when I started using it compared to other popular solutions.
I found a weird behavior that happens when a parent of SortableList has a backdropFilter set (in my case it was blur).
<div
  style={{
    backgroundImage: "url('https://source.unsplash.com/czOuPVsfHDw/900x900')",
  }}
>
  <div
    style={{
      maxWidth: 400,
      margin: "30px auto",
      padding: 64,
      backdropFilter: "blur(8px)",
    }}
  >
    <SortableList
      items={items}
      onChange={setItems}
      renderItem={(item) => (
        <SortableList.Item id={item.id}>
          {item.id}
          <SortableList.DragHandle />
        </SortableList.Item>
      )}
    />
  </div>
</div>

Here's a reproduction on CodeSandbox
You can see that the dragged element is not positioned correctly, I also noticed that its shift depends on the viewport size (by resizing the window for example).
A workaround I found was to apply the backdrop-blur on a pseudo-element (used tailwind for convenience):
...
 <div
   className="relative before:absolute before:inset-0 before:backdrop-blur-xl"
    style={{
      maxWidth: 400,
      margin: "30px auto",
      padding: 64,
    }}
  >
...

I just wonder why a such thing happens and how to make it work properly.

I'm experiencing the same issue. Applying backdrop-blur causes the overlay to be translated.


Clean up user/table creation/cleanup in AccumuloClusterHarness

#1164  - Update the harness to create "test unique" users.prefixed with something (currently the tables are prefixed with the method name) so that each test can perform proper cleanup when finished.   The current  deleteusers code in the harness look for users prefixed with the method name to delete but that is not how they are created so the deletion does not work.
@keith-turner commented that the class name as a prefix is confusing because with inheritance and multiple classes its hard to know if you got it right. Maybe just use a constant like
public static final String HARNESS_USER_PREFIX="a1b1ae6bfef2ca11d92b86ab60930b3c56971f6e"
Then all code can just use that constant. I picked the latest commit hash on the master branch for the value. The constant could also be a classname, it does not really matter what it is as long its somewhat unique.
When this is completed all ITs implementing this class that use getUser(int) can remove all explicit user delete code.

After looking further into this they "whys" of how things are being done in MINI vs STANDALONE are too unclear - why can't STANDALONE create users the same way as MINI vs adding them to the config file?
Therefore I am deleting this ticket and leaving it up to resolutions made in ticket #1074

Closing issue, upon recommendation of the reporter.


Web - Some characters overlaps the red dot underlining in the "Display Name" Input field.

If you haven’t already, check out our contributing guidelines for onboarding and email<EMAIL_ADDRESS>to request to join our Slack channel!

Action Performed:

Open Safari in  full screen mode & login to app
Go to settings
Click  on Profile
Click on Display name
Type random characters which contain  "g" & "y"  in First Name & Last Name input field,  eg: fsfsfsyyyggfsfyygg
Observe that, letter "g" & "y" overlaps the red dotted underlining

Expected Result:
Characters shouldn't overlaps the red dot underlining
Actual Result:
Letter "g" & "y" overlaps the red dotted underlining
Workaround:
Unknown
Platforms:

Which of our officially supported platforms is this issue occurring on?

[ ] Android / native
[ ] Android / Chrome
[ ] iOS / native
[ ] iOS / Safari
[x] MacOS/ Safari
[ ] MacOS / Desktop

Version Number: 1.3.40-5
Reproducible in staging?: n/a
Reproducible in production?: n/a
If this was caught during regression testing, add the test name, ID and link from TestRail:
Email or phone of affected tester (no customers):
Logs: https://stackoverflow.com/c/expensify/questions/4856
Notes/Photos/Videos: Any additional supporting documentation
https://github.com/Expensify/App/assets/93399543/ff7b01ea-4d30-4968-8472-d694eca1373a

Expensify/Expensify Issue URL:
Issue reported by: @lincolnb17
Slack conversation: https://expensify.slack.com/archives/C049HHMV9SM/p1689404784439189
View all open jobs on GitHub

I think the solution here might be to disable css property autoCorrect for fields First name and Last name in the code here and here. But this would remove the red dotted lines entirely.

Contributor details
Your Expensify account email<EMAIL_ADDRESS>Upwork Profile Link: https://www.upwork.com/freelancers/~01e83985ab636b50c5

Proposal
Please re-state the problem that we are trying to solve in this issue.
Remove the issue of red-dotted line getting overlapped with letters
What is the root cause of that problem?
The problem is due to letter 'g' and 'y' ending tails getting overlapped with the line
What changes do you think we should make in order to solve the problem?
Give a space between the line and the characters
What alternative solutions did you explore? (Optional)
Move the letter 'g' and 'y' above a little using css so they don't overlap

I commented in Slack but I kind of think we shouldn't worry about this, I have a feeling it's a native thing and not something we'd control anyways.

Cool, that works for me if you're happy. I agree it's not an "us" problem, you can see it elsewhere on like Github for example:

Anyhow, closing then!


"Alimpești" is listed in the drop-down menu

Description:
"Alimpești" is listed in the drop-down menu alongside its corresponding county and commune, according to the law
Precondition:
The website is up an running.

Step 1
Type "Alimpești" in the search bar.

Expected results
The location is listed in the drop-down menu as "Comuna Alimpești, GORJ".


Enterprise Library ，Unity and AntiXss support net core?

AntiXss
patterns & practices–Enterprise Library
patterns & practices -Unity

This issue is being closed because it has not been updated in 3 months.
We apologize if this causes any inconvenience. We ask that if you are still encountering this issue, please log a new issue with updated information and we will investigate.


JSON module producing negative numbers

Hello,
I've noticed that the JSON output module is producing negative numbers for the following fields whenever the MSB is one: saddr-raw, daddr-raw, seqnum, acknum. This is probably due to reading these as signed rather than unsigned integers. At first I thought this was the intended behavior, but the CSV module is outputting unsigned integers which makes more sense. Is it possible to make the behavior consistent? This is also creating issues with other software that read from ZMap (see https://github.com/stanford-esrg/lzr/issues/5). I'm using ZMap version 2.1.1 on Ubuntu 18.04.

Can you provide an example? The code looks right at a glance, and fields are output correctly when I run ZMap locally on Ubuntu. If this was an integer signing problem, I would expect half of the records to come out negative, and I just dumped 100,000 records without a single negative value output.

When I run zmap -p 80 -N 10 -f saddr-raw,daddr-raw,seqnum,acknum -O json I see lots of negative numbers. If you are not seeing the same thing then it's probably limited to my system. I'll track down the issue when I get a chance and report back if it has something to do with ZMap.

This can happen if you pipe through jq or certain other JSON pretty-print software.

I was reading the raw output from ZMap. In any case, compiling the latest version fixed the issue for me (I had installed using apt before), so I'm closing the issue.


KNOCK HARD to skip the first area

This can be written in a config file or something?
It is also clued once you reach the second area.

I let the player know when they get to the end that they can skip with KNOCK HARD. Maybe I will do the config file later.


adding seed as a param

Hello, currently when fetching datasets from openml, the dataset is split into train and test using a fixed seed 0 - if one re- does an AutoML experiment several times, it would be useful if the seed could be set each time separately.

Try again? Pre-commit didnt work


Bug: race condition could lead to lost writes

I believe there's a race condition in Graveler, that in some cases could lead to lost writes.
This is a design flaw with graveler.Set():
As it is currently implemented, it relies on two independent DB connections:

one that tries to ensure mutual exclusion between writing to a branch, and changing it (for example, a commit will delete all records with the branch's current staging token, replacing it with a new one).
one that, once acquired, relies on said mutual exclusion to read the branch's current staging token, and add records to it.

Since these are two separate mechanisms, lock safety is not guaranteed, especially in the face of a connection error or reset: the use of pg_advisory_xact_lock_shared as the locking mechanism, ties the lock's liveness to the life of the PostgreSQL transaction.
This means that the branch locker is essentially broken: it can not guarantee there are no modifications to a branch while the callback is running, which will in this case, result in writes being lost, as illustrated below:

While this is a possible scenario, it relies on 2 rather uncommon events happening at the same time:

a network failure (or some other reason causing the Postgresql server to terminate the locker's transaction) but one that does not affect the successful completion of the writer's transaction underlying connection
simultaneously, a commit (or another event that mutates the branch's staging token) must begin after the writer function has read the current staging token for the branch.

While this is (arguably?) a rare scenario, it violates a basic guarantee provided by lakeFS.

After F2F discussions I agree that this scenario is possible.
In general, as soon as we have 2 transactions, the onus of proving consistency is on us.  I propose that we change the commit/write transaction to be safe, and use the separate locking transaction purely in order to give the desired blocking semantics.
One way to do this (suggested by @ozkatz) is to use the SERIALIZABLE isolation level.  That will work, but the performance implications will be unpleasant.  We could do it with just REPEATABLE READ semantics (the default isolation level, so no overhead in the uncontended case!).  Wrap every mutation operation (write or commit) with a read of the staging token.  Fail if the before and after tokens don't match.
BEGIN;
SELECT before_token FROM get_staging_token_for_branch();
-- SQL commands;
SELECT after_token FROM get_staging_token_for_branch();
-- ABORT unless before_token = after_token;
COMMIT;

Fail if the before and after tokens don't match.
@arielshaqed yep, so another change we'll have to make, unless we want to modify behavior - is to retry this in a blocking loop somewhere else in the stack.

Fixed with the move to kv.


How to read a boolean column in C++

Hey, I was wondering what's the correct way to read a boolean value from an arrow::Array?
For int64_t types and string types we were able to figure out how to read them (example below), however for  boolean we're unsure how to read them correctly and how they're encoded. Is it individual bits in the whole array->GetValues() returned data?
// std::shared_ptr<arrow::Array> array;

// int64
const int64_t* view = array->GetValues<int64_t>(1,0);

// strings
const int32_t *offsets = array->GetValues<int32_t>(1, 0);
const char    *view  = array->GetValues<char>(2, 0);

It would also be nice to have that information available in the docs.

In all cases, you may prefer to downcast to the concrete Array subclass and use accessor methods. In this case that would be Int64Array, StringArray, and BooleanArray. BooleanArray has a Value() method that gets the value at a particular index, and of course all arrays have IsValid/IsNull methods.
Indeed, the encoding of a Boolean array is one boolean per bit. You can see this in the definition of BooleanArray::Value above. You'll have to take into account the offset as well in case the array is a slice. (Hence the zeroth value in an array lies at the offset + 0th bit.)

Ok, thanks!


File operations fail when folder or file name contains parentheses

Describe the bug
File operations (sync, put, get) fail when a folder or file name contains parentheses, although these are valid *nix file and folder names.
To Reproduce
I discovered this whilst attempting to sync a folder containing a Calibre ebooks library.  Calibre uses the following on-disk structure throughout the library:  "a_calibre_library/author name/book title (1)" where the number in parenthesis is an integer that is auto-incremented for each new book.
A little testing showed that more generally, any folder or file containing parenthesis causes file operations to fail.
Create example directory tree:
mkdir -p "./testdir1/folder_with_parens_(foo)"
touch "./testdir1/folder_with_parens_(foo)/file_without_parens.txt"

Write a deploy containing the following operation:
files.sync(src="testdir1/", dest="/tmp/testdir1")

Result (excerpt from pyinfra output with -v):
--> Preparing operations...
Loading: deploy.py
Loaded fact link (path=/tmp/testdir1)
Loaded fact directory (path=/tmp/testdir1)
[testhost] sh: 1: Syntax error: "(" unexpected
[testhost] Error: could not load fact: directory (path=/tmp/testdir1/folder_with_parens_(foo))
Loaded fact directory (path=/tmp/testdir1/folder_with_parens_(foo))
--> An unexpected exception occurred in: deploy.py

The same occurs for files.put() - (tested).  Presumably files.get() would fail similarly, though not tested.
Expected behavior
Files and/or folders sync/put/get without error
Meta

Include output of pyinfra --support.
--> Support information:
If you are having issues with pyinfra or wish to make feature requests, please
check out the GitHub issues at https://github.com/Fizzadar/pyinfra/issues .
When adding an issue, be sure to include the following:
System: Linux
Platform: Linux-5.8.0-53-generic-x86_64-with-glibc2.31
Release: 5.8.0-53-generic
Machine: x86_64
pyinfra: v1.4.2
Executable: /path/to/bin/pyinfra
Python: 3.9.2 (CPython, GCC 9.3.0)

How was pyinfra installed (source/pip)?

pip, into a virtualenv

Include pyinfra-debug.log (if one was created)

No log

Consider including output with -vv and --debug.

Didn't do this.
Workarounds
files.rsync() succeeds as a workaround, though doesn't work as a solution for single file operations.
More generally, I can fix the issue entirely for all the operations by applying the following hacks to "pyinfra/api/connectors/util.py":
Add this to imports:
import shlex

Wrap the unix path with shlex.quote(path):
def escape_unix_path(path):
'''
Escape unescaped spaces in a (unix) path.
'''

if PurePath and isinstance(path, PurePath):
    path = str(path)

# return UNIX_PATH_SPACE_REGEX.sub(r'\1\\ ', path)
return shlex.quote(UNIX_PATH_SPACE_REGEX.sub(r'\1\\ ', path))

I think in Python 2.7 "pipes.quote(path)" does the same thing, for what it's worth.
I thought about just escaping the parens by modifying the existing regex to escape parens, but that didn't feel like a general enough solution.  shlex did the trick, and maybe would catch any special shell characters in a path string.
That might not be the best general solution, but hopefully that's enough of a hint to point you in the right direction.

Thanks for spotting this @Pimmelton! I'm keen to properly fix this issue once and for all. The new command API classes will properly quote this, but has yet to be implemented for the files.* operations & facts. I've got a WIP PR setup now (https://github.com/Fizzadar/pyinfra/pull/603) tracking implementing this which will resolve this issue!
(Will also be adding tests for this specific case!).

Nice, thanks!

This should now be properly handled/fixed in v1.4.5 which contains the various commits from https://github.com/Fizzadar/pyinfra/pull/603 that implement proper shell quoting for filenames.

Did you test with parentheses in filenames and paths, or just spaces (as a quick examination of the testing branch yesterday seemed to indicate)?  I still get this error when running my original test case against 1.4.5.

I did! But I've just realised while the operations work, the facts do not 🤦.

This should now be properly resolved in v1.4.6 per https://github.com/Fizzadar/pyinfra/commit/dc8265b40da780ec57f85e783129d14b71d13769 which contains tests for both operations & facts where the paths need quoting.

Confirmed fixed using both my contrived test cases and real deploy.
Nicely done, and thanks.

Excellent news, thanks for confirming that!


No adjacent duplicates

sorry wrong target

updated #15 instead of this which is meant for the fork I have pending merge in upstream


Release blog post about 4 year of OST

I'll be writing a personal recap of 4 years of OST. The blog post will be edited and first published together with OpenSource.net.

Here the blog post released by OpenSource.net. I'm gone add port the blog post now to our website:
https://opensource.net/closing-the-gap-accelerating-environmental-open-source/

Solved by #890


Restart task is not triggered from command and top level menu

Testing #29618.

Clone https://github.com/Microsoft/vscode-languageserver-node and do npm install's
Run the following task task tsc: build client\src\tsconfig.json through Task menu
Once terminated, do 'Task -> Restart Task...' from menu.
Observe that nothing happens.

Same happens if restart task is triggered from command palette. I assume this is because we moved tasks to a terminal by default.

It's interesting if I do the same but in jsonrpc subfolder, then 'Restart Task' works as a charm.

@michaelchiche restart task terminates a running task and restarts it. I will add a message to say that no task is running if a user triggers the action. I will also rename it to Restart Running Task...


Provide a way to make text entry fields larger

Provide a way to make text entry fields larger, which will make it easier for scouts to use them for notes/comments

@brianjmaher , in most modern browsers (i've tested chrome and firefox) you can resize text fields with the drag handle in the bottom right of a textarea.  It's not very obvious, but could you try that on your device and tell me if it works?  In the meantime, i'll find a way to style that drag handle.

The handle works on Windows desktop Chrome. I couldn't see or use it on iOS Chrome or iOS Safari.


Error Handler (Old 40)

In gitlab by @A-Lawrence on Aug 31, 2015, 01:08
Develop a good error handling class so that there is a central location to log errors within the system and display them on our custom themes.
Allow writing of logs to the error handler at certain points.

In gitlab by @A-Lawrence on Aug 31, 2015, 01:14
Admin error is a redirect, to avoid page being half rendered.  Ref issue #488488 (40).
ÔåÆ <<cset 28c2dcabadfe>>
ALawrence66 - 2015-02-22T13:51:45.717920+00:00

In gitlab by @A-Lawrence on Aug 31, 2015, 01:14
Admin error pages now display the error details. Refs issue #488488 (40).
ÔåÆ <<cset 71a09236444b>>
ALawrence66 - 2015-02-22T13:50:47.002538+00:00

In gitlab by @A-Lawrence on Aug 31, 2015, 01:14
Added user facing error pages.  Errors are stored in the laravel.log file, for now.  Eventually we'll attach them to users but I'm happy with the current setup.  Resolves issue #488488 (40).
Also added the admin error page, for 500 errors.
ÔåÆ <<cset 61e1cf573e12>>
ALawrence66 - 2015-02-22T13:40:45.654097+00:00

In gitlab by @A-Lawrence on Aug 31, 2015, 01:14
Removing milestone: DEVELOPMENT - NO SET RELEASE (automated comment)
ALawrence66 - 2013-11-02T20:56:14.456337+00:00

In gitlab by @A-Lawrence on Aug 31, 2015, 01:14
Created a message handling system for output on screen - this addition to
the code addresses issue #488488 (40), although still needs a PHP error handler to be developed, along with 404/503, etc.  Also removed some redundant code and allowed for more logic heavy models.  This is now the main Master, module specific masters are very rare! The templating code has been modified so that templates are auto assigned - resolves issue #488635 (7) and addresses issue #51.
ÔåÆ <<cset 0fe882f1f5fd>>
ALawrence66 - 2013-08-26T00:29:21.174790+00:00


Update flowcharts in the docs

A few words/commands in the flowcharts need to be updated, and links need to be added to the "Read More" buttons

Duplicate #204


Feature Request: Also Visualize Operations

The schema visualization functionality is great. The next logical step might be to also input and visualize schema operations.

You can select "Mutation" root type from drop-down list on the floating panel and see operations:

sorry, I meant GraphQL Operations as in the actual mutation and query (lowercase) calls to the schema

I'd also vote for support of "Mutation" and "Subscriptions" which could be accessible from "Root".


Spacedesk Driver (Server)

Package Requested
Manifest:
Fill out as much of the manifest as you can:
PackageIdentifier:  Datronicsoft.SpacedeskDriver
PackageVersion: 0.9.95
PackageLocale: en-US
Publisher: datronicsoft
PackageName: spacedesk
License:  # license is here: https://spacedesk.net/##box_431
ShortDescription:  Network Display Multi Monitor Driver (Server)
Installers:
 - Architecture:  x64
   InstallerType: msi
   InstallerUrl:  https://spacedesk.net/downloads/spacedesk_driver_Win_10_64_v0995_BETA.msi
   InstallerSha256: 832f2fa7d0fc6ffd3b51e626a656a721dcc24178adc8e1079c4bfc731ca25276
ManifestType: singleton
ManifestVersion: 1.0.0

Notes:

Download page: https://spacedesk.net/##box_434
Latest Installer URL which redirects to MSI: https://spacedesk.net/downloadidd64

There ya go 😄


Add support for multiple deployments in a single yaml file

Identify an approach to support more than one deployment in a single yaml (https://github.com/konveyor/move2kube/blob/7caa5fd079ad6efe44ca3fb19806e8d024719689/internal/apiresourceset/k8sapiresourceset.go#L92) while loading Kubernetes resources.

We can use an approach similar to https://github.com/konveyor/move2kube/blob/master/internal/common/tar.go#L84-L86
but for yaml instead of tar.


View capabilities in create edit role, role details

Capabilities view list
The tables are read only, the next step is to make them interactive by assigning capabilities to roles

 Thank you for your submission! We really appreciate it. Like many open source projects, we ask that you sign our Contributor License Agreement before we can accept your contribution.You have signed the CLA already but the status is still pending? Let us recheck it.


chore: downgrade Go to 1.19 for commercial

Related PRs:

[ ] https://github.com/aquasecurity/defsec/pull/1497

thanks - I think we will hold off for this for now.


Zero-length [compressed] messages trigger disconnection

Reproduction:

Run either of the echo server examples
Connect to the server in Chrome
Send an empty message: ws.send(""); or ws.send(new ArrayBuffer(0));
Observe that the socket is now disconnected
Observe that the server does not trigger the message handler

This only happens with uWS and Chrome. I've tested the exact same steps above with Firefox and it works fine. I've also tested the following:

Chrome, Firefox against another socket backend ws://demos.kaazing.com/echo (No disconnect on either)
Chrome, Firefox against a server that does not echo empty frames back ws://echo.websocket.org (No disconnect on either)

Not sure if it affects other engines like WebKit/Trident/EdgeHTML. Tested against Chrome 80.0.3987.132 and Firefox 74.0 against uWS 0.17.3.

Very interesting find. I can reproduce with Chrome 80 but not with Firefox just like you say.
Chrome sends 7 bytes while Firefox sends 6 bytes for its empty message. It might actually be a Chrome bug, as you should only have 2 bytes + 4 bytes of data for any empty message?
Will do some investigation here, but this issue shouldn't be critical as you can easily avoid this edge case.

Fixed in master, thanks for reporting!


🐞 Interactive TUI seems broken since Dagger 0.8.x

What is the issue?
I upgraded from Dagger 0.6 to Dagger 0.8. The experimental dagger interactive terminal UI is now broken. As a developer, this interface is really useful and I would like to keep it ;)
Log output
Caught panic:

runtime error: invalid memory address or nil pointer dereference

Restoring terminal...

goroutine 8 [running]:
runtime/debug.Stack()
        /usr/local/go/src/runtime/debug/stack.go:24 +0x64
runtime/debug.PrintStack()
        /usr/local/go/src/runtime/debug/stack.go:16 +0x1c
github.com/charmbracelet/bubbletea.(*Program).Run.func1()
        /go/pkg/mod/github.com/charmbracelet/bubbletea@v0.24.2/tea.go:440 +0x8c
panic({0x1015f6ba0, 0x1020161f0})
        /usr/local/go/src/runtime/panic.go:890 +0x258
github.com/dagger/dagger/internal/tui.(*Details).SetItem(...)
        /app/internal/tui/details.go:37
github.com/dagger/dagger/internal/tui.Model.processUpdate({0x140005a7b70, {0x10179f7e0, 0x1400002a780}, 0x1400002a840, 0x1400002a870, 0x1400002a8a0, 0x14000155900, {{0x0, 0x0}, 0xcb, ...}, ...}, ...)
        /app/internal/tui/model.go:302 +0x61c
github.com/dagger/dagger/internal/tui.Model.Update({0x140005a7b70, {0x10179f7e0, 0x1400002a780}, 0x1400002a840, 0x1400002a870, 0x1400002a8a0, 0x14000155900, {{0x1017b64e0, 0x14000118000}, 0xcb, ...}, ...}, ...)
        /app/internal/tui/model.go:138 +0x4c0
github.com/charmbracelet/bubbletea.(*Program).eventLoop(0x140002dc180, {0x1017a7450?, 0x1400058ee00?}, 0x0?)
        /go/pkg/mod/github.com/charmbracelet/bubbletea@v0.24.2/tea.go:373 +0x5d8
github.com/charmbracelet/bubbletea.(*Program).Run(0x140002dc180)
        /go/pkg/mod/github.com/charmbracelet/bubbletea@v0.24.2/tea.go:503 +0x74c
main.interactiveTUI.func1()
        /app/cmd/dagger/engine.go:136 +0x28
created by main.interactiveTUI
        /app/cmd/dagger/engine.go:135 +0x1c8

Steps to reproduce
env _EXPERIMENTAL_DAGGER_INTERACTIVE_TUI=1 dagger run ts-node --esm build.mjs

The content build.mjs is a "default" node setup with npm install and npm test. Pipeline is working fine without the env var.
SDK version
@dagger.io/dagger 0.8.4
OS version
macOS 13.2

I just hit the same issue with a Python pipeline:
$ dagger run ./dagger_pipeline.py
Caught panic:

runtime error: invalid memory address or nil pointer dereference

Restoring terminal...

goroutine 6 [running]:
runtime/debug.Stack()
        /usr/local/go/src/runtime/debug/stack.go:24 +0x64
runtime/debug.PrintStack()
        /usr/local/go/src/runtime/debug/stack.go:16 +0x1c
github.com/charmbracelet/bubbletea.(*Program).Run.func1()
        /go/pkg/mod/github.com/charmbracelet/bubbletea@v0.24.2/tea.go:440 +0x8c
panic({0x1013a2ba0, 0x101dc21f0})
        /usr/local/go/src/runtime/panic.go:890 +0x258
github.com/dagger/dagger/internal/tui.(*Details).SetItem(...)
        /app/internal/tui/details.go:37
github.com/dagger/dagger/internal/tui.Model.Update({0x14000117d30, {0x10154b7e0, 0x140003ec690}, 0x140003ec750, 0x140003ec780, 0x140003ec7e0, 0x14000139900, {{0x0, 0x0}, 0x8c, ...}, ...}, ...)
        /app/internal/tui/model.go:134 +0xf4
github.com/charmbracelet/bubbletea.(*Program).eventLoop(0x140003be240, {0x101553450?, 0x140000c4e00?}, 0x0?)
        /go/pkg/mod/github.com/charmbracelet/bubbletea@v0.24.2/tea.go:373 +0x5d8
github.com/charmbracelet/bubbletea.(*Program).Run(0x140003be240)
        /go/pkg/mod/github.com/charmbracelet/bubbletea@v0.24.2/tea.go:503 +0x74c
main.interactiveTUI.func1()
        /app/cmd/dagger/engine.go:136 +0x28
created by main.interactiveTUI
        /app/cmd/dagger/engine.go:135 +0x1c8

Sorry about the breakage, will try to get to the bottom of this tomorrow! I added a nil check to the place that's panicking, but even with that the UI seems super borked, so there's something deeper going on.


Keep only specified EXIF, IPTC, and XMP metadata tag values (similar to withExifMerge)

Question about an existing feature
The documentation states that the output option withExifMerge can be used to retain only the specified EXIF metadata from the input image into the output image. However, how can I also specify other metadata, for example IPTC and XMP tags?
(Output option keepMetadata indicates that EXIF, ICC, XMP, IPTC metadata can be retained - so this indicates these tags are accessible from within sharp.)
Additionally, I'm looking to retain the same values for the metadata tags specified, since they currently exist in the input file. (I'm not looking to manually specify override values.)
What are you trying to achieve?
I'm a photographer using Adobe Lightroom Classic and adding copyright information to my photographs. I would like to use sharp to optimize my photos for my website, and strip all metadata (e.g. aperture, shutter speed, etc.) from my images except retain metadata that relates to copyright information and original creation date. Specifically, I would like to remove all metadata except retain only the following fields:

EXIF Artist
EXIF Copyright
EXIF DateTimeOriginal
EXIF OffsetTimeOriginal
IPTC Date Created
IPTC Time Created
IPTC By-line
IPTC By-line Title
IPTC Credit
IPTC Source
IPTC Copyright Notice
XMP DateCreated
XMP Source
XMP Credit
XMP AuthorsPosition
XMP creator
XMP rights
XMP UsageTerms
XMP CreatorContactInfo

CreatorCity
CreatorRegion
CreatorWorkEmail
CreatorWorkUrl

XMP WebStatement

When you searched for similar issues, what did you find that might be related?

I found packages such as exifreader which can read metadata, and has some more flexibility to do things like convert the shutter speed value from a decimal (e.g. 0.005) to a human readable fraction (1/200), but it cannot write or remove metadata.
node-iptc looks to be able to write metadata, but ultimately I'm not trying to change values (or remove all and add back certain ones - they already exist).
I couldn't find anything on StackOverflow or Reddit that related to removing only some metadata tags, but leaving others.
Since the sharp docs mention the ability to do this with EXIF tags, I'm wondering if this can also be done with IPTC and XMP tags as well.
This StackOverflow post seems to somewhat outline the process, but not using sharp.

Please provide a minimal, standalone code sample, without other dependencies, that demonstrates this question
(EXIF tags sourced from EXIF docs.)
const outputWithSelectMetadata = await sharp(inputWithExif)
  .withExifMerge({
    IFD0: {
      Artist: ORIGINAL_FROM_FILE, // how do I retain the original value?
      Copyright: ORIGINAL_FROM_FILE
    },
    ExifIFD: { // is this a valid EXIF tag group in sharp?
      DateTimeOriginal: ORIGINAL_FROM_FILE,
      OffsetTimeOriginal: ORIGINAL_FROM_FILE
    }
  })
  .withIPTCMerge({ // for example
    'Date Created': ORIGINAL_FROM_FILE,
    'Time Created': ORIGINAL_FROM_FILE,
    'By-line': ORIGINAL_FROM_FILE,
    'By-line Title': ORIGINAL_FROM_FILE,
    'Credit': ORIGINAL_FROM_FILE,
    'Source': ORIGINAL_FROM_FILE,
    'Copyright Notice': ORIGINAL_FROM_FILE
  })
  .withXMPMerge({ // for example
    'DateCreated': ORIGINAL_FROM_FILE,
    'Source': ORIGINAL_FROM_FILE,
    'Credit': ORIGINAL_FROM_FILE,
    'AuthorsPosition': ORIGINAL_FROM_FILE,
    'creator': ORIGINAL_FROM_FILE,
    'rights': ORIGINAL_FROM_FILE,
    'UsageTerms': ORIGINAL_FROM_FILE,
    'CreatorContactInfo': ORIGINAL_FROM_FILE,
    'WebStatement': ORIGINAL_FROM_FILE
  })
  .toFile('output.jpg', (err, info) => { ... });

Any help is appreciated! Thanks so much!

EXIF metadata support in sharp is provided via libvips+libexif, which only parses EXIF, therefore IPTC and XMP are exposed as binary blobs for parsing with external tools.
You might have some luck with the slightly-misnamed exiftool, which I believe can parse some IPTC and XMP metadata.

That's come up in my searches. I'll look there - thanks for the clarification!

I hope this information helped. Please feel free to re-open with more details if further assistance is required.


Dragged item component persists if I dont delete them during onDrop or onHit

I have subscribed to onDrop on DragDropContainer. Usually, I delete the dragged node based on some conditions. However, when conditions don't satisfy. I simply return without doing anything. This is causing a problem when drag event occurs subsequently.
The onDrop is called in context to the previous dragged node and also currently dragged node. So, it calls twice. And the calls increases on and on when such dragged node is not deleted in the call.
Can you please suggest me how should I handle this? Thank you very much for such a simple and powerful library.

I had the same problem using onDrop on DragDropContainer and I thought I was doing something wrong.
In my case, which was very specific, I could use onHit on DropTarget to implement my logic and avoid this issue.
I will try to debug and in case of any progress I will open a Pull Request.

Hi there! I think I'm experiencing the same issue. When I need to use the same Drag element multiple times, the first time everything is fine, but since the 2nd time on, onDrop will fire as many times as that Drag element was used before plus 1 more time. Hopefully there's a solution to it


Why we do not have Bookings or Virtual Appointments in the list

Why we do not have Bookings or Virtual Appointments in the list. Customers would like to know where is bookings
data stored.

Document Details
⚠ Do not edit this section. It is required for learn.microsoft.com ➟ GitHub issue linking.

ID: 61c6fa7b-7e9e-21a5-40da-adbd35040ec1
Version Independent ID: 530df3b2-2048-8351-2f09-59e604aedf5b
Content: Microsoft 365 data locations - Microsoft 365 Enterprise
Content Source: microsoft-365/enterprise/o365-data-locations.md
Service: microsoft-365-enterprise
GitHub Login: @kelleyvice-msft
Microsoft Alias: kvice

@msbemba Thank you very much for the contribution and sharing this explanation. @dipinn26592 Hope this comment is helpful for you. If you see a documentation update is required, please feel free to open an issue for the same. We proceed here to close it. Thanks for taking out some time to open the issue. Appreciate and encourage you to do the same in future also.


Update CI jobs

This updates our config to run the following jobs:

JDK 11 on all platforms, using the latest supported Error Prone version (currently 2.10.0).  This builds and tests all of our targets.
A "legacy" job, running on Linux with JDK 8 and the oldest supported Error Prone version (currently 2.4.0).  Some targets are now disabled here, like building of sample Android apps and corresponding tests.
A JDK 17 job with latest Error Prone that runs core NullAway tests, but still doesn't run all Android / JarInfer tests (still work to do to get them passing).

I think this is a sufficient set of CI jobs to give us good coverage of realistic configurations.  We could add a Linux / JDK 11 / EP 2.4.0 job for a bit more coverage if we like.
We'll need to update the "Required" CI jobs after this lands.

Pull Request Test Coverage Report for Build #751

0 of 0   changed or added relevant lines in 0 files are covered.
No unchanged relevant lines lost coverage.
Overall coverage remained the same at 92.946%

Totals

Change from base Build #748:
0.0%

Covered Lines:
4322

Relevant Lines:
4650

💛  - Coveralls

We could add a Linux / JDK 11 / EP 2.4.0 job for a bit more coverage if we like.

This makes sense to me if it works right now. We might eventually need to break compatibility for that configuration, but we want to make sure we know when we do (particularly since that's what I believe we are dealing with internally for at least one repo).

We could add a Linux / JDK 11 / EP 2.4.0 job for a bit more coverage if we like.

This makes sense to me if it works right now. We might eventually need to break compatibility for that configuration, but we want to make sure we know when we do (particularly since that's what I believe we are dealing with internally for at least one repo).

Cool, done in 9056234


Renaming the first tutorial

Good start :) but you also need to rename the real file

In the folder 'content\tutorials\01_Play_Pen', rename the corresponding MD file and you're done!

Perfect! Thanks!

Thank you!
Now, to do the rest of them, I could... hmm... clone the repo into my Wingnutt account, change all the names, edit the json to match, then do a single PR for all of them, yes?  Bad idea?  (thx)

Nope this is exactly what you should do:)

It's a perfect idea actually :)

Hi guys, thanks!  I should repair all the 'next tutorial' links at the bottom of each tutorial, too?  Or remove them?  Thoughts?  (thx)

I vote to update them.

Same here

Roger... will do.

Crap, sorry guys, but maybe you can help me easier than finding the answer on the web.  It says my current doc repo @ github wingnutt is 52 commits behind.  I can't seem to find a refresh, but there IS a "compare" and a "pull request" beside that notification.  Could you hold my puppy paw a bit longer? (thx)

git checkout master
git fetch BabylonJS
git merge BabylonJS/master

(In a git console, of course :) )
and afterwards:
git push

All sorts of familiar faces in here.  :)  Not sure I understand.  I would prefer not to use a local repo.  I would like to update Wingnutt (my git user name) /Documentation from BabylonJS/Documentation (master or production).  I was hoping to do it all at github.  Possible?  (thx)

I just realized you are using github's web interface.
go to your local repository (Wingnut/Documentation) and click create pull request.
in the left box, choose Wingnut/Documentation.
then two boxes will vanish. click on the link "compare across forks" (the upper sentence)
now choose Babylon/Documentation at the 3rd box.
make sure the 2nd and 4th are saying "master".
Then create the pull request and merge it

Looking good, thanks!  Now it says "This branch is 1 commit ahead of BabylonJS:master.".  I suppose that is fine.  Does this cause any workload to curators?  Did someone need to approve this "refreshment"?  I hope not.   It is perfectly fine.
This is git's way :) it will eventually make sense.

Still two questions.  Did anyone besides me... need to approve my refreshing pull? (that sounds dirty somehow)  :)
And... do you know if I can sync my online repo, to the BJS master... so it stays fresh all the time?
(thanks again)

The PR you just created was on your local repository, which you alone control you have write rights).
What you just did was taking all of the commits missing in your repository and merged them.
You usually do the other way around - PR to the BJS repository. There you have no write rights, so it needs to be approved by an admin.
Your local repository stays as fresh as you keep it. The best way is to actually use a git command line (or a git tool like Github for windows). But if you want to use the web interface alone, this will be the only way (that I know) to keep your repository synced.

I understand.  But I heard somewhere that pushes (from my home repo) were not allowed.  Do I push it to the Wingnutt account, and then send a PR to the curators asking them to pull my edits fro Wingnutt repo?  It seems... that if I request that curators pull from my home repo, I would need a server and to make my home repo... shared.  I really don't want to run a server or be concerned that my home computer is shut-off when curators want to do a remote pull from me.
I have much to read/learn.... sorry for the puppy questions.

not sure what your "home repo" is.
This is a very simplified explanation, but:
you have your online form (repository) under Wingnutt/Documentation. This is yours alone to control. You can create branches there, you can edit files, you can "download" (clone in gittish) it to your local computer and work on it there. Syncing between the clone (on your computer) and your repository (on github) is done using pulls and pushes. You don't need any pull request there.
When making changes on your repository (whether online or on your computer) you create commits. those commits can later be sent to Babylon's repository using pull requests.
A best practice to achieve a clean pull request from your repository (to update BJS's documentation, for example) it to create a branch of your master and edit the files there. A branch is a local copy of (usually) your master. This way you keep your master clean and can make changes without the fear of doing anything wrong. One you are satisfied with the content of this branch (you made changes to files you wanted to change) then you create a pull request from your branch to Babylon's master. The admins will then see the files you changed and decide whether or not you changes are good. Afterwards you can close your branch and go back to your master, to continue working on other files.

Don't fear of making a mistake. If you made a mistake in a branch, you can simply delete it, go to the master, and start again.
You have no need of a server or anything of that sorts. Your repository is public. Babylon can pull your changes whenever the admins want. But it needs your help to keep those requests structured. This is a pull request. You send a pull request from your online repository (the one already on your server), so your local computer is totally not involved in this.
I hope it made more sense than confusing you! :smile:

Ok, thanks, very good.  Home repo = the clone of docs repo that I have on my home computer.
If I asked the curators to pull from my home computer... I WOULD need a server and WOULD need to set that folder as shared, I suspect.  Having anything visit and get files from my home computer... 1. scares me.. and 2. requires that my computer stay powered and its server stay running.
The only reason I maintain a fresh repo on my home computer... is to do builds of edits I make... to make sure the HTML version looks good after the edit of the md.  Strictly html test-viewing.
Then, I come to my github web interface, make sure it is fresh, make sure that the file I edited at home... hasn't changed since I started the home edit... then paste-in the edited .md at the web interface, then do the PR from there.
Thanks for all the help, R, T, and DK.

I am not sure you understood what I said.
but! do whatever makes you feel right :smile:
just a final note - you don't send PRs from your home computer. even when doing that, you still first upload the data to your own repository on github. Then and only then will the PR be sent to the onlien repository of Babylon.
No one, at any point, has access to your local files.

No, I understand.  You were telling me good housekeeping ideas for my online repo at github.
And yes, that is good info about... things go to my Wingnutt fork first, and then the pulls over to production... come from there.
This 'push' you spoke-of... pushes to Wingnutt account, not to BJS account, and that's why it is allowed.  I think DK told me "no pushes" but he was talking about pushing directly to BJS git account... and not to Wingnutt git acct.
I'm curious which part you misunderstood.  :)

I don't need to keep my local (home) repo sync'd.  I will do fresh clone to home... before I start editing an md (also edited at home, with my MarkdownPad2 github-flavored editor).  I would do a docs build of that edited md (at home), to make sure the html looked ok.  THEN, without delay... I head to Wingnutt online acct... refresh my Wingnutt online repo, (then fork my master as you suggested), choose the file to edit, paste in the md text that I made at home, save the edit, do the PR.
Workflow hell, eh?  If I don't inspect the html version before I come online to github... the html could look like crap after the commit, which means I would need to edit again, and again, and again, until the html looked nice.  Lots of work for curators... not wise.

Tiny, simple edits... probably don't need all that BS, and can be done completely at github web interface, likely.

ok. I guess we're done here.  :)

Guys, I might have pushed a bad button yesterday.  Please delete/ignore any git stuff from me... while I practice.
I was using the "update my master from bjs master" thing that Raanan taught me, but I forgot to select MY repo in the left box, and BJS in the right box.  I had them reversed, so I think my pull/merge... went the wrong direction.
Thanks for your patience, guys.

Would anyone like to help with my Permission denied (publickey) (in git bash)?
http://urbanproductions.com/wingy/babylon/misc/perm_denied.txt
Advice VERY welcome (thx).  It's still the first day, though, so I'm begging for help a bit early.  I have much more reading and bleeding to do, yet.  :)
"The agent has no identities."  (ssh agent).  I think THAT might be something important and broken.  :)

Yay!  http://urbanproductions.com/wingy/babylon/misc/perm_granted.txt  I am SO PROUD of me.  heh
I have ssh identity!  I am SOMEBODY!  :)