swift-playground 0.0.1
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +7 -0
- data/.gitignore +14 -0
- data/.ruby-version +1 -0
- data/Gemfile +19 -0
- data/LICENSE +22 -0
- data/README.md +209 -0
- data/Rakefile +16 -0
- data/bin/swift-playground +5 -0
- data/lib/swift/playground.rb +193 -0
- data/lib/swift/playground/asset.rb +52 -0
- data/lib/swift/playground/assets/javascript.rb +9 -0
- data/lib/swift/playground/assets/stylesheet.rb +36 -0
- data/lib/swift/playground/cli.rb +29 -0
- data/lib/swift/playground/cli/commands/generate.rb +95 -0
- data/lib/swift/playground/cli/commands/new.rb +49 -0
- data/lib/swift/playground/cli/definition.rb +32 -0
- data/lib/swift/playground/cli/global/error_handling.rb +34 -0
- data/lib/swift/playground/cli/shared_attributes.rb +19 -0
- data/lib/swift/playground/cli/ui.rb +120 -0
- data/lib/swift/playground/debug.rb +4 -0
- data/lib/swift/playground/generator.rb +32 -0
- data/lib/swift/playground/metadata.rb +12 -0
- data/lib/swift/playground/section.rb +117 -0
- data/lib/swift/playground/sections/code_section.rb +19 -0
- data/lib/swift/playground/sections/documentation_section.rb +63 -0
- data/lib/swift/playground/template/Documentation/defaults.css.scss +96 -0
- data/lib/swift/playground/template/Documentation/section.html.erb +23 -0
- data/lib/swift/playground/template/contents.xcplayground.erb +8 -0
- data/lib/swift/playground/util.rb +3 -0
- data/lib/swift/playground/util/path_or_content.rb +31 -0
- data/lib/swift/playground/util/pipeline.rb +53 -0
- data/lib/swift/playground/util/pipeline/section_filter.rb +55 -0
- data/lib/swift/playground/util/pipeline/unicode_emoji_filter.rb +27 -0
- data/lib/swift/playground/util/syntax_highlighting.rb +24 -0
- data/swift-playground.gemspec +29 -0
- metadata +207 -0
@@ -0,0 +1,52 @@
|
|
1
|
+
module Swift
|
2
|
+
class Playground
|
3
|
+
assets_path = Pathname.new('swift/playground/assets')
|
4
|
+
autoload :Stylesheet, assets_path.join('stylesheet')
|
5
|
+
autoload :Javascript, assets_path.join('javascript')
|
6
|
+
|
7
|
+
class Asset
|
8
|
+
include Util::PathOrContent
|
9
|
+
|
10
|
+
class << self
|
11
|
+
protected
|
12
|
+
|
13
|
+
def default_filename(filename = nil)
|
14
|
+
@default_filename = filename unless filename.nil?
|
15
|
+
@default_filename
|
16
|
+
end
|
17
|
+
end
|
18
|
+
|
19
|
+
attr_accessor :content
|
20
|
+
|
21
|
+
def initialize(content, options = {})
|
22
|
+
pathname_or_content = path_or_content_as_io(content)
|
23
|
+
self.content = pathname_or_content.read
|
24
|
+
|
25
|
+
filename = options[:filename] || derived_filename(pathname_or_content)
|
26
|
+
@filename = filename || default_filename
|
27
|
+
end
|
28
|
+
|
29
|
+
def filename(number)
|
30
|
+
@filename % number
|
31
|
+
end
|
32
|
+
|
33
|
+
def save(destination_path, number)
|
34
|
+
destination_path = Pathname.new(destination_path)
|
35
|
+
|
36
|
+
expanded_filename = filename(number)
|
37
|
+
path = destination_path.join(expanded_filename)
|
38
|
+
|
39
|
+
FileUtils.mkdir_p path.dirname
|
40
|
+
path.open('w') do |file|
|
41
|
+
file.write content
|
42
|
+
end
|
43
|
+
end
|
44
|
+
|
45
|
+
protected
|
46
|
+
|
47
|
+
def default_filename
|
48
|
+
self.class.send(:default_filename)
|
49
|
+
end
|
50
|
+
end
|
51
|
+
end
|
52
|
+
end
|
@@ -0,0 +1,36 @@
|
|
1
|
+
require 'sass'
|
2
|
+
|
3
|
+
module Swift
|
4
|
+
class Playground
|
5
|
+
class Stylesheet < Asset
|
6
|
+
default_filename 'stylesheet-%d.css'
|
7
|
+
|
8
|
+
def save(destination_path, number)
|
9
|
+
save_content
|
10
|
+
|
11
|
+
self.content = Sass.compile(content)
|
12
|
+
super(destination_path, number)
|
13
|
+
|
14
|
+
restore_content
|
15
|
+
end
|
16
|
+
|
17
|
+
protected
|
18
|
+
|
19
|
+
def derived_filename(pathname_or_content)
|
20
|
+
filename = super(pathname_or_content)
|
21
|
+
filename.gsub(/\.scss$/, '')
|
22
|
+
end
|
23
|
+
|
24
|
+
private
|
25
|
+
|
26
|
+
def save_content
|
27
|
+
@saved_content = content
|
28
|
+
end
|
29
|
+
|
30
|
+
def restore_content
|
31
|
+
self.content = @saved_content
|
32
|
+
@saved_content = nil
|
33
|
+
end
|
34
|
+
end
|
35
|
+
end
|
36
|
+
end
|
@@ -0,0 +1,29 @@
|
|
1
|
+
require 'gli'
|
2
|
+
require_relative 'cli/definition'
|
3
|
+
require_relative 'cli/shared_attributes'
|
4
|
+
require_relative 'cli/ui'
|
5
|
+
require_relative 'cli/commands/new'
|
6
|
+
require_relative 'cli/commands/generate'
|
7
|
+
require_relative 'cli/global/error_handling'
|
8
|
+
|
9
|
+
require_relative 'generator'
|
10
|
+
|
11
|
+
module Swift
|
12
|
+
class Playground
|
13
|
+
module CLI
|
14
|
+
extend GLI::App
|
15
|
+
|
16
|
+
program_desc SUMMARY
|
17
|
+
version VERSION
|
18
|
+
|
19
|
+
subcommand_option_handling :normal
|
20
|
+
arguments :strict
|
21
|
+
sort_help :manually
|
22
|
+
|
23
|
+
include Commands::Generate
|
24
|
+
include Commands::New
|
25
|
+
|
26
|
+
include Global::ErrorHandling
|
27
|
+
end
|
28
|
+
end
|
29
|
+
end
|
@@ -0,0 +1,95 @@
|
|
1
|
+
module Swift::Playground::CLI
|
2
|
+
module Commands
|
3
|
+
module Generate
|
4
|
+
extend Definition
|
5
|
+
|
6
|
+
definition do
|
7
|
+
desc 'Generate a playground file from the provided Markdown file'
|
8
|
+
arg '<markdown_file>'
|
9
|
+
arg '<playground_file>', :optional
|
10
|
+
command :generate do |c|
|
11
|
+
c.extend SharedCreationSwitches
|
12
|
+
|
13
|
+
c.flag :stylesheet,
|
14
|
+
arg_name: '<file>',
|
15
|
+
type: String,
|
16
|
+
desc: 'CSS stylesheet for the HTML documentation sections of the playground. SASS/SCSS syntax is supported. This will be included after the default stylesheet.'
|
17
|
+
|
18
|
+
c.flag :javascript,
|
19
|
+
arg_name: '<file>',
|
20
|
+
type: String,
|
21
|
+
desc: 'A javascript file for the HTML documentation sections of the playground. Each section is rendered independently of another and the script will not have access to the DOM from any other sections.'
|
22
|
+
|
23
|
+
c.switch :emoji,
|
24
|
+
default_value: true,
|
25
|
+
desc: "Convert emoji aliases (e.g. `:+1:`) into emoji characters."
|
26
|
+
|
27
|
+
c.switch :highlighting,
|
28
|
+
default_value: true,
|
29
|
+
desc: "Detect non-swift code blocks and add syntax highlighting. Only has an effect if 'github-linguist' and 'pygments.rb' gems are installed."
|
30
|
+
|
31
|
+
c.flag :'highlighting-style',
|
32
|
+
arg_name: '<style>',
|
33
|
+
type: 'String',
|
34
|
+
default_value: 'default',
|
35
|
+
desc: "The name of a pygments (http://pygments.org/) style to apply to syntax highlighted code blocks. Set to 'custom' if providing your own pygments-compatible stylesheet. Ignored if --no-highlighting is set."
|
36
|
+
|
37
|
+
# c.flag :resources,
|
38
|
+
# arg_name: '<directory>',
|
39
|
+
# type: String,
|
40
|
+
# desc: 'A directory of resources to be bundled with the playground.'
|
41
|
+
|
42
|
+
c.action do |_, options, args|
|
43
|
+
markdown_file = Pathname.new(args[0]).expand_path
|
44
|
+
if args[1]
|
45
|
+
playground_file = Pathname.new(args[1]).expand_path
|
46
|
+
else
|
47
|
+
playground_file = markdown_file.sub_ext('.playground')
|
48
|
+
end
|
49
|
+
|
50
|
+
playground = Swift::Playground::Generator.generate(markdown_file)
|
51
|
+
playground.platform = options['platform']
|
52
|
+
playground.allow_reset = options['reset']
|
53
|
+
playground.convert_emoji = options['emoji']
|
54
|
+
|
55
|
+
if options['highlighting']
|
56
|
+
playground.syntax_highlighting = options['highlighting-style']
|
57
|
+
else
|
58
|
+
playground.syntax_highlighting = false
|
59
|
+
end
|
60
|
+
|
61
|
+
if options['stylesheet']
|
62
|
+
stylesheet_path = Pathname.new(options['stylesheet']).expand_path
|
63
|
+
|
64
|
+
unless stylesheet_path.exist?
|
65
|
+
raise "Stylesheet file does not exist: '#{stylesheet_path}'."
|
66
|
+
end
|
67
|
+
|
68
|
+
stylesheet = Swift::Playground::Stylesheet.new(stylesheet_path)
|
69
|
+
playground.stylesheets << stylesheet
|
70
|
+
end
|
71
|
+
|
72
|
+
if options['javascript']
|
73
|
+
javascript_path = Pathname.new(options['javascript']).expand_path
|
74
|
+
|
75
|
+
unless javascript_path.exist?
|
76
|
+
raise "Javascript file does not exist: '#{javascript_path}'."
|
77
|
+
end
|
78
|
+
|
79
|
+
javascript = Swift::Playground::Javascript.new(javascript_path)
|
80
|
+
playground.javascripts << javascript
|
81
|
+
end
|
82
|
+
|
83
|
+
playground.save(playground_file)
|
84
|
+
|
85
|
+
UI.say "Created playground at #{playground_file}"
|
86
|
+
|
87
|
+
if options['open']
|
88
|
+
system('open', playground_file.to_s)
|
89
|
+
end
|
90
|
+
end
|
91
|
+
end
|
92
|
+
end
|
93
|
+
end
|
94
|
+
end
|
95
|
+
end
|
@@ -0,0 +1,49 @@
|
|
1
|
+
require 'active_support/core_ext/string/strip'
|
2
|
+
|
3
|
+
module Swift::Playground::CLI
|
4
|
+
module Commands
|
5
|
+
module New
|
6
|
+
extend Definition
|
7
|
+
|
8
|
+
definition do
|
9
|
+
desc 'Create an empty playground (just as Xcode would via "File > New > Playground...")'
|
10
|
+
arg '<playground_file>'
|
11
|
+
command :new do |c|
|
12
|
+
c.extend SharedCreationSwitches
|
13
|
+
|
14
|
+
c.action do |_, options, args|
|
15
|
+
playground_file = Pathname.new(args[0]).expand_path
|
16
|
+
|
17
|
+
playground = Swift::Playground.new(platform: options[:platform])
|
18
|
+
|
19
|
+
case options[:platform]
|
20
|
+
when 'ios'
|
21
|
+
contents = <<-IOS.strip_heredoc
|
22
|
+
// Playground - noun: a place where people can play
|
23
|
+
|
24
|
+
import UIKit
|
25
|
+
|
26
|
+
var str = "Hello, playground"
|
27
|
+
IOS
|
28
|
+
when 'osx'
|
29
|
+
contents = <<-OSX.strip_heredoc
|
30
|
+
// Playground - noun: a place where people can play
|
31
|
+
|
32
|
+
import Cocoa
|
33
|
+
|
34
|
+
var str = "Hello, playground"
|
35
|
+
OSX
|
36
|
+
end
|
37
|
+
|
38
|
+
playground.sections << Swift::Playground::CodeSection.new(contents)
|
39
|
+
playground.save(playground_file)
|
40
|
+
|
41
|
+
if options['open']
|
42
|
+
system('open', playground_file.to_s)
|
43
|
+
end
|
44
|
+
end
|
45
|
+
end
|
46
|
+
end
|
47
|
+
end
|
48
|
+
end
|
49
|
+
end
|
@@ -0,0 +1,32 @@
|
|
1
|
+
require 'active_support/concern'
|
2
|
+
|
3
|
+
# This class makes it possible to provide helper methods in a module that will
|
4
|
+
# be included inside the main Swift::Playground::CLI module.
|
5
|
+
#
|
6
|
+
# It's unfortunately a little magical, but its difficult to work around this
|
7
|
+
# due to the way the GLI dsl is not designed to use anywhere except at the top
|
8
|
+
# level (or at best, inside a module) and not in a class.
|
9
|
+
module Swift::Playground::CLI
|
10
|
+
module Definition
|
11
|
+
# Include ActiveSupport::Concern methods, so this module behaves like
|
12
|
+
# ActiveSupport::Concern for any other module or class that extends it:
|
13
|
+
include ActiveSupport::Concern
|
14
|
+
|
15
|
+
def self.extended(mod)
|
16
|
+
# Use the behaviour of the ActiveSupport::Concern modules `self.extended`
|
17
|
+
# implementation:
|
18
|
+
ActiveSupport::Concern.extended(mod)
|
19
|
+
end
|
20
|
+
|
21
|
+
def definition(&block)
|
22
|
+
self.included do
|
23
|
+
# The use of `extend(self)` here makes sure that a module that extends
|
24
|
+
# the Definition module will have access to its methods from within
|
25
|
+
# the GLI command actions it defines. It will define these commands
|
26
|
+
# inside the block it passes to its call of the `definition` method.
|
27
|
+
extend(self)
|
28
|
+
self.class_eval(&block)
|
29
|
+
end
|
30
|
+
end
|
31
|
+
end
|
32
|
+
end
|
@@ -0,0 +1,34 @@
|
|
1
|
+
module Swift::Playground::CLI
|
2
|
+
module Global
|
3
|
+
module ErrorHandling
|
4
|
+
extend Definition
|
5
|
+
|
6
|
+
definition do
|
7
|
+
on_error do |exception|
|
8
|
+
case exception
|
9
|
+
when Interrupt
|
10
|
+
UI.error
|
11
|
+
UI.error("Execution interrupted.")
|
12
|
+
when SystemExit
|
13
|
+
# An intentional early exit has occurred and all relevant messages
|
14
|
+
# have already been displayed, so do nothing
|
15
|
+
else
|
16
|
+
# We only want to display details of the exception under debug if it
|
17
|
+
# is not a GLI exception (as a GLI exception relates to parsing
|
18
|
+
# errors - e.g. wrong command, that we do not need to expand upon):
|
19
|
+
debug_exception = (exception.class.to_s !~ /\AGLI/) ? exception : nil
|
20
|
+
|
21
|
+
if exception.message
|
22
|
+
UI.error("Execution failed: #{exception.message}", debug_exception)
|
23
|
+
else
|
24
|
+
UI.error("Execution failed.", debug_exception)
|
25
|
+
end
|
26
|
+
end
|
27
|
+
|
28
|
+
false # Prevent default GLI error handling
|
29
|
+
end
|
30
|
+
|
31
|
+
end
|
32
|
+
end
|
33
|
+
end
|
34
|
+
end
|
@@ -0,0 +1,19 @@
|
|
1
|
+
module Swift::Playground::CLI
|
2
|
+
module SharedCreationSwitches
|
3
|
+
def self.extended(command)
|
4
|
+
command.flag :platform,
|
5
|
+
default_value: 'ios',
|
6
|
+
arg_name: '[ios|osx]',
|
7
|
+
must_match: %w{ios osx},
|
8
|
+
desc: 'The target platform for the generated playground.'
|
9
|
+
|
10
|
+
command.switch :reset,
|
11
|
+
default_value: true,
|
12
|
+
desc: 'Allow the playground to be reset to it\'s original state via "Editor > Reset Playground" in Xcode.'
|
13
|
+
|
14
|
+
command.switch :open,
|
15
|
+
negatable: false,
|
16
|
+
desc: 'Open the playground in Xcode once it has been created.'
|
17
|
+
end
|
18
|
+
end
|
19
|
+
end
|
@@ -0,0 +1,120 @@
|
|
1
|
+
require 'highline/import'
|
2
|
+
require 'paint'
|
3
|
+
require 'forwardable'
|
4
|
+
require 'active_support/core_ext/module'
|
5
|
+
|
6
|
+
unless STDOUT.tty?
|
7
|
+
# If we aren't using a TTY, then we need to avoid Highline attempting to set
|
8
|
+
# TTY specific features (such as 'no echo mode'), as these will fail. We can
|
9
|
+
# do so by monkey patching Highline to make the methods that peform these
|
10
|
+
# functions no-ops:
|
11
|
+
class HighLine
|
12
|
+
module SystemExtensions
|
13
|
+
def raw_no_echo_mode
|
14
|
+
end
|
15
|
+
|
16
|
+
def restore_mode
|
17
|
+
end
|
18
|
+
end
|
19
|
+
end
|
20
|
+
end
|
21
|
+
|
22
|
+
Paint::SHORTCUTS[:swift_playground] = {
|
23
|
+
:red => Paint.color(:red),
|
24
|
+
:blue => Paint.color(:blue),
|
25
|
+
:cyan => Paint.color(:cyan),
|
26
|
+
:bright => Paint.color(:bright)
|
27
|
+
}
|
28
|
+
$paint = Paint::SwiftPlayground
|
29
|
+
|
30
|
+
# Convenience module for accessing Highline features
|
31
|
+
module Swift::Playground::CLI
|
32
|
+
module UI
|
33
|
+
extend SingleForwardable
|
34
|
+
|
35
|
+
mattr_accessor :show_debug, :color_mode, :silence
|
36
|
+
|
37
|
+
def_delegators :$terminal, :agree, :ask, :choose
|
38
|
+
def_delegators :$paint, *Paint::SHORTCUTS[:swift_playground].keys
|
39
|
+
|
40
|
+
class << self
|
41
|
+
def say(message = "\n")
|
42
|
+
return if silence
|
43
|
+
|
44
|
+
terminal.say message
|
45
|
+
end
|
46
|
+
|
47
|
+
def error(message = nil, exception = nil)
|
48
|
+
return if silence
|
49
|
+
|
50
|
+
stderr.puts red(message)
|
51
|
+
if exception && show_debug
|
52
|
+
exception_details = ["Handled <#{exception.class}>:",
|
53
|
+
exception.message,
|
54
|
+
*exception.backtrace]
|
55
|
+
stderr.puts red("\n" + exception_details.join("\n") + "\n")
|
56
|
+
end
|
57
|
+
end
|
58
|
+
|
59
|
+
def debug(message = nil, &block)
|
60
|
+
return if silence
|
61
|
+
|
62
|
+
if show_debug
|
63
|
+
message = formatted_log_message(message, &block)
|
64
|
+
stderr.puts blue(message)
|
65
|
+
end
|
66
|
+
end
|
67
|
+
|
68
|
+
def info(message = nil, &block)
|
69
|
+
return if silence
|
70
|
+
|
71
|
+
if show_debug
|
72
|
+
message = formatted_log_message(message, &block)
|
73
|
+
stderr.puts cyan(message)
|
74
|
+
end
|
75
|
+
end
|
76
|
+
|
77
|
+
private
|
78
|
+
|
79
|
+
def formatted_log_message(message = nil, &block)
|
80
|
+
if block
|
81
|
+
lines = block.call.split("\n")
|
82
|
+
if message
|
83
|
+
message = "#{message}: "
|
84
|
+
message += "\n " if lines.count > 1
|
85
|
+
message += lines.join("\n ")
|
86
|
+
else
|
87
|
+
message += lines.join("\n")
|
88
|
+
end
|
89
|
+
end
|
90
|
+
|
91
|
+
message
|
92
|
+
end
|
93
|
+
|
94
|
+
def colorize(stream)
|
95
|
+
case (color_mode || 'auto')
|
96
|
+
when 'auto'
|
97
|
+
if stream.tty?
|
98
|
+
Paint.mode = Paint.detect_mode
|
99
|
+
else
|
100
|
+
Paint.mode = 0
|
101
|
+
end
|
102
|
+
when 'always'
|
103
|
+
Paint.mode = Paint.detect_mode
|
104
|
+
when 'never'
|
105
|
+
Paint.mode = 0
|
106
|
+
end
|
107
|
+
end
|
108
|
+
|
109
|
+
def terminal
|
110
|
+
colorize($stdout)
|
111
|
+
$terminal
|
112
|
+
end
|
113
|
+
|
114
|
+
def stderr
|
115
|
+
colorize($stderr)
|
116
|
+
$stderr
|
117
|
+
end
|
118
|
+
end
|
119
|
+
end
|
120
|
+
end
|