diff --git a/README.md b/README.md index 888cc11..650f930 100644 --- a/README.md +++ b/README.md @@ -55,7 +55,7 @@ Download the `.nvda-addon` file from [GitHub Releases](https://github.com/InfiAr ## Getting started -1. Open a code file in Notepad, for example [samples/latihan.py](samples/latihan.py) from this repository. Its first lines say what to try where. +1. Open a code file in Notepad, for example [samples/practice.py](samples/practice.py) from this repository (in Indonesian: [samples/latihan.py](samples/latihan.py)). Its first lines say what to try where. 2. Move up and down with the arrow keys and listen to the tones rise and fall. 3. Press NVDA+shift+K, then W, to hear where you are. NVDA+shift+K, then H, lists every command, and control+shift+P lists them with their keys. 4. Press control+F5 to run it. @@ -91,7 +91,7 @@ The build writes `codeCompass-.nvda-addon`. It needs no SCons or gettex - `addon/sounds/`: Visual Studio Code's accessibility signal sounds, as WAV files. - `addon/locale/id/LC_MESSAGES/nvda.po`: the Indonesian translation. `tools/update_po.py` rewrites it to match the messages in the code, and a test fails when a message has no translation. - `tests/`: analyzer tests, translation checks and tests of the plugin against stubbed NVDA and wx modules. -- `samples/`: files to try the add-on with: `latihan.py` walks through the features, `rusak.js` is broken on purpose. +- `samples/`: files to try the add-on with: `practice.py` (and `latihan.py`, the same in Indonesian) walks through the features, `rusak.js` is broken on purpose. ## Contributing diff --git a/addon/doc/en/readme.md b/addon/doc/en/readme.md index e97a1d3..cfacca3 100644 --- a/addon/doc/en/readme.md +++ b/addon/doc/en/readme.md @@ -68,7 +68,7 @@ Bookmarks are kept per file, also after closing Notepad or restarting NVDA. Each When you come back to Notepad and another program (an editor, a code generator, Git) has changed the open file, Code Compass notices: -- With no unsaved changes, the file is reloaded right away and Code Compass says what changed, for example "latihan.py changed on disk and was reloaded: 1 line changed, 2 lines added". The caret stays on its line and bookmarks follow their lines. +- With no unsaved changes, the file is reloaded right away and Code Compass says what changed, for example "practice.py changed on disk and was reloaded: 1 line changed, 2 lines added". The caret stays on its line and bookmarks follow their lines. - With unsaved changes, nothing is replaced: Code Compass tells you, and NVDA+shift+K then R reloads the file when you choose to. It asks first, and control+Z undoes the reload. - NVDA+shift+K then R also reloads at any time. Saving in Notepad does not count as a change. - Notepad++ watches its files itself (File, Reload from Disk), so Code Compass leaves it to Notepad++. diff --git a/samples/latihan.py b/samples/latihan.py index 288db0d..b02738a 100644 --- a/samples/latihan.py +++ b/samples/latihan.py @@ -1,4 +1,5 @@ # Latihan Code Compass. Buka di Notepad, lalu coba hal-hal di bawah ini. +# (Versi bahasa Inggris: practice.py.) # # Ctrl+Shift+Space (petunjuk parameter): taruh kursor di dalam kurung # pemanggilan fungsi, misalnya di "15000" atau "diskon=10" pada baris diff --git a/samples/practice.py b/samples/practice.py new file mode 100644 index 0000000..0bc7b94 --- /dev/null +++ b/samples/practice.py @@ -0,0 +1,112 @@ +# Code Compass practice file. Open it in Notepad and try the things below. +# (Indonesian version: latihan.py.) +# +# control+shift+space (parameter hint): put the caret on a call, for +# example on "calculate_total" or inside its parentheses in main, and +# press control+shift+space. Code Compass reads the parameters and +# which argument you are on. Try Shop("Corner Shop", ...) and +# shop.add_item(...) too. +# control+space: type "calc" on an empty line, then press control+space +# a few times. +# F12: with the caret on a called name such as "calculate_total", jump to +# where it is declared. control+alt+backspace takes you back. +# shift+F12: a list of every line that uses that name. +# F2: rename a name, for example "price" in calculate_total. +# alt+page down and alt+page up: move by function. alt+left arrow: to the +# function or class around it. +# control+shift+O: the outline. NVDA+shift+K, then F: the family of the +# function at the caret. +# control+alt+K: set a bookmark. control+alt+L and control+alt+J: jump +# between bookmarks. +# NVDA+shift+K, then shift+T: the to-do and fixme notes in comments. +# control+F5: run this file. control+S: save and check for problems. +# Want to hear the error sound? Delete any ")" and wait a second. + + +def calculate_total(price, quantity, discount=0): + """Total price after a discount, in percent.""" + gross = price * quantity + reduction = gross * discount / 100 + return gross - reduction + + +def format_money(amount, with_symbol=True): + """An amount as money text, for example 1234.5 -> "$1,234.50".""" + text = f"{amount:,.2f}" + return f"${text}" if with_symbol else text + + +class Item: + def __init__(self, name, price, stock=1): + self.name = name + self.price = price + self.stock = stock + + def in_stock(self): + return self.stock > 0 + + +class Shop: + """A small shop with a list of items.""" + + def __init__(self, name, city, owner="Rafli"): + self.name = name + self.city = city + self.owner = owner + self.items = [] + + def add_item(self, name, price, stock=1): + item = Item(name, price, stock) + self.items.append(item) + return item + + def find(self, word, in_stock_only=False): + found = [] + for item in self.items: + if word.lower() in item.name.lower(): + if in_stock_only and not item.in_stock(): + continue + found.append(item) + return found + + def report(self): + # TODO: sort the items from the most expensive + lines = [f"{self.name} in {self.city}, owned by {self.owner}"] + for number, item in enumerate(self.items, start=1): + status = "in stock" if item.in_stock() else "sold out" + lines.append(f" {number}. {item.name}: {format_money(item.price)} ({status})") + return "\n".join(lines) + + +def split_evenly(total, people): + """Split a total between people; people must not be zero.""" + try: + return total / people + except ZeroDivisionError: + # FIXME: tell the user with a clearer message + return 0 + + +def main(): + shop = Shop("Corner Shop", "Bandung", owner="Rafli") + shop.add_item("Arabica Coffee", 12.5, stock=3) + shop.add_item("Jasmine Tea", 4.75, stock=5) + shop.add_item("Palm Sugar", 3.2, stock=0) + + print(shop.report()) + print() + + total = calculate_total(15, 3, discount=10) + print("Total:", format_money(total)) + print("Each:", format_money(split_evenly(total, 2))) + + for item in shop.find("tea", in_stock_only=True): + print("Found:", item.name) + + # Remove the # on the line below to try an error when running + # (control+F5), then press F8 to go to its line. + # print(calculate_total(10, "two")) + + +if __name__ == "__main__": + main()