Skip to content

Commit

Permalink
Merge pull request #115 from IvanMathy/feat/lazyScripts
Browse files Browse the repository at this point in the history
Feat/lazy scripts
  • Loading branch information
IvanMathy authored Jul 11, 2020
2 parents 3af3036 + 2e13c13 commit 539c8f3
Show file tree
Hide file tree
Showing 16 changed files with 146 additions and 47 deletions.
8 changes: 2 additions & 6 deletions Boop/Boop.xcodeproj/project.pbxproj
Original file line number Diff line number Diff line change
Expand Up @@ -584,7 +584,7 @@
CODE_SIGN_STYLE = Manual;
COMBINE_HIDPI_IMAGES = YES;
DEVELOPMENT_TEAM = "";
FRAMEWORK_SEARCH_PATHS = "$(PROJECT_DIR)/../Carthage/Build/Mac/**";
FRAMEWORK_SEARCH_PATHS = "";
INFOPLIST_FILE = BoopTests/Info.plist;
LD_RUNPATH_SEARCH_PATHS = (
"$(inherited)",
Expand All @@ -607,7 +607,7 @@
CODE_SIGN_STYLE = Manual;
COMBINE_HIDPI_IMAGES = YES;
DEVELOPMENT_TEAM = "";
FRAMEWORK_SEARCH_PATHS = "$(PROJECT_DIR)/../Carthage/Build/Mac/**";
FRAMEWORK_SEARCH_PATHS = "";
INFOPLIST_FILE = BoopTests/Info.plist;
LD_RUNPATH_SEARCH_PATHS = (
"$(inherited)",
Expand Down Expand Up @@ -748,7 +748,6 @@
CURRENT_PROJECT_VERSION = 2;
DEVELOPMENT_TEAM = RLZ8XBTX7G;
FRAMEWORK_SEARCH_PATHS = (
"$(PROJECT_DIR)/../Carthage/Build/Mac",
"$(inherited)",
"$(PROJECT_DIR)/Boop/Frameworks",
"$(PROJECT_DIR)",
Expand Down Expand Up @@ -780,7 +779,6 @@
DEVELOPMENT_TEAM = "";
ENABLE_HARDENED_RUNTIME = YES;
FRAMEWORK_SEARCH_PATHS = (
"$(PROJECT_DIR)/../Carthage/Build/Mac",
"$(inherited)",
"$(PROJECT_DIR)/Boop/Frameworks",
"$(PROJECT_DIR)",
Expand Down Expand Up @@ -811,7 +809,6 @@
CURRENT_PROJECT_VERSION = 3;
DEVELOPMENT_TEAM = "";
FRAMEWORK_SEARCH_PATHS = (
"$(PROJECT_DIR)/../Carthage/Build/Mac",
"$(inherited)",
"$(PROJECT_DIR)/Boop/Frameworks",
"$(PROJECT_DIR)",
Expand Down Expand Up @@ -843,7 +840,6 @@
CURRENT_PROJECT_VERSION = 3;
DEVELOPMENT_TEAM = "";
FRAMEWORK_SEARCH_PATHS = (
"$(PROJECT_DIR)/../Carthage/Build/Mac",
"$(inherited)",
"$(PROJECT_DIR)/Boop/Frameworks",
"$(PROJECT_DIR)",
Expand Down
4 changes: 3 additions & 1 deletion Boop/Boop/Controllers/PopoverViewController.swift
Original file line number Diff line number Diff line change
Expand Up @@ -90,7 +90,7 @@ class PopoverViewController: NSViewController {

window?.makeFirstResponder(self.searchField)
// This doesn't work for some reason.
self.searchField.moveToEndOfLine(nil)
//self.searchField.moveToEndOfLine(nil)
}

guard didSomething else {
Expand Down Expand Up @@ -134,6 +134,8 @@ class PopoverViewController: NSViewController {
self.enabled = false
self.tableHeightConstraint.animator().constant = 0

tableViewController.results = []

appDelegate.setPopover(isOpen: false)
}

Expand Down
11 changes: 4 additions & 7 deletions Boop/Boop/System/Models/Script+Require.swift
Original file line number Diff line number Diff line change
Expand Up @@ -14,12 +14,9 @@ extension Script {
static private let boopPrefix = "@boop/"
static private let moduleExt = ".js"

func setupRequire() {
func setupRequire(context: JSContext) {
let require: @convention(block) (String) -> (JSValue?) = {
path in

let savedExports = self.context.objectForKeyedSubscript("exports")

[unowned self] path in

var path = path

Expand Down Expand Up @@ -71,10 +68,10 @@ extension Script {
"""

return self.context.evaluateScript(wrappedCode, withSourceURL: url)
return JSContext.current().evaluateScript(wrappedCode, withSourceURL: url)
}

self.context.setObject(require, forKeyedSubscript: "require" as NSString)
context.setObject(require, forKeyedSubscript: "require" as NSString)

}

Expand Down
34 changes: 18 additions & 16 deletions Boop/Boop/System/Models/Script.swift
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,24 @@ class Script: NSObject {
var url: URL
var scriptCode: String

var context: JSContext
lazy var context: JSContext = { [unowned self] in
let context: JSContext = JSContext()
context.name = self.name ?? "Unknown Script"

context.exceptionHandler = { [unowned self] context, exception in
let message = "[\(self.name ?? "Unknown Script")] Error: \(exception?.toString() ?? "Unknown Error") "
print(message)
self.onScriptError(message: message)
}

self.setupRequire(context: context)

context.setObject(ScriptExecution.self, forKeyedSubscript: "ScriptExecution" as NSString)

context.evaluateScript(self.scriptCode, withSourceURL: url)

return context
}()

lazy var main: JSValue = {
return context.objectForKeyedSubscript("main")
Expand Down Expand Up @@ -46,22 +63,7 @@ class Script: NSObject {
self.icon = (parameters["icon"] as? String)?.lowercased()
self.bias = parameters["bias"] as? Double

context = JSContext()
context.name = self.name ?? "Unknown Script"

super.init();

context.exceptionHandler = { context, exception in
let message = "[\(self.name ?? "Unknown Script")] Error: \(exception?.toString() ?? "Unknown Error") "
print(message)
self.onScriptError(message: message)
}

self.setupRequire()

context.setObject(ScriptExecution.self, forKeyedSubscript: "ScriptExecution" as NSString)

context.evaluateScript(script, withSourceURL: url)

// We set the delegate after the initial eval to avoid
// showing init errors from scripts at launch.
Expand Down
2 changes: 1 addition & 1 deletion Boop/Boop/System/ScriptManager.swift
Original file line number Diff line number Diff line change
Expand Up @@ -97,7 +97,7 @@ class ScriptManager: NSObject {

let json = try JSONSerialization.jsonObject(with: meta.data(using: .utf8)!, options: .allowFragments) as! [String: Any]

let scriptObject = Script.init(url: url, script: script, parameters: json, builtIn: builtIn, delegate: self)
let scriptObject = Script(url: url, script: script, parameters: json, builtIn: builtIn, delegate: self)

scripts.append(scriptObject)

Expand Down
6 changes: 6 additions & 0 deletions Boop/Documentation/CustomScripts.md
Original file line number Diff line number Diff line change
Expand Up @@ -125,10 +125,16 @@ state.postError("Invalid XML")
If the user selects more than one part of the text (by using `cmd` or `alt` while selecting), the script will be called multiple times as it uses either `selection` or `text`. If `fullText` is read or written to, the loop stops even if there is more unevaluated selections.


## Advanced features

#### Modules

Starting with version 1.2.0, modules can be imported in Boop scripts. See the [Modules page](Modules.md) for details.

#### Debugging

Starting with version 1.2.0, scripts can inspected and attached to by javascript console/debugger. See the [Debugging Scripts page](Debugging.md) for details.


## Limitations

Expand Down
108 changes: 108 additions & 0 deletions Boop/Documentation/Debugging.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,108 @@
# Debugging Scripts


___
**<p align=center>Debugging is only available in dev builds, in Boop version 1.2.0 and above.</p>**
___

## Getting a dev build

To use debugging tools, you need a dev build of Boop. This is a restriction of JavascriptCore (the thing that runs scripts) that I have not yet found a way to avoid.

Since dev builds cannot be signed, they cannot be distributed and you need to make your own. For this, you'll need Xcode (free on the Mac App Store or the (Apple Developer Website)[https://developer.apple.com]. Once you have it, follow these steps:

- Download the Boop source code from Github or clone it locally using git. You can find a compressed version of the source code on the (releases page)[https://github.com/IvanMathy/Boop/releases/].

- In the source code, find and open `Boop.xcodeproj` with Xcode. (It's in the `Boop` folder, surprisingly)

- On the top left corner of the Xcode window, hit the play button. The dev build should start.

## Preparing the debugger

To see your script in the debugger, you'll need to run that script at least once. It does not matter if it doesn't work, Boop just needs to know you want to use it.

After running your script, open Safari (the web browser that comes bundled on macOS). If you've never used the developer tools, open Safari's preferences, go to the `Advanced` tab and enable `Show Develop menu in menu bar`.


<p align="center">
<img src="Images/safari.png?raw=true" width="663" alt="UI Screenshot">
</p>



## Connecting the debugger

Once set up, you should be able to see your script in the `Develop` menu, in the submenu with the same name as your computer (in the screenshot, it's `Ivan MKII` - yours will be different, most likely), under the `Boop Section`. Select the script you'd like to debug.

<p align="center">
<img src="Images/developMenu.png?raw=true" width="663" alt="UI Screenshot">
</p>

If you see `No Inspectable Applications`, please double check that:

- Boop is running
- It is a dev build
- You used your script at least once

## Using the debugger

The debugger is the same as the built-in one from Safari, and works just like any in-browser inspector. The central panel shows the source of your script. *Editing it there will not affect the script within Boop, and any change will be lost.*

<p align="center">
<img src="Images/debugger.png?raw=true" width="663" alt="UI Screenshot">
</p>


## Modules

Modules are show as separate files in the left panel of the debugger. You'll notice that the module's source will have some added code above and below, surrounded with tags like this:

```javascript
/***********************************
* Start of Boop's wrapper *
***********************************/
```

This is how Boop makes sure modules are safe and compatible. The wrapper is added at runtime. It ain't pretty but it gets the job done.

<p align="center">
<img src="Images/modules.png?raw=true" width="663" alt="UI Screenshot">
</p>


## Console

When the debugger is active, the `console` object gets enabled. You can use the bottom panel or the `Console` tab to read the output and use the interactive prompt.

<p align="center">
<img src="Images/console.png?raw=true" width="663" alt="UI Screenshot">
</p>

Anything posted to the console prior to opening the debugger is recorded and will be displayed when you open it. Therefore, you don't need to rush to open the debugger to see initialization messages for example.

Please note that `console` is not available in non-debug builds, and using it will cause your script to throw an exception.

## Breakpoints

You can use breakpoints in the debugger, just like you would in any programming environment. The best way to do that is to place it in somewhere called by the `main()` function.

<p align="center">
<img src="Images/breakpoints.png?raw=true" width="663" alt="UI Screenshot">
</p>

When a breakpoint is active, you can see the scope in the right panel, step over/into in the left panel, or interact with the console in the bottom panel.

While the breakpoint is active, Boop will become unresponsive. To make Boop usable again, continue the script execution by pressing the play button at the top left of the debugger.

## Reloading scripts

When reloading scripts within Boop, the debugger **will not** automatically reload. If you want to check a new version of your script, close the debugger, run the scripts again for it to register, then re-open the debugger.

Leaving the debugger open will not release the previous version of the script, and therefore duplicates will show in the debugging menu.

When reopening previously opened scripts (even from a previous development session), the debugger will remember any previously applied breakpoint. Keep that in mind when debugging a module, as it's easy to forget you had a breakpoint in there.


## Final wisdom

Just like any software development environment, there is inherent danger in using those tools if you don't know what you're doing. Use at your own risk, avoid clicking stuff you don't know, and be nice to people.
Binary file added Boop/Documentation/Images/breakpoints.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added Boop/Documentation/Images/console.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added Boop/Documentation/Images/debugger.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added Boop/Documentation/Images/developMenu.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added Boop/Documentation/Images/modules.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added Boop/Documentation/Images/safari.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
3 changes: 2 additions & 1 deletion Boop/Documentation/Readme.md
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ Hey there! Thanks for trying out Boop. This documentation should hopefully help

- [Custom Scripts](CustomScripts.md)
- [Modules](Modules.md)
- [Debugging Scripts](Debugging.md)

## Getting Boop

Expand Down Expand Up @@ -45,7 +46,7 @@ Yes! Simply follow the instruction in the [Custom Scripts page](CustomScripts.md

### Does Boop collect data on me?

No. The only time Boop communicates outside of itself is to check whether a new version is available. This is done by fetching a static .json file, with no additional data passed along. If you downloaded Boop through the Mac App Store, it's possible that standard data and/or crash reports get sent back to Apple and shared with me if you enabled App Analytics sharing.
No. The only time Boop communicates outside of itself is to check whether a new version is available. This is done by fetching a static .json file, with no additional data passed along. If you downloaded Boop through the Mac App Store, it's possible that standard data and/or crash reports get sent back to Apple and shared with me if you enabled App Analytics sharing, though I have not seen that happen yet.

### How can I report a problem?

Expand Down
4 changes: 2 additions & 2 deletions Boop/UI/Base.lproj/MainMenu.xib
Original file line number Diff line number Diff line change
Expand Up @@ -666,8 +666,8 @@ Gw
<toolbarItem implicitItemIdentifier="NSToolbarFlexibleSpaceItem" id="ri3-8q-QoT"/>
<toolbarItem implicitItemIdentifier="C5459F48-A2E4-4A46-BA5F-7368C65F8AC9" label="Custom View" paletteLabel="Custom View" tag="-1" sizingBehavior="auto" id="mG6-GU-aNe">
<nil key="toolTip"/>
<customView key="view" misplaced="YES" id="zMi-QK-dkF" customClass="StatusView" customModule="Boop" customModuleProvider="target">
<rect key="frame" x="0.0" y="14" width="200" height="20"/>
<customView key="view" id="zMi-QK-dkF" customClass="StatusView" customModule="Boop" customModuleProvider="target">
<rect key="frame" x="0.0" y="14" width="208" height="20"/>
<autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMinY="YES"/>
<subviews>
<stackView distribution="equalCentering" orientation="horizontal" alignment="centerY" spacing="0.0" horizontalStackHuggingPriority="249.99998474121094" verticalStackHuggingPriority="249.99998474121094" detachesHiddenViews="YES" translatesAutoresizingMaskIntoConstraints="NO" id="Nlt-yx-S75">
Expand Down
13 changes: 0 additions & 13 deletions Boop/UI/Preferences.storyboard
Original file line number Diff line number Diff line change
Expand Up @@ -290,19 +290,6 @@
</objects>
<point key="canvasLocation" x="386" y="722"/>
</scene>
<!--View Controller-->
<scene sceneID="TVB-in-Pdn">
<objects>
<viewController id="IHQ-4i-6gQ" sceneMemberID="viewController">
<view key="view" id="MrJ-FT-7dr">
<rect key="frame" x="0.0" y="0.0" width="450" height="300"/>
<autoresizingMask key="autoresizingMask"/>
</view>
</viewController>
<customObject id="ALB-1N-M3I" userLabel="First Responder" customClass="NSResponder" sceneMemberID="firstResponder"/>
</objects>
<point key="canvasLocation" x="28" y="-62"/>
</scene>
</scenes>
<resources>
<image name="NSAdvanced" width="32" height="32"/>
Expand Down

0 comments on commit 539c8f3

Please sign in to comment.