bitburner-official / bitburner-src

Bitburner source code.
Other
828 stars 270 forks source link

autocomplete does not work well with multi-word data #1014

Open ZachHaber opened 10 months ago

ZachHaber commented 10 months ago

Multi-word autocomplete entries don't work well. If you have fairly unique entries, it'll mostly work. You still have to be aware that you need to add " around the options you return from the autocomplete function, otherwise it won't autocomplete with quotes - which makes them separate arguments.

If you have options that differ only after the first word, then the whole thing just fails as-is - even with quotes.

For example, use this as the autocomplete, and attempt to autocomplete "nonunique second word":

export function autocomplete() {
  return ['"unique first"', '"nonunique second word"', '"nonunique sea word"'];
}

You'll get to "nonunique se and it will no-longer register any autocomplete options!

If you then add 'seriously' to the list of options (['"unique first"', '"nonunique second word"', '"nonunique sea word"', 'seriously']), then it would autocomplete to "nonunique seriously from "nonunique se

The only way around this is to do a ton of parsing in the autocomplete function to try and figure out what already exists, and then only return the next parts of matching options, and as was mentioned in #522, it's rather complex to try and figure out what is already set and what isn't.

Version: v2.5.2 (c6141f2ad)

I've already written myself a wrapper to deal with this issue (and a few more), but I just wanted to bring it to light, since I couldn't find a bug that dealt with this particular part of the autocomplete.

For reference: this came up as I was trying to add autocomplete for contract types.

Snarling commented 10 months ago

Autocomplete really is only meant to autocomplete the currently in progress word (if a character other than space is the last part of the in progress string, then that word needs autocompleted, but if a space is the last character then the autocompletion should be suggesting entirely new words to add at the end). The game sends you an array of the previous arguments already typed out so that you can have different autocomplete options sent based on what has already been typed.

However, going through and trying to make an example script to show you how it's expected to be done, I noticed some inconsistent behavior in how it sends that info to the your autocomplete function.

e.g. run autocomplete.js nonunique (with no space at the end, nonunique is the word in progress) sends the same thing to the autocomplete function as if run autocomplete.js nonunique (with a space at the end, the empty next word is what is being autocompleted).

So I'll keep this issue open to smooth out that inconsistency and then provide a reliable way to handle the autocompletion based on arguments already typed out

cigarmemr commented 10 months ago

e.g. run autocomplete.js nonunique (with no space at the end, nonunique is the word in progress) sends the same thing to the autocomplete function as if run autocomplete.js nonunique (with a space at the end, the empty next word is what is being autocompleted).

I had the same confusion before, as the player can hardly control either it is now to autocomplete the previous incomplete arg or to suggest candicates for the next one.

Here is an instance of autocomplete() I used in a script, which uses args to control which Hash Upgrade to buy:

export function autocomplete(data, arrArgs) {
  let arrHashUpgrades = Object.keys(objHashUpgrades)
  let arrACargs = arrArgs.filter((arg) => !['-a'].includes(arg))
  if (arrACargs.length === 0 || !arrHashUpgrades.includes(arrACargs[0])) {
    return arrHashUpgrades
  }
  let upgradeArg = arrACargs[0]
  if (upgradeArg === 'serverSecurity' || upgradeArg === 'serverMoney') {
    return data.servers.filter((host) => !(fIsHS(host) || fIsSPS(host) || host === 'home'))
  }
  if (upgradeArg === 'companyFavor') {
    return Object.keys(objCompanies)
  }
  return []
}

'-a' is excluded first as it is a flag to do some control. The purpose of all the rest logic is to make a judgement:

It generally works well, but for some rare cases it does not function as intended: image

Note that there is a space padding the first arg serverSe, indicates the first arg is done and I'm going to enter the second one. In this case I hope it will recognize the first arg as an forementioned "Otherwise" and provides nothing rather than arrHashUpgrades

A solution I can think of is: when the player hits tab and the string already inputted ends with space, push an empty string '' into the array containing args. The autocomplete() will receive ['serverSe', ''] instead of ['serverSe'] and then can make a judgement.

ZachHaber commented 10 months ago

The args passed into the autocomplete function appear to be inconsistent as well.

./script.js "test multi"| + tab gives ['test multi'] as a result

./script.js "test multi" | + tab gives `['test','multi'].

ZachHaber commented 10 months ago

A solution I can think of is: when the player hits tab and the string already inputted ends with space, push an empty string '' into the array containing args. The autocomplete() will receive ['serverSe', ''] instead of ['serverSe'] and then can make a judgement.

I second that idea, so far it's the best one I can think of. Though I don't know how much it would break scripts.

The other idea I had was to add in a rawArgs string property to both the autocomplete's data arg as well as ns so that people who want to can do more advanced parsing. The use-case I have in this would be solved by simply being able to check if the rawArgs ends with a space. You could also give a little more detail for advanced parsing by adding in a cursorPosition property that is the index the cursor is at in the rawArgs string.

ZachHaber commented 10 months ago

Also, for what its worth, this is my current autocomplete/flags setup with multi-word autcomplete, type inferences, shortcuts for flags (e.g. '-b'), and semi-validated options for flags as well: https://gist.github.com/ZachHaber/35762ae151fa5ee14348b24a8ff27ec0

There are still some issues with it, like ensuring that non-boolean options have to have a value after them before it will suggest further flags. For the most part, I've worked around the issue mentioned here by slicing the multi-word options strings based on the current multi-word string to allow autcomplete based off of the "current" word.