mirror of
https://github.com/SquidDev-CC/CC-Tweaked
synced 2025-07-19 02:12:53 +00:00
Copy and paste the wiki guide on require
I wrote the original, so I don't need to feel guilty :) Closes #565.
This commit is contained in:
parent
932b77d7ee
commit
0f9ddac83c
83
doc/guides/using_require.md
Normal file
83
doc/guides/using_require.md
Normal file
@ -0,0 +1,83 @@
|
|||||||
|
---
|
||||||
|
module: [kind=guide] using_require
|
||||||
|
---
|
||||||
|
|
||||||
|
# Reusing code with require
|
||||||
|
A library is a collection of useful functions and other definitions which is stored separately to your main program. You
|
||||||
|
might want to create a library because you have some functions which are used in multiple programs, or just to split
|
||||||
|
your program into multiple more modular files.
|
||||||
|
|
||||||
|
Let's say we want to create a small library to make working with the @{term|terminal} a little easier. We'll provide two
|
||||||
|
functions: `reset`, which clears the terminal and sets the cursor to (1, 1), and `write_center`, which prints some text
|
||||||
|
in the middle of the screen.
|
||||||
|
|
||||||
|
Start off by creating a file called `more_term.lua`:
|
||||||
|
|
||||||
|
```lua {data-snippet=more_term}
|
||||||
|
local function reset()
|
||||||
|
term.clear()
|
||||||
|
term.setCursorPos(1, 1)
|
||||||
|
end
|
||||||
|
|
||||||
|
local function write_center(text)
|
||||||
|
local x, y = term.getCursorPos()
|
||||||
|
local width, height = term.getSize()
|
||||||
|
term.setCursorPos(math.floor((width - #text) / 2) + 1, y)
|
||||||
|
term.write(text)
|
||||||
|
end
|
||||||
|
|
||||||
|
return { reset = reset, write_center = write_center }
|
||||||
|
```
|
||||||
|
|
||||||
|
Now, what's going on here? We define our two functions as one might expect, and then at the bottom return a table with
|
||||||
|
the two functions. When we require this library, this table is what is returned. With that, we can then call the
|
||||||
|
original functions. Now create a new file, with the following:
|
||||||
|
|
||||||
|
```lua {data-mount=more_term:more_term.lua}
|
||||||
|
local more_term = require("more_term")
|
||||||
|
more_term.reset()
|
||||||
|
more_term.write_center("Hello, world!")
|
||||||
|
```
|
||||||
|
|
||||||
|
When run, this'll clear the screen and print some text in the middle of the first line.
|
||||||
|
|
||||||
|
## require in depth
|
||||||
|
While the previous section is a good introduction to how @{require} operates, there are a couple of remaining points
|
||||||
|
which are worth mentioning for more advanced usage.
|
||||||
|
|
||||||
|
### Libraries can return anything
|
||||||
|
In our above example, we return a table containing the functions we want to expose. However, it's worth pointing out
|
||||||
|
that you can return ''anything'' from your library - a table, a function or even just a string! @{require} treats them
|
||||||
|
all the same, and just returns whatever your library provides.
|
||||||
|
|
||||||
|
### Module resolution and the package path
|
||||||
|
In the above examples, we defined our library in a file, and @{require} read from it. While this is what you'll do most
|
||||||
|
of the time, it is possible to make @{require} look elsewhere for your library, such as downloading from a website or
|
||||||
|
loading from an in-memory library store.
|
||||||
|
|
||||||
|
As a result, the *module name* you pass to @{require} doesn't correspond to a file path. One common mistake is to load
|
||||||
|
code from a sub-directory using `require("folder/library")` or even `require("folder/library.lua")`, neither of which
|
||||||
|
will do quite what you expect.
|
||||||
|
|
||||||
|
When loading libraries (also referred to as *modules*) from files, @{require} searches along the *@{package.path|module
|
||||||
|
path}*. By default, this looks something like:
|
||||||
|
|
||||||
|
* `?.lua`
|
||||||
|
* `?/init.lua`
|
||||||
|
* `/rom/modules/main/?.lua`
|
||||||
|
* etc...
|
||||||
|
|
||||||
|
When you call `require("my_library")`, @{require} replaces the `?` in each element of the path with your module name, and
|
||||||
|
checks if the file exists. In this case, we'd look for `my_library.lua`, `my_library/init.lua`,
|
||||||
|
`/rom/modules/main/my_library.lua` and so on. Note that this works *relative to the current program*, so if your
|
||||||
|
program is actually called `folder/program`, then we'll look for `folder/my_library.lua`, etc...
|
||||||
|
|
||||||
|
One other caveat is loading libraries from sub-directories. For instance, say we have a file
|
||||||
|
`my/fancy/library.lua`. This can be loaded by using `require("my.fancy.library")` - the '.'s are replaced with '/'
|
||||||
|
before we start looking for the library.
|
||||||
|
|
||||||
|
## External links
|
||||||
|
There are several external resources which go into require in a little more detail:
|
||||||
|
|
||||||
|
- The [Lua Module tutorial](http://lua-users.org/wiki/ModulesTutorial) on the Lua wiki.
|
||||||
|
- [Lua's manual section on @{require}](https://www.lua.org/manual/5.1/manual.html#pdf-require).
|
@ -1,22 +1,24 @@
|
|||||||
--- This provides a pure Lua implementation of the builtin @{require} function
|
--[[- This provides a pure Lua implementation of the builtin @{require} function
|
||||||
-- and @{package} library.
|
and @{package} library.
|
||||||
--
|
|
||||||
-- Generally you do not need to use this module - it is injected into the
|
Generally you do not need to use this module - it is injected into the every
|
||||||
-- every program's environment. However, it may be useful when building a
|
program's environment. However, it may be useful when building a custom shell or
|
||||||
-- custom shell or when running programs yourself.
|
when running programs yourself.
|
||||||
--
|
|
||||||
-- @module cc.require
|
@module cc.require
|
||||||
-- @since 1.88.0
|
@since 1.88.0
|
||||||
-- @usage Construct the package and require function, and insert them into a
|
@see using_require For an introduction on how to use @{require}.
|
||||||
-- custom environment.
|
@usage Construct the package and require function, and insert them into a
|
||||||
--
|
custom environment.
|
||||||
-- local r = require "cc.require"
|
|
||||||
-- local env = setmetatable({}, { __index = _ENV })
|
local r = require "cc.require"
|
||||||
-- env.require, env.package = r.make(env, "/")
|
local env = setmetatable({}, { __index = _ENV })
|
||||||
--
|
env.require, env.package = r.make(env, "/")
|
||||||
-- -- Now we have our own require function, separate to the original.
|
|
||||||
-- local r2 = env.require "cc.require"
|
-- Now we have our own require function, separate to the original.
|
||||||
-- print(r, r2)
|
local r2 = env.require "cc.require"
|
||||||
|
print(r, r2)
|
||||||
|
]]
|
||||||
|
|
||||||
local expect = require and require("cc.expect") or dofile("rom/modules/main/cc/expect.lua")
|
local expect = require and require("cc.expect") or dofile("rom/modules/main/cc/expect.lua")
|
||||||
local expect = expect.expect
|
local expect = expect.expect
|
||||||
|
@ -26,13 +26,14 @@ const Click = (options: { run: () => void }) =>
|
|||||||
|
|
||||||
type WindowProps = {};
|
type WindowProps = {};
|
||||||
|
|
||||||
type WindowState = {
|
type Example = {
|
||||||
visible: boolean,
|
files: { [file: string]: string },
|
||||||
|
|
||||||
example: string,
|
|
||||||
exampleIdx: number,
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
type WindowState = {
|
||||||
|
exampleIdx: number,
|
||||||
|
} & ({ visible: false, example: null } | { visible: true, example: Example })
|
||||||
|
|
||||||
type Touch = { clientX: number, clientY: number };
|
type Touch = { clientX: number, clientY: number };
|
||||||
|
|
||||||
class Window extends Component<WindowProps, WindowState> {
|
class Window extends Component<WindowProps, WindowState> {
|
||||||
@ -41,12 +42,14 @@ class Window extends Component<WindowProps, WindowState> {
|
|||||||
private top: number = 0;
|
private top: number = 0;
|
||||||
private dragging?: { downX: number, downY: number, initialX: number, initialY: number };
|
private dragging?: { downX: number, downY: number, initialX: number, initialY: number };
|
||||||
|
|
||||||
|
private snippets: { [file: string]: string } = {};
|
||||||
|
|
||||||
constructor(props: WindowProps, context: unknown) {
|
constructor(props: WindowProps, context: unknown) {
|
||||||
super(props, context);
|
super(props, context);
|
||||||
|
|
||||||
this.state = {
|
this.state = {
|
||||||
visible: false,
|
visible: false,
|
||||||
example: "",
|
example: null,
|
||||||
exampleIdx: 0,
|
exampleIdx: 0,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -57,10 +60,16 @@ class Window extends Component<WindowProps, WindowState> {
|
|||||||
const element = elements[i] as HTMLElement;
|
const element = elements[i] as HTMLElement;
|
||||||
|
|
||||||
let example = element.innerText;
|
let example = element.innerText;
|
||||||
|
|
||||||
|
const snippet = element.getAttribute("data-snippet");
|
||||||
|
if (snippet) this.snippets[snippet] = example;
|
||||||
|
|
||||||
if (element.getAttribute("data-lua-kind") == "expr") {
|
if (element.getAttribute("data-lua-kind") == "expr") {
|
||||||
example = exprTemplate.replace("__expr__", example);
|
example = exprTemplate.replace("__expr__", example);
|
||||||
}
|
}
|
||||||
render(<Click run={this.runExample(example)} />, element);
|
|
||||||
|
const mount = element.getAttribute("data-mount");
|
||||||
|
render(<Click run={this.runExample(example, mount)} />, element);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -76,13 +85,13 @@ class Window extends Component<WindowProps, WindowState> {
|
|||||||
</div>
|
</div>
|
||||||
<div class="computer-container">
|
<div class="computer-container">
|
||||||
<Computer key={exampleIdx} files={{
|
<Computer key={exampleIdx} files={{
|
||||||
"example.lua": example, ...defaultFiles
|
...example!.files, ...defaultFiles
|
||||||
}} />
|
}} />
|
||||||
</div>
|
</div>
|
||||||
</div> : <div class="example-window example-window-hidden" />;
|
</div> : <div class="example-window example-window-hidden" />;
|
||||||
}
|
}
|
||||||
|
|
||||||
private runExample(example: string): () => void {
|
private runExample(example: string, mount: string | null): () => void {
|
||||||
return () => {
|
return () => {
|
||||||
if (!this.positioned) {
|
if (!this.positioned) {
|
||||||
this.positioned = true;
|
this.positioned = true;
|
||||||
@ -90,9 +99,17 @@ class Window extends Component<WindowProps, WindowState> {
|
|||||||
this.top = 20;
|
this.top = 20;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const files: { [file: string]: string } = { "example.lua": example };
|
||||||
|
if (mount !== null) {
|
||||||
|
for (const toMount of mount.split(",")) {
|
||||||
|
const [name, path] = toMount.split(":", 2);
|
||||||
|
files[path] = this.snippets[name] || "";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
this.setState(({ exampleIdx }: WindowState) => ({
|
this.setState(({ exampleIdx }: WindowState) => ({
|
||||||
visible: true,
|
visible: true,
|
||||||
example: example,
|
example: { files },
|
||||||
exampleIdx: exampleIdx + 1,
|
exampleIdx: exampleIdx + 1,
|
||||||
}));
|
}));
|
||||||
}
|
}
|
||||||
|
Loading…
x
Reference in New Issue
Block a user