Remove dependencies, improve documentation.

This commit is contained in:
Greg 2019-05-29 22:36:49 -04:00
parent 370a2eb604
commit 8ce3b6a6de
20 changed files with 293 additions and 84 deletions

160
README.md
View File

@ -2,7 +2,7 @@
Create Go language bindings for Objective-C. Create Go language bindings for Objective-C.
Using NSWrap, you can easily work with many MacOS interfaces, subclasses, Using NSWrap, you can work with MacOS interfaces, subclasses,
library functions, protocols and delegates entirely in Go. library functions, protocols and delegates entirely in Go.
# Getting Started # Getting Started
@ -13,20 +13,15 @@ NSWrap runs on MacOS and requires `clang` (from the XCode command line
tools) and the MacOS system header files. tools) and the MacOS system header files.
```sh ```sh
go get git.wow.st/gmp/nswrap/... go get git.wow.st/gmp/nswrap
``` ```
From your `go` source directory, type: The `nswrap` command line tool should now be installed in your `go/bin` path.
```sh
cd git.wow.st/gmp/nswrap
go install
```
Since NSWrap uses `clang` to generate an AST from Objective-C input files, you Since NSWrap uses `clang` to generate an AST from Objective-C input files, you
will need to install XCode and its associated command line tools. Enter will need to install XCode and its associated command line tools. Enter
`clang --version` from your terminal prompt to see if you have it installed `clang --version` from your terminal prompt to see if you have it installed.
already. You will also need to have the Objective-C header files for the You will also need the Objective-C header files for the
various frameworks you want to use. Look for them in various frameworks you want to use. Look for them in
`/System/Library/Frameworks/*/Headers`. `/System/Library/Frameworks/*/Headers`.
@ -90,8 +85,8 @@ package as your automatically generated bindings.
NSWrap will create bindings for all classes identified in the `classes` NSWrap will create bindings for all classes identified in the `classes`
directive of the configuration file. All of the class and instance methods directive of the configuration file. All of the class and instance methods
are bound to Go and all types identified in the process are wrapped are bound to Go and all types identified in the process are wrapped
in Go types (as described below), except for methods that contain prohibited in Go types (as described below), except for methods that contain unsupported
return types or paramater types (such as blocks and function pointers). return types or paramater types such as blocks and function pointers.
```go ```go
s1 := ns.NSStringAlloc() // allocate and autorelease an instance of NSString s1 := ns.NSStringAlloc() // allocate and autorelease an instance of NSString
@ -106,21 +101,20 @@ with the class name, and, if necessary, disambiguated for overloaded
Objective-C methods. Any redundant initial Objective-C methods. Any redundant initial
characters are elided (e.g. the Objective-C characters are elided (e.g. the Objective-C
`[NSString stringWithString:aString]` is shortened in Go to `[NSString stringWithString:aString]` is shortened in Go to
`ns.NSStringWithString(aString)`). Instance methods are carried over `ns.NSStringWithString(aString)`). Instance methods are converted to
as-is but in TitleCase, and disambiguated for method overloading as described TitleCase and disambiguated for method overloading as described below.
below.
Note that while return types and parameter types needed for the binding will Note that while return types and parameter types needed for the binding will
be defined and wrapped for you in Go types, be defined and wrapped for you in Go types,
you will not get any of their methods you will not get any of their methods
unless those types also appear in your NSWrap configuration file. unless those types also appear in your NSWrap configuration file.
For example, the `NSDictionaryWithObjects(...)` constructor takes two `NSArray` For example, the `[NSDictionary WithObjects: forKeys:]` constructor takes two
parameters, so if you want to use it you will probably want `NSArray` parameters, so if you want to use it from Go you will probably want
to have `NSArray` in your configuration file in addition to `NSDictionary`. to have `NSArray` in your configuration file in addition to `NSDictionary`.
## Overloaded Methods ## Overloaded Methods
Because Go does not allow overloaded function definitions, NSWrap automatically Because Go does not allow overloaded functions, NSWrap automatically
disambiguates overloaded method names as required. disambiguates overloaded method names as required.
This is done by successively adding parameter names onto the end of the Go This is done by successively adding parameter names onto the end of the Go
function name until a unique name is created. function name until a unique name is created.
@ -170,7 +164,7 @@ Go interface.
## Working With NSObject and its Descendants ## Working With NSObject and its Descendants
Objective-C Objects are represented in Go by a type and an interface as Objective-C objects are represented in Go by a type and an interface as
follows: follows:
```go ```go
@ -191,8 +185,8 @@ and therefore implement `NSObject`.
The `Id` type in Go represents the Objective-C type `id`, which is a pointer The `Id` type in Go represents the Objective-C type `id`, which is a pointer
to an Objective-C object. Because `cgo` does not understand this type, to an Objective-C object. Because `cgo` does not understand this type,
NSWrap will always translate it to a `void*` on the C side. NSWrap will always translate it to a `void*` on the C side.
The `NSObject` interface in Go allows any `NS` type to be used with The `NSObject` interface in Go allows any type that directly or indirectly
generic Objective-C functions. For example: embeds `Id` to be used with generic Objective-C functions. For example:
```go ```go
o1 := ns.NSStringWithGoString("my string") o1 := ns.NSStringWithGoString("my string")
@ -202,15 +196,15 @@ a := ns.NSMutableArrayWithObjects(o1,s1)
Since `NSString` and `NSSet` in Go both implement the `NSObject` interface, Since `NSString` and `NSSet` in Go both implement the `NSObject` interface,
they can both be used as parameters to the `NSMutableArray` constructor. they can both be used as parameters to the `NSMutableArray` constructor.
This will help you, too, with delegates This will help you, too, when working with delegates
(see below). Classes that accept delegates will generally accept any (see below). Classes that accept delegates will generally accept any
`NSObject` in ther `initWithDelegate()` or `setDelegate()` methods, and `NSObject` in their `initWithDelegate()` or `setDelegate()` methods, and
may or may not test at runtime if the provided object actually may or may not test at runtime if the provided object actually
implements the required delegate protocol. implements the required delegate protocol.
* Inheritance * Inheritance
Objective-C permits single inheritance. In Go, this is modeled using Objective-C only provides single inheritance. In Go, this is modeled using
embedding. Top level objects that inherit from `NSObject` in Objective-C embedding. Top level objects that inherit from `NSObject` in Objective-C
embed the Go type `Id` and therefore implement the `NSObject` Go interface. embed the Go type `Id` and therefore implement the `NSObject` Go interface.
Other objects embed their superclass. For example: Other objects embed their superclass. For example:
@ -245,22 +239,23 @@ receivers. Go will automatically find the indirectly embedded `NSView` and
call the right method. call the right method.
Go's type inference appears to be slightly broken (as of 1.12.1) because Go's type inference appears to be slightly broken (as of 1.12.1) because
the following does not work. Look out for this if you are getting type the following does not work. Look out for this if you like to chain your
errors: `Alloc` and `Init` methods and are getting type errors:
```go ```go
//DO NOT DO THIS //DO NOT DO THIS
b := ns.NSButtonAlloc().InitWithFrame(ns.MakeRect(100,100,200,200)) b := ns.NSButtonAlloc().InitWithFrame(ns.MakeRect(100,100,200,200))
//For some reason Go thinks b has type ns.NSView, because InitWithFrame is defined for ns.NSView, even though //For some reason Go thinks b has type ns.NSView, because InitWithFrame is defined for ns.NSView,
//NSButtonAlloc() returns an ns.NSButton. //even though NSButtonAlloc() returns an ns.NSButton.
``` ```
Go has no trouble finding embedded methods for your `NSButton` and will Go has no trouble finding embedded methods for your `NSButton` and will
happily search up the chain through `NSControl`, `NSView`, `NSResponder` and happily search up the chain through `NSControl`, `NSView`, `NSResponder` and
`NSObject` and all of their associated protocols and categories. As of this `NSObject` and all of their associated protocols and categories. As of this
writing, on MacOS 10.13.6, NSWrap binds 90 instance methods for `NSObject`, writing, on MacOS 10.13.6, NSWrap binds 90 instance methods for `NSObject`,
so things like `Hash()`, `IsEqualTo()`, `ClassName()` and many many so things like `Hash()`, `IsEqualTo()`, `ClassName()`, `RespondsToSelector`
others are available and can be called on any object directly from Go. and many many others are available and can be called on any object directly
from Go.
Go does not perform the same type Go does not perform the same type
magic when you use variables as function or method parameters. magic when you use variables as function or method parameters.
@ -271,18 +266,20 @@ an `NSView` type, you need to explicitly pass the embedded `NSView`
NSWrap creates a method for `Id` allowing objects to be converted NSWrap creates a method for `Id` allowing objects to be converted
at run-time to any other class. You will need this for Enumerators, which at run-time to any other class. You will need this for Enumerators, which
always return `Id`. See below under Enumerators for an example, but make always return `Id`. See below under Enumerators for an example, but make
sure you know (or test) what type your objects are before converting them, sure you know (or test) what type your objects are before converting them.
or else you will get an exception from the Objective-C runtime. You can
implement a somewhat less convenient version of a Go type switch this way.
Because `Id` can be converted to any type, and every object in the Foundation Because `Id` can be converted to any type, and every object in the Foundation
classes inherits from `Id`, it is possible to send any message to any classes inherits from `Id`, it is possible to send any message to any
object, if you are feeling lucky. You are going to have to explicitly object, if you are feeling lucky. If you are not lucky you will get an
exception from the Objective-C runtime. You are going to have to explicitly
convert your object to the wrong type before the compiler will let you do this. convert your object to the wrong type before the compiler will let you do this.
```go ```go
a := ns.NSArrayWithObjects(o1,o2) // NSArray embeds Id a := ns.NSArrayWithObjects(o1,o2) // NSArray embeds Id
fmt.Println(a.NSString().UTF8String()) // DON'T! fmt.Println(a.NSString().UTF8String()) // DON'T!
// | | \-method of NSString, returns *Char, a "Stringer" type // | | \-method of NSString, returns *Char, a "Stringer"
// | \-method of Id returning NSString // | \-method of Id returning NSString
// \-calls "String()" on its parameters // \-calls "String()" on its parameters
``` ```
@ -301,22 +298,28 @@ As seen above with the `NSMutableArrayWithObjects()` constructor example,
NSWrap supports variadic NSWrap supports variadic
functions. Because of the limitations of `cgo`, there is a numerical limit functions. Because of the limitations of `cgo`, there is a numerical limit
to the number of parameters in a variadic function call, which defaults to to the number of parameters in a variadic function call, which defaults to
16 but can be set with the `vaargs` configuration directive. 16 but can be set with the `vaargs` configuration directive. NSWrap will
automatically include a `nil` sentinel before calling any Objective-C
methods with variadic parameter lists. The direct types `va_list` and
`va_list_tag` are not currently supported.
## Pointers to Pointers ## Pointers to Pointers
When NSWrap encounters a pointer to a pointer to an Objective-C object, it When NSWrap encounters a pointer to a pointer to an Objective-C object, it
treats it as an array of objects and translates it into a pointer to a treats it as an array of objects and translates it into a pointer to a
Go slice. If you are passing empty slices into these functions, be sure to Go slice. If you are passing empty slices into these functions, be sure to
pre-allocate them to a sufficient size and capacity (see below for an pre-allocate them to a sufficient capacity. Ssee below for an
example). These Go slices can be used for input and output of methods and example. These Go slices can be used for input and output of methods and
functions. functions.
Pointers to pointers are sometimes passed to Objective-C methods or functions Pointers to pointers are sometimes passed to Objective-C methods or functions
as a way of receiving output from those functions. In those cases, after the as a way of receiving output from those functions, especially because
CGo call, the method parameter is treated as a nil-terminated array of object Objective-C does not allow for multiple return values. In those cases, after
pointers. The object pointers are copied into the input Go slice, which is the CGo call, the method parameter will be treated as a nil-terminated array of
then truncated to the appropriate length. object pointers that may have been modified by the Objective-C function or
method. NSWrap will copy the object pointers back into the input Go slice, up
to its capacity (which will never be changed). The input Go slice is then
truncated to the appropriate length.
An example in Core Foundation is the `getObjects:andKeys:count` method for An example in Core Foundation is the `getObjects:andKeys:count` method for
`NSDictionary`: `NSDictionary`:
@ -328,16 +331,21 @@ An example in Core Foundation is the `getObjects:andKeys:count` method for
ns.NSArrayWithObjects(nst("key1"),nst("key2")), ns.NSArrayWithObjects(nst("key1"),nst("key2")),
) )
os,ks := make([]ns.Id,0,5), make([]ns.Id,0,5) // length 0, capacity 5 slices os,ks := make([]ns.Id,0,5), make([]ns.Id,0,5) // length 0, capacity 5 slices
dict.GetObjects(&os,&ks,5) // count = 5, must be the same size or smaller than the input slice capacity dict.GetObjects(&os,&ks,5)
// last parameter is the count, must be less than or equal to the input slice capacity
fmt.Printf("Length of os is now %d\n",len(os)) // os and ks slices are now length = 2 fmt.Printf("Length of os is now %d\n",len(os)) // os and ks slices are now length = 2
for i,k := range ks { for i,k := range ks {
fmt.Printf("-- %s -> %s\n",k.NSString(),os[i].NSString()) fmt.Printf("-- %s -> %s\n",k.NSString(),os[i].NSString())
} }
``` ```
NSWrap will never check the "count" parameter, so the user will always need
to make sure it is less than or equal to the capacity of the relevant input
Go slices.
Using pointers to pointers is necessary in many Core Foundation situations Using pointers to pointers is necessary in many Core Foundation situations
where you need to get an error message out of a function or method, for example where you need to get an error message out of a function or method.
in `[NSString stringWithContentsOfURL...]`: Here is an example using `[NSString stringWithContentsOfURL...]`:
```go ```go
err := make([]ns.NSError,1) err := make([]ns.NSError,1)
@ -422,14 +430,21 @@ const _CLOCK_MONOTONIC_RAW = C._CLOCK_MONOTONIC_RAW
## Memory management ## Memory management
You can call `Retain()`, `Release()` and `Autorelease()` on any object. Objective-C objects are always allocated and returned from CGo code, and
therefore these pointers are not garbage collected by Go. You can use any
of the standard Objective-C memory management techniques for those pointers,
which seem to work but have not been extensively tested.
All allocation functions generated by NSWrap call `autorelease` before they Since everything inherits methods from `NSObject`, you can call `Retain()`,
return an object. If you are not working in an environment (such as an `Release()` and `Autorelease()` on any object.
All allocation functions created by NSWrap (i.e. those ending in `Alloc`)
call `autorelease` before they return an object.
If you are not working in an environment (such as an
Application Delegate callback) that provides an autorelease pool, you can Application Delegate callback) that provides an autorelease pool, you can
create your own: create your own:
* Work directly with NSAutoreleasePool objects * Work directly with `NSAutoreleasePool` objects
```go ```go
swamp := ns.NSAutoreleasePoolAlloc().Init() swamp := ns.NSAutoreleasePoolAlloc().Init()
@ -440,7 +455,7 @@ str := ns.NSStringWithGoString("these objects will be automatically deallocated
swamp.Drain() swamp.Drain()
``` ```
* ...or use the AutoreleasePool() helper function * ...or use the `AutoreleasePool()` helper function
NSWrap provides a helper function that can be passed a `func()` with no NSWrap provides a helper function that can be passed a `func()` with no
parameters or return value. It is conventient to give it an anonymous function parameters or return value. It is conventient to give it an anonymous function
@ -492,25 +507,27 @@ delegates:
... ...
``` ```
The generated delegate inherits from NSObject and is identified as implementing The generated delegate inherits from `NSObject` and, in its interface
the protocols specified in `nswrap.yaml`. declaration, is advertised as implementing the protocols specified in
`nswrap.yaml`.
When a delegate is activated and one of the callback methods named in the When a delegate is activated and one of the callback methods named in the
configuration file is called, the delegate will call back into an exported Go configuration file is called, the delegate will call back into a Go
function. If a user-defined callback function has been specified, function exported by NSWrap. If a user-defined callback function has been
specified,
it will be called with all of its parameters converted to their Go type it will be called with all of its parameters converted to their Go type
equivalents. User-defined callbacks are registered by calling a function equivalents. User-defined callbacks are registered by calling a function
with the method name in TitleCase + `Callback`, so in the example above, with the method name in TitleCase + `Callback`, so in the example above,
call `ns.CentralManagerDidUpdateStateCallback(...)` with the name of your you would call `ns.CentralManagerDidUpdateStateCallback(...)` with the name of
callback function to register to receive notifications when your central your callback function to register to receive notifications when your central
manager updates its state. manager updates its state.
The code in `examples/bluetooth` implements a working Bluetooth Low Energy The example in `examples/bluetooth` implements a working Bluetooth Low-Energy
heart rate monitor entirely in Go. heart rate monitor entirely in Go.
The following Go code creates a CBDelegate object in Go, The following Go code instantiates a `CBDelegate` object,
registers a callback for `centralManagerDidUpdateState`, allocates registers a callback for `centralManagerDidUpdateState`, allocates
a CBCentralManager object, and installs our delegate: a `CBCentralManager` object, and installs our delegate:
```go ```go
func cb(c ns.CBCentralManager) { func cb(c ns.CBCentralManager) {
@ -541,15 +558,15 @@ type for the callback is `func(ns.NSNotification)` with no return value.
## Working with AppKit ## Working with AppKit
You can wrap the AppKit framework classes and create an NSApplication You can wrap the AppKit framework classes and create an `NSApplication`
Delegate. This allows you to build a Cocoa app entirely in Go. Delegate. This allows you to build a Cocoa application entirely in Go.
Because AppKit uses thread local storage, you will need to make sure all Because AppKit uses thread local storage, you will need to make sure all
calls into it are done from the main OS thread. This can be a challenge in calls into it are done from the main OS thread. This can be a challenge in
Go even though runtime.LockOSThread() is supposed to provide Go even though runtime.LockOSThread() is supposed to provide
this functionality. Good luck with that! this functionality.
This is actually a full working example: This is actually a full working Cocoa application:
```yaml ```yaml
# nswrap.yaml # nswrap.yaml
@ -585,7 +602,7 @@ package main
import ( import (
"fmt" "fmt"
"runtime" "runtime"
"git.wow.st/gmp/nswrap/examples/app/ns" // point to your NSWrap output directory "git.wow.st/gmp/nswrap/examples/app/ns" // point to your own NSWrap output directory
) )
func didFinishLaunching(n ns.NSNotification) { func didFinishLaunching(n ns.NSNotification) {
@ -619,7 +636,7 @@ func main() {
Pretty simple right? Not really, NSWrap just generated almost 15,000 lines of Pretty simple right? Not really, NSWrap just generated almost 15,000 lines of
code. See `examples/app` for a slightly more complex example with working code. See `examples/app` for a slightly more complex example with working
menus and visual format-based auto layout. menus, visual format-based auto layout, and a custom button class.
## Subclasses ## Subclasses
@ -637,7 +654,7 @@ subclasses:
yourClass: # the superclass to inherit from yourClass: # the superclass to inherit from
- init.* # what methods to override - init.* # what methods to override
- -(void)hi_there:(int)x # Objective-C prototype of your new method(s) - -(void)hi_there:(int)x # Objective-C prototype of your new method(s)
# |--note the hyphen indicating that this is an instance method # |--this hyphen indicates that this is an instance method
``` ```
In the example above, your new class will be named `myClass` in Objective-C In the example above, your new class will be named `myClass` in Objective-C
@ -652,6 +669,17 @@ name, its return type, and the names and types of its parameters if any.
Since multiple inheritance is not permitted in Objective-C, it is not possible Since multiple inheritance is not permitted in Objective-C, it is not possible
to specify more than one superclass in a `subclasses` entry. to specify more than one superclass in a `subclasses` entry.
Go callbacks for subclasses are passed a special struct named "super" as their
first parameter. This struct is filled with superclass methods, which
allows you to do things like this:
```go
func methodCallback(super ns.MyClassSupermethods, param NSString) {
...
super.Method(param)
}
```
You can use subclasses to define new AppKit controls with configurable You can use subclasses to define new AppKit controls with configurable
callbacks. For example, lets make an `NSButton` that calls back into Go when callbacks. For example, lets make an `NSButton` that calls back into Go when
you press it: you press it:
@ -683,7 +711,7 @@ func didFinishLaunching(n ns.NSNotification) {
} }
``` ```
Later on you can add the your new button to a view and tell Cocoa where to lay Later on you can add your new button to a view and tell Cocoa where to lay
it out. It's all a little verbose, but that's because for some reason you it out. It's all a little verbose, but that's because for some reason you
decided to write Objective-C code in Go. decided to write Objective-C code in Go.

View File

@ -3,7 +3,7 @@ package ast
import ( import (
"strings" "strings"
"github.com/elliotchance/c2go/util" "git.wow.st/gmp/nswrap/util"
) )
// AllocSizeAttr is a type of attribute that is optionally attached to a variable // AllocSizeAttr is a type of attribute that is optionally attached to a variable

View File

@ -4,7 +4,7 @@ import (
"reflect" "reflect"
"testing" "testing"
"github.com/elliotchance/c2go/util" "git.wow.st/gmp/nswrap/util"
) )
func TestArrayFiller(t *testing.T) { func TestArrayFiller(t *testing.T) {

View File

@ -5,7 +5,7 @@ import (
"strconv" "strconv"
"strings" "strings"
"github.com/elliotchance/c2go/util" "git.wow.st/gmp/nswrap/util"
) )
var TrackPositions bool = false var TrackPositions bool = false

View File

@ -6,7 +6,7 @@ import (
"strings" "strings"
"testing" "testing"
"github.com/elliotchance/c2go/util" "git.wow.st/gmp/nswrap/util"
) )
func formatMultiLine(o interface{}) string { func formatMultiLine(o interface{}) string {

View File

@ -1,7 +1,7 @@
package ast package ast
import ( import (
"github.com/elliotchance/c2go/util" "git.wow.st/gmp/nswrap/util"
) )
// CharacterLiteral is type of character literal // CharacterLiteral is type of character literal

View File

@ -1,7 +1,7 @@
package ast package ast
import ( import (
"github.com/elliotchance/c2go/util" "git.wow.st/gmp/nswrap/util"
) )
// ConstantArrayType is constant array type // ConstantArrayType is constant array type

View File

@ -1,11 +1,11 @@
package ast package ast
import ( import (
"errors" //"errors"
"fmt" //"fmt"
"reflect" //"reflect"
"github.com/elliotchance/c2go/cc" //"github.com/elliotchance/c2go/cc"
) )
// FloatingLiteral is type of float literal // FloatingLiteral is type of float literal
@ -63,6 +63,7 @@ type FloatingLiteralError struct {
Err error Err error
} }
/* NOT USED
// RepairFloatingLiteralsFromSource finds the exact values of floating literals // RepairFloatingLiteralsFromSource finds the exact values of floating literals
// by reading their values directly from the preprocessed source. // by reading their values directly from the preprocessed source.
// //
@ -111,4 +112,4 @@ func RepairFloatingLiteralsFromSource(rootNode Node, preprocessedFile string) []
} }
return errs return errs
} }*/

View File

@ -1,7 +1,7 @@
package ast package ast
import ( import (
"github.com/elliotchance/c2go/util" "git.wow.st/gmp/nswrap/util"
) )
// FormatAttr is a type of attribute that is optionally attached to a variable // FormatAttr is a type of attribute that is optionally attached to a variable

View File

@ -1,6 +1,6 @@
package ast package ast
import "github.com/elliotchance/c2go/util" import "git.wow.st/gmp/nswrap/util"
// MaxFieldAlignmentAttr is a type of attribute that is optionally attached to a variable // MaxFieldAlignmentAttr is a type of attribute that is optionally attached to a variable
// or struct field definition. // or struct field definition.

View File

@ -3,7 +3,7 @@ package ast
import ( import (
"strings" "strings"
"github.com/elliotchance/c2go/util" "git.wow.st/gmp/nswrap/util"
) )
// NonNullAttr is a type of attribute that is optionally attached to a variable // NonNullAttr is a type of attribute that is optionally attached to a variable

View File

@ -4,7 +4,7 @@ import (
"fmt" "fmt"
"path/filepath" "path/filepath"
"github.com/elliotchance/c2go/util" "git.wow.st/gmp/nswrap/util"
) )
// Position is type of position in source code // Position is type of position in source code

View File

@ -3,7 +3,7 @@ package ast
import ( import (
"strings" "strings"
"github.com/elliotchance/c2go/util" "git.wow.st/gmp/nswrap/util"
) )
// SentinelAttr is a type of attribute that is optionally attached to a variable // SentinelAttr is a type of attribute that is optionally attached to a variable

View File

@ -1,7 +1,7 @@
package ast package ast
import ( import (
"github.com/elliotchance/c2go/util" "git.wow.st/gmp/nswrap/util"
) )
// VectorType is vector type // VectorType is vector type

View File

@ -96,6 +96,8 @@ func didFinishLaunching(n ns.NSNotification) {
cv.AddConstraints(ns.NSLayoutConstraintsWithVisualFormat( cv.AddConstraints(ns.NSLayoutConstraintsWithVisualFormat(
nst("H:[b1]-[b2]"),ns.NSLayoutFormatAlignAllBaseline, nst("H:[b1]-[b2]"),ns.NSLayoutFormatAlignAllBaseline,
ns.NSDictionary{}, viewmap)) ns.NSDictionary{}, viewmap))
a.ActivateIgnoringOtherApps(1)
} }
func shouldTerminateAfterLastWindowClosed(s ns.NSApplication) ns.BOOL { func shouldTerminateAfterLastWindowClosed(s ns.NSApplication) ns.BOOL {

View File

@ -256,7 +256,7 @@ func main() {
confbytes, err := ioutil.ReadFile("nswrap.yaml") confbytes, err := ioutil.ReadFile("nswrap.yaml")
if err != nil { if err != nil {
fmt.Printf("Cannot open config file nswrap.yaml. %s\n",err) fmt.Printf("%s\n\nFATAL ERROR: Configuration file must be present in directory where nswrap\nis invoked.\n",err)
os.Exit(-1) os.Exit(-1)
} }
if err = yaml.Unmarshal(confbytes,&Config); err != nil { if err = yaml.Unmarshal(confbytes,&Config); err != nil {

19
util/errors.go Normal file
View File

@ -0,0 +1,19 @@
package util
import "fmt"
// PanicIfNil will panic with the message provided if the check is nil. This is
// a convieniance method to avoid many similar if statements.
func PanicIfNil(check interface{}, message string) {
if check == nil {
panic(message)
}
}
// PanicOnError will panic with the message and error if the error is not nil.
// If the error is nil (no error) then nothing happens.
func PanicOnError(err error, message string) {
if err != nil {
panic(fmt.Sprintf("%s: %s", message, err.Error()))
}
}

57
util/regexp.go Normal file
View File

@ -0,0 +1,57 @@
package util
import (
"regexp"
"strings"
"sync"
)
// GroupsFromRegex gets RegExp groups after matching it on a line
func GroupsFromRegex(rx, line string) map[string]string {
// We remove tabs and newlines from the regex. This is purely cosmetic,
// as the regex input can be quite long and it's nice for the caller to
// be able to format it in a more readable way.
rx = strings.Replace(rx, "\r", "", -1)
rx = strings.Replace(rx, "\n", "", -1)
rx = strings.Replace(rx, "\t", "", -1)
re := GetRegex(rx)
match := re.FindStringSubmatch(line)
if len(match) == 0 {
return nil
}
result := make(map[string]string)
for i, name := range re.SubexpNames() {
if i != 0 {
result[name] = match[i]
}
}
return result
}
// cachedRegex - structure for saving regexp`s
type cachedRegex struct {
sync.RWMutex
m map[string]*regexp.Regexp
}
// Global variable
var cr = cachedRegex{m: map[string]*regexp.Regexp{}}
// GetRegex return regexp
// added for minimaze regexp compilation
func GetRegex(rx string) *regexp.Regexp {
cr.RLock()
v, ok := cr.m[rx]
cr.RUnlock()
if ok {
return v
}
// if regexp is not in map
cr.Lock()
cr.m[rx] = regexp.MustCompile(rx)
cr.Unlock()
return GetRegex(rx)
}

40
util/test.go Normal file
View File

@ -0,0 +1,40 @@
package util
import (
"fmt"
"math"
"strconv"
"strings"
)
// ShowDiff will print two strings vertically next to each other so that line
// differences are easier to read.
func ShowDiff(a, b string) string {
aLines := strings.Split(a, "\n")
bLines := strings.Split(b, "\n")
maxLines := int(math.Max(float64(len(aLines)), float64(len(bLines))))
out := "\n"
for lineNumber := 0; lineNumber < maxLines; lineNumber++ {
aLine := ""
bLine := ""
// Replace NULL characters with a dot. Otherwise the strings will look
// exactly the same but have different length (and therfore not be
// equal).
if lineNumber < len(aLines) {
aLine = strconv.Quote(aLines[lineNumber])
}
if lineNumber < len(bLines) {
bLine = strconv.Quote(bLines[lineNumber])
}
diffFlag := " "
if aLine != bLine {
diffFlag = "*"
}
out += fmt.Sprintf("%s %3d %-40s%-40s\n", diffFlag, lineNumber+1, aLine, bLine)
}
return out
}

62
util/util.go Normal file
View File

@ -0,0 +1,62 @@
package util
import (
"strconv"
"strings"
)
// InStrings returns true if item exists in items. It must be an exact string
// match.
func InStrings(item string, items []string) bool {
for _, v := range items {
if item == v {
return true
}
}
return false
}
// Ucfirst returns the word with the first letter uppercased; none of the other
// letters in the word are modified. For example "fooBar" would return "FooBar".
func Ucfirst(word string) string {
if word == "" {
return ""
}
if len(word) == 1 {
return strings.ToUpper(word)
}
return strings.ToUpper(string(word[0])) + word[1:]
}
// Atoi converts a string to an integer in cases where we are sure that s will
// be a valid integer, otherwise it will panic.
func Atoi(s string) int {
i, err := strconv.Atoi(s)
PanicOnError(err, "bad integer")
return i
}
// GetExportedName returns a deterministic and Go safe name for a C type. For
// example, "*__foo[]" will return "FooSlice".
func GetExportedName(field string) string {
if strings.Contains(field, "interface{}") ||
strings.Contains(field, "Interface{}") {
return "Interface"
}
// Convert "[]byte" into "byteSlice". This also works with multiple slices,
// like "[][]byte" to "byteSliceSlice".
for len(field) > 2 && field[:2] == "[]" {
field = field[2:] + "Slice"
}
// NotFunc(int)()
field = strings.Replace(field, "(", "_", -1)
field = strings.Replace(field, ")", "_", -1)
return Ucfirst(strings.TrimLeft(field, "*_"))
}