From fa8d0a3e97c31ea2523d73923af33e8764394efa Mon Sep 17 00:00:00 2001 From: rxi Date: Wed, 5 Mar 2014 20:52:47 +0000 Subject: [PATCH] First commit --- .gitignore | 4 +++ LICENSE | 20 +++++++++++ README.md | 54 +++++++++++++++++++++++++++++ lurker.lua | 100 +++++++++++++++++++++++++++++++++++++++++++++++++++++ 4 files changed, 178 insertions(+) create mode 100644 .gitignore create mode 100644 LICENSE create mode 100644 README.md create mode 100644 lurker.lua diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..d19ba21 --- /dev/null +++ b/.gitignore @@ -0,0 +1,4 @@ +lume.lua +__* +*.tmp +*.swp diff --git a/LICENSE b/LICENSE new file mode 100644 index 0000000..dd39d52 --- /dev/null +++ b/LICENSE @@ -0,0 +1,20 @@ +Copyright (c) 2014, rxi + + +Permission is hereby granted, free of charge, to any person obtaining a copy of +this software and associated documentation files (the "Software"), to deal in +the Software without restriction, including without limitation the rights to +use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies +of the Software, and to permit persons to whom the Software is furnished to do +so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. diff --git a/README.md b/README.md new file mode 100644 index 0000000..92481ac --- /dev/null +++ b/README.md @@ -0,0 +1,54 @@ +# lurker + +Automatically hotswaps changed Lua files into a running +[LÖVE](http://love2d.org) project. + + +## Installation + +Drop the [lurker.lua](lurker.lua?raw=1) and +[lume.lua](https://raw.github.com/rxi/lume/master/lume.lua) files into an +existing project and add the following line inside the `love.update()` +function: +```lua +require("lurker").update() +``` +lurker will automatically detect changed files and hotswap them into the +running project. + + +## Additional Functionality + +To more easily make use of additional functionality, the lurker module can be +set to a variable when it is required into the project: +```lua +lurker = require "lurker" +``` + +### lurker.scan() +As opposed to using the `lurker.update()` function -- such to avoid the +overhead of repeatedly polling for file changes -- you can instead opt to +trigger a scan of the directory by calling `lurker.scan()` manually. If the +scan detects any changes a hotswap is performed. + +### lurker.preswap +`lurker.preswap` can be set to a function. This function is called before a +hotswap occurs and is passed the name of the file which will be swapped. +```lua +lurker.preswap = function(f) print("File " .. f .. " swapping...") end +``` + +### lurker.postswap +`lurker.postswap` can be set to a function. This function is called after a +hotswap occurs and is passed the name of the file which was swapped. +```lua +lurker.postswap = function(f) print("File " .. f .. " was swapped") end +``` + +### lurker.interval +The interval in seconds for how often the scan of the directory is performed. +This is `.5` by default. + +### lurker.path +The directory which is scanned for changes. This is `.` (The project's root) by +default. diff --git a/lurker.lua b/lurker.lua new file mode 100644 index 0000000..930b6f2 --- /dev/null +++ b/lurker.lua @@ -0,0 +1,100 @@ +-- +-- lurker +-- +-- Copyright (c) 2014, rxi +-- +-- This library is free software; you can redistribute it and/or modify it +-- under the terms of the MIT license. See LICENSE for details. +-- + +local lume = require "lume" + +local lurker = { _version = "1.0.0" } + + +local dir = love.filesystem.enumerate or love.filesystem.getDirectoryItems +local isdir = love.filesystem.isDirectory +local time = love.timer.getTime or os.time +local lastmodified = love.filesystem.getLastModified + + +function lurker.init() + lurker.print("Initing lurker") + lurker.path = "." + lurker.preswap = function() end + lurker.postswap = function() end + lurker.interval = .5 + lurker.last = 0 + lurker.files = {} + lume.each(lurker.getchanged(), lurker.resetfile) + return lurker +end + + +function lurker.print(...) + print("[lurker] " .. lume.format(...)) +end + + +function lurker.listdir(path, recursive, skipdotfiles) + path = (path == ".") and "" or path + local function fullpath(x) return path .. "/" .. x end + local t = {} + for _, f in pairs(lume.map(dir(path), fullpath)) do + if not skipdotfiles or not f:match("/%.[^/]*$") then + if recursive and isdir(f) then + lume.merge(t, lurker.listdir(f, true, true)) + else + table.insert(t, lume.trim(f, "/")) + end + end + end + return t +end + + +function lurker.update() + local diff = time() - lurker.last + if diff > lurker.interval then + lurker.last = lurker.last + diff + lurker.scan() + end +end + + +function lurker.getchanged() + local function fn(f) + return f:match("%.lua$") and lurker.files[f] ~= lastmodified(f) + end + return lume.filter(lurker.listdir(lurker.path, true, true), fn) +end + + +function lurker.modname(f) + return (f:gsub("%.lua$", ""):gsub("[/\\]", ".")) +end + + +function lurker.resetfile(f) + lurker.files[f] = lastmodified(f) +end + + +function lurker.scan() + for _, f in pairs(lurker.getchanged()) do + lurker.print("Hotswapping '{f}'...", {f = f}) + lurker.preswap(f) + local modname = lurker.modname(f) + local t, ok, err = lume.time(lume.hotswap, modname) + if ok then + lurker.print("Swapped '{f}' in {t} secs", {f = f, t = t}) + else + lurker.print("Failed to swap '{f}' : {e}", {f = f, e = err}) + end + lurker.resetfile(f) + lurker.postswap(f) + end +end + + +return lurker.init()