Code Standards and Guidelines - Windower/packages GitHub Wiki

The following style and design guide applies to libraries and services for Windower 5. In places where service design varies from libraries it is explicitly noted.

For addons these are not mandatory but recommended, again with some explicitly mentioned exceptions.

Code Styling

The packages repository comes with an .editorconfig file for VS Code to help enforce these styling guidelines. VS Code is (for that and other reasons) our recommended editor for package development.

✔️ Do use UTF-8 (no BOM) as the file encoding.

✔️ Do use \r\n line endings.

❌ Do not use trailing spaces.

✔️ Do use a new line at the end of each file.

❌ Do not use hard tabs for indentation.

✔️ Do use spaces for indentation.

🔹 Use four spaces for code files. 🔹 Use two spaces for XML files.

❌ Do not use double quotes for any strings.

✔️ Do use single quotes for all strings.

✔️ Do put spaces around all operators.

✔️ Do put spaces after each comma in a function call.

❌ Do not use single line code blocks, i.e. if ... else ... end

✔️ Do try to match the style of existing code if not specified in here

🔹 If existing code conflicts, prefer matching the style of libraries and services over other addons.

Variable Scoping

✔️ Do use local wherever possible, variables should have as narrow a scope as is required. This includes function declarations.

✔️ Do use do ... end blocks liberally to limit scope, especially at the global or file level.

Variable Naming

✔️ Do use snake_case instead of camelCase or PascalCase.

✔️ Do use an underscore to mark unused variables in function signatures.

For example, function(_, k) return data[k] end.

✔️ Do use value over value_current.

For example, exp and not exp_current.

✔️ Do use value_modifier over modifier_value.

For example, hp_max and not max_hp.

Global State

✔️ Do use return values for libraries.

❌ Do not modify global state for libraries.

require('library') should not have any effect other than its return value.

🔹 Exceptions to that are services.

Services may require global state to avoid the garbage collector removing data that is shared across packages using the shared library. The results of shared.new must not be collected, same as the table containing the data and environment of the shared data server. A simple way to achieve that is to make them global.

Syntactic Salt & Sugar

❌ Do not use functions with literal arguments without parentheses.

For example, use foo('str') over foo 'str' or any derivations thereof.

✔️ Do use some_t:foo(...) over t.foo(some_t, ...) when available.

❌ Do not use metatable hacks just to be able to use this syntax.

Performance

✔️ Do prefer performance over readability and elegance for libraries and services.

🔹 Addons can approach this as they see fit.

Normally, addons should prefer readability and concise code, but performance-critical sections may reverse those priorities. Ideally there would be a comfortable middle ground.

Code Reuse

❌ Do not reimplement similar mechanisms in multiple libraries.

If multiple libraries need to define the same things they would either deserve their own library or an adjustment of core mechanisms to fit those needs.

Addon Behavior

✔️ Do make verbose chat output (i.e. "command succeeded") toggleable via settings and generally default it to off.

If chat output is actively displaying information the user requested via a specific command, then this doesn't apply, but consider if a UI component might work instead, as it could provide live information without cluttering chat.

If informative event based chat output is a primary function of an addon then that may be enabled by default but the addon must still offer a setting to disable it. Also, consider separate settings for each event in addition to a global on/off switch. Overly verbose or redundant output should still be avoided and placed under a verbose mode or similar setting which defaults to off, as mentioned above.

✔️ Do use client_data over resources when possible.

client_data is the newer method and fetches data directly from the client. resources is a separately maintained database and hence has more memory and developer overhead, making it less preferable.

✔️ Do make user-facing settings and interactions language-agnostic when possible.

For example, use .name or equivalent for commands or settings that a user may interact with. For internal mechanisms, specifying the language explicitly may be preferable.