Skip to content

Commit

Permalink
docs cleanout
Browse files Browse the repository at this point in the history
  • Loading branch information
asprionj committed Apr 15, 2019
1 parent 397521d commit 40ce8e0
Show file tree
Hide file tree
Showing 7 changed files with 71 additions and 43 deletions.
43 changes: 35 additions & 8 deletions docs/src/index.md
Original file line number Diff line number Diff line change
Expand Up @@ -32,10 +32,10 @@ Open a Browser and go to `http://localhost:8000/` to see the content being rende

### Serve docs

A derived function from `serve` that will be convenient to Julia package developpers is `servedocs` which runs `Documenter` along with `LiveServer` to render your docs and will track and render any modifications to your docs.
This can make docs development significantly easier.
A function derived from `serve` that will be convenient to Julia package developpers is `servedocs`. It runs [Documenter.jl](https://github.com/JuliaDocs/Documenter.jl) along with `LiveServer` to render your docs and will track and render any modifications to your docs.
This instantaneous feedback makes writing docs significantly easier and faster.

Assuming you are in `directory/to/YourPackage.jl` and that you have a `docs/` folder as prescribed by [Documenter.jl](https://github.com/JuliaDocs/Documenter.jl), just run:
Assuming you are in `directory/to/YourPackage.jl` and that you have a `docs/` folder as prescribed by `Documenter`, just run:

```julia
julia> using LiveServer
Expand Down Expand Up @@ -66,8 +66,35 @@ Let's assume you have a directory similar to that generated by [`LiveServer.exam
└── blah.html
```
After launching `serve()` in that directory, you can visit the rendered website by navigating to the page `http://localhost:8000/`.
This initiates a GET request from the browser which is caught by a listener loop in LiveServer.
The requested file (here `index.html`) is then sent to the client after injection of a small script which can trigger a reload of the page upon receiving a signal.
If you now modify the page `index.html`, LiveServer will detect that the file has been modified and send a signal to the client which will cause the small script to trigger a page reload.
Calling `serve()` from within this directory starts a file server. It serves
the contents of the directory as a static site, with the folder structure
defining the paths of the URLs. That is, the file `blah.html` can be viewed
at `/pages/blah.html`. When a directory is specified instead of a file,
the server checks whether there is a file `index.html` in this directory and
serves it if available.
Visiting `http://localhost:8000/` makes your browser send a standard HTTP `GET`
request to the server. The server, running a listener loop, receives this
request, looks for `index.html` in the root
folder, and serves it. After serving it, `LiveServer` adds this file to the
list of watched files. That is, whenever this file changes, a callback is
fired (see below). The HTML page may also contain references to style sheets or
pictures, which are then requested by your browser as well. The server
sends them to the browser, and also adds them to the list of watched
files.
**But what about the live reloading?** Triggering a page refresh in the browser
is achieved by a WebSocket connection. The WebSocket API, according to
[MDN](https://developer.mozilla.org/en-US/docs/Web/API/WebSockets_API), is
> an advanced technology that makes it possible to open a two-way interactive
> communication session between the user's browser and a server.
`LiveServer` injects a small piece of JavaScript code into every HTML file
before serving it. This snippet is executed by the browser and opens a WebSocket
connection to the server, which in turn adds it to a list of viewers of this page.
The server can now send the message "update" to all viewers of a page
whenever the page is changed. The code snippet reacts to this message by
triggering a page reload (same as hitting `F5`). The update is triggered by the
callback mentioned above. When the file is not an HTML file, the viewers of _any_
HTML file are updated, since `LiveServer` currently does not keep track of which
HTML files reference what other files.
3 changes: 2 additions & 1 deletion docs/src/lib/internals.md
Original file line number Diff line number Diff line change
Expand Up @@ -47,7 +47,7 @@ LiveServer.is_watched

## Live serving

The exported [`serve`](@ref) and [`setverbose`](@ref) functions are not stated again.
The exported [`serve`](@ref) and [`servedocs`](@ref) functions are not stated again.
The `serve` method instantiates a listener (`HTTP.listen`) in an asynchronous task.
The callback upon an incoming HTTP stream decides whether it is a standard HTTP request or a request for an upgrade to a websocket connection.
The former case is handled by [`LiveServer.serve_file`](@ref), the latter by
Expand All @@ -56,6 +56,7 @@ Finally, [`LiveServer.file_changed_callback`](@ref) is the function passed to th

```@docs
LiveServer.serve_file
LiveServer.ws_upgrade
LiveServer.ws_tracker
LiveServer.file_changed_callback
```
Expand Down
19 changes: 10 additions & 9 deletions docs/src/man/extending_ls.md
Original file line number Diff line number Diff line change
Expand Up @@ -8,8 +8,8 @@ This page explains how to extend `SimpleWatcher <: FileWatcher` and, more genera

In most circumstances, using an instance of the `SimpleWatcher` type with your own _custom callback function_ is what you will want to do.

The `SimpleWatcher` does what you expect in terms of watching files it is told to watch and triggering a function (the _callback_) when a change is detected.
The callback function takes as argument the path of the file that was modified and returns nothing.
The `SimpleWatcher` does what you expect: it watches files for changes and triggers a function (the _callback_) when a change is detected.
The callback function takes as argument the path of the file that was modified and returns `nothing`.

The base callback function ([`LiveServer.file_changed_callback`](@ref)) does only one thing: it sends a signal to the relevant viewers to trigger page reloads.
You will typically want to re-use `file_changed_callback` or copy its code.
Expand All @@ -20,13 +20,13 @@ As an example of a custom callback, here is a simple modified callback mechanism
custom_callback(fp::AbstractString) = (println("Hello!"); file_changed_callback(fp))
```

A more sophisticated customised callback is the one that is used in [`servedocs`](@ref) (see [`servedocs_callback`](@ref)).
A more sophisticated customised callback is the one that is used in [`servedocs`](@ref) (see [`LiveServer.servedocs_callback`](@ref)).
The callback has a different behaviour depending on which file is modified and does a few extra steps before signalling the viewers to reload appropriate pages.

## Writing your own `FileWatcher`

If you decide to write your own `FileWatcher` type, you will need to meet the API.
The easier is probably that you look at the code for `SimpleWatcher` and adapt it to your need.
The easier is probably that you look at the code for [`LiveServer.SimpleWatcher`](@ref) and adapt it to your need.
Let's assume for now that you want to define a `CustomWatcher <: FileWatcher`.

### Fields
Expand All @@ -35,18 +35,19 @@ The only field that is _required_ by the rest of the code is

* `status`: a symbol that must be set to `:interrupted` upon errors in the file watching task

Likely you will want to have some (likely most) of the fields of a `SimpleWatcher` i.e.:
Likely you will want to have some (probably most) of the fields of a `SimpleWatcher` i.e.:

* `callback`: the callback function to be triggered upon an event,
* `task`: the asynchronous file watching task,
* `watchedfiles`: the vector of [`LiveServer.WatchedFile`](@ref) i.e. the paths to the file being watched as long as their time of last modification,
* `sleeptime`: the time to wait before going over the list of `watchedfiles` to check for events, you won't want this to be too small and it's lower bounded to `0.05` in `SimpleWatcher`.
* `watchedfiles`: the vector of [`LiveServer.WatchedFile`](@ref) i.e. the paths to the file being watched as well as their time of last modification,
* `sleeptime`: the time to wait before going over the list of `watchedfiles` to check for changes, you won't want this to be too small and it's lower bounded to `0.05` in `SimpleWatcher`.

Of course you can add any extra field you may want.

### Methods

Subsequently, your `CustomWatcher` may redefine some or all of the following methods (those that aren't will use the default method defined for all `FileWatcher`).
Subsequently, your `CustomWatcher` may redefine some or all of the following methods (those that aren't will use the default method defined for `FileWatcher` and thus
all of its sub-types).

The methods that are _required_ by the rest of the code are

Expand All @@ -55,7 +56,7 @@ The methods that are _required_ by the rest of the code are

You may also want to re-define existing methods such as

* `file_watcher_task!(::FileWatcher)`: the loop that goes over the watched files, it checks for an event (modifications) and trigger the callback function; this will be the `CustomWatcher.task`. If errors happen in this asynchronous task, the `CustomWatcher.status` should be set to `:interrupted` so that all running tasks can be stopped properly.
* `file_watcher_task!(::FileWatcher)`: the loop that goes over the watched files, checking for modifications and triggering the callback function. This task will be referenced by the field `CustomWatcher.task`. If errors happen in this asynchronous task, the `CustomWatcher.status` should be set to `:interrupted` so that all running tasks can be stopped properly.
* `set_callback!(::FileWatcher, ::Function)`: a helper function to bind a watcher with a callback function.
* `is_running(::FileWatcher)`: a helper function to check whether `CustomWatcher.task` is done.
* `is_watched(::FileWatcher, ::AbstractString)`: check if a file is watched by the watcher.
Expand Down
17 changes: 11 additions & 6 deletions docs/src/man/functionalities.md
Original file line number Diff line number Diff line change
Expand Up @@ -20,11 +20,12 @@ julia> serve()

which will make the content of the folder available to be viewed in a browser.

You can specify the port that the server should listen to (default is `8000`) as well as the directory to serve if not the current one.
There is also a `verbose` argument if you want to see messages being displayed on file changes and
You can specify the port that the server should listen to (default is `8000`) as well as the directory to serve (if not the current one) as keyword arguments.
There is also a `verbose` keyword-argument if you want to see messages being displayed on file changes and
connections.

More interestingly, you can specify the `filewatcher` which allows to define what will trigger the messages to the client and ultimately cause the active browser tabs to reload.
More interestingly, you can optionally specify the `filewatcher` (the only
regular argument) which allows to define what will trigger the messages to the client and ultimately cause the active browser tabs to reload.
By default, it is file modifications that will trigger page reloads but you may want to write your own file watcher to perform additional actions upon file changes or trigger browser reload differently.

See the section on [Extending LiveServer](@ref) for more informations.
Expand Down Expand Up @@ -55,9 +56,9 @@ Let's assume the structure of your package looks like
```

The standard way of running `Documenter.jl` is to run `make.jl`, wait for completion and then maybe use a third party tool to see the output.
The standard way of running `Documenter.jl` is to run `make.jl`, wait for completion and then use a standard browser or maybe some third party tool to see the output.

With `servedocs` however, you can edit the `.md` files in your `docs/src` and see the changes being applied directly in your browser which makes writing documentation easier.
With `servedocs` however, you can edit the `.md` files in your `docs/src` and see the changes being applied directly in your browser which makes writing documentation faster and easier.
To launch it, navigate to `YourPackage.jl/` and simply

```julia-repl
Expand All @@ -70,4 +71,8 @@ Upon modifying a `.md` file (e.g. updating `docs/src/index.md`), the `make.jl` w

!!! note

the first pass of Documenter.jl takes a few seconds but subsequent passes are quite fast so that the workflow with `Documenter.jl`+`LiveServer.jl` is pretty quick.
The first pass of `Documenter.jl` takes a few seconds to complete, but subsequent passes are quite fast so that the workflow with `Documenter.jl`+`LiveServer.jl` is pretty quick.

The first pass collects all information in the code (i.e. docstrings), while
subsequent passes only consider changes in the markdown (`.md`) files. This
restriction is necessary to achieve a fast update behavior.
15 changes: 4 additions & 11 deletions src/file_watching.jl
Original file line number Diff line number Diff line change
Expand Up @@ -48,7 +48,7 @@ abstract type FileWatcher end
SimpleWatcher([callback]; sleeptime::Float64=0.1) <: FileWatcher
A simple file watcher. You can specify a callback function, receiving the path of each file that
has changed as an `AbstractString`, at construction or later by the API function described below.
has changed as an `AbstractString`, at construction or later by the API function [`set_callback!`](@ref).
The `sleeptime` is the time waited between two runs of the loop looking for changed files, it is
constrained to be at least 0.05s.
"""
Expand All @@ -60,13 +60,6 @@ mutable struct SimpleWatcher <: FileWatcher
status::Symbol # set to :interrupted as appropriate (caught by server)
end

"""
SimpleWatcher([callback]; sleeptime)
Instantiate a new `SimpleWatcher` with an optional callback triggered upon file change.
The `sleeptime` argument can be used to determine how often to check for file change (default is
every 0.1 second and minimum is 0.05).
"""
SimpleWatcher(callback::Union{Nothing,Function}=nothing; sleeptime::Float64=0.1) =
SimpleWatcher(callback, nothing, max(0.05, sleeptime), Vector{WatchedFile}(), :runnable)

Expand Down Expand Up @@ -120,7 +113,7 @@ end
"""
set_callback!(fw::FileWatcher, callback::Function)
Mandatory API function to set or change the callback function being executed upon a file change.
Set or change the callback function being executed upon a file change.
Can be "hot-swapped", i.e. while the file watcher is running.
"""
function set_callback!(fw::FileWatcher, callback::Function)
Expand All @@ -135,7 +128,7 @@ end
"""
is_running(fw::FileWatcher)
Optional API function to check whether the file watcher is running.
Checks whether the file watcher is running.
"""
is_running(fw::FileWatcher) = (fw.task !== nothing) && !istaskdone(fw.task)

Expand Down Expand Up @@ -182,7 +175,7 @@ end
"""
is_watched(fw::FileWatcher, f_path::AbstractString)
Check whether a file `f_path` is being watched by file watcher `fw`.
Checks whether the file specified by `f_path` is being watched.
"""
is_watched(fw::FileWatcher, f_path::AbstractString) = any(wf -> wf.path == f_path, fw.watchedfiles)

Expand Down
15 changes: 8 additions & 7 deletions src/server.jl
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@ end
"""
file_changed_callback(f_path::AbstractString)
Function reacting to the change of a file `f_path`. Is set as callback for the file watcher.
Function reacting to the change of the file at `f_path`. Is set as callback for the file watcher.
"""
function file_changed_callback(f_path::AbstractString)
VERBOSE[] && println("ℹ [LiveUpdater]: Reacting to change in file '$f_path'...")
Expand Down Expand Up @@ -63,10 +63,10 @@ end
Handler function for serving files. This takes a file watcher, to which files to be watched can be
added, and a request (e.g. a path entered in a tab of the browser), and converts it to the
appropriate file system path. If the path corresponds to a HTML file, it will inject the reloading
script (see file `client.js`) at the end of its body, i.e. directly before the </body> tag.
`<script>` (see file `client.html`) at the end of its body, i.e. directly before the `</body>` tag.
All files served are added to the file watcher, which is responsible to check whether they're
already watched or not. Finally the file is served via a 200 (successful) response. If the file
does not exist, a response with status 404 and message "404 not found" is sent.
does not exist, a response with status 404 and an according message is sent.
"""
function serve_file(fw, req::HTTP.Request)
fs_path = get_fs_path(req.target)
Expand Down Expand Up @@ -162,21 +162,22 @@ end


"""
serve(filewatcher; port, directory, coreloop)
serve(filewatcher; port=8000, dir="", verbose=false, coreloop=(c,fw)->nothing)
Main function to start a server at `http://localhost:port` and render what is in the current
directory. (See also [`example`](@ref) for an example folder).
* `filewatcher` is a file watcher implementing the API described for [`SimpleWatcher`](@ref) and messaging the viewers (web sockets) upon detecting file changes.
* `filewatcher` is a file watcher implementing the API described for [`SimpleWatcher`](@ref) (which also is the default) and messaging the viewers (via WebSockets) upon detecting file changes.
* `port` is an integer between 8000 (default) and 9000.
* `directory` specifies where to launch the server if not the current working directory
* `dir` specifies where to launch the server if not the current working directory.
* `verbose` is a boolean switch to make the server print information about file changes and connections.
* `coreloopfun` specifies a function which can be run every 0.1 second while the liveserver is going; it takes two arguments: the cycle counter and the filewatcher. By default the coreloop does nothing.
# Example
```julia
LiveServer.example()
serve(port=8080, dir="example")
serve(port=8080, dir="example", verbose=true)
```
If you open a browser to `http://localhost:8080/`, you should see the `index.html` page from the
Expand Down
2 changes: 1 addition & 1 deletion src/utils.jl
Original file line number Diff line number Diff line change
Expand Up @@ -90,7 +90,7 @@ end
"""
setverbose(b)
Set the verbosity of LiveServer to either true (showing messages upon events) or false (default).
Set the verbosity of LiveServer to either `true` (showing messages upon events) or `false` (default).
"""
setverbose(b::Bool) = (VERBOSE.x = b)

Expand Down

0 comments on commit 40ce8e0

Please sign in to comment.