mirror of
https://github.com/pulsar-edit/pulsar.git
synced 2024-12-28 00:52:29 +03:00
Merge remote-tracking branch 'origin/master' into cefode
Conflicts: native/v8_extensions/native.mm spec/app/config-spec.coffee spec/app/window-spec.coffee spec/spec-helper.coffee spec/stdlib/fs-utils-spec.coffee src/app/atom-package.coffee src/app/config.coffee src/app/window.coffee src/packages/fuzzy-finder/lib/load-paths-handler.coffee src/packages/markdown-preview/lib/markdown-preview-view.coffee src/packages/tree-view/spec/tree-view-spec.coffee src/stdlib/require.coffee
This commit is contained in:
commit
501dc9b76c
@ -7,7 +7,7 @@ Config = require 'config'
|
||||
Project = require 'project'
|
||||
|
||||
require 'window'
|
||||
requireStylesheet "jasmine.css"
|
||||
requireStylesheet "jasmine.less"
|
||||
|
||||
# Load TextMate bundles, which specs rely on (but not other packages)
|
||||
atom.loadTextMatePackages()
|
||||
|
@ -11,7 +11,7 @@ always hit `meta-p` to bring up a list of commands that are relevant to the
|
||||
currently focused UI element. If there is a key binding for a given command, it
|
||||
is also displayed. This is a great way to explore the system and get to know the
|
||||
key commands interactively. If you'd like to add or change a binding for a
|
||||
command, refer to the [keymaps](#keymaps) section to learn how.
|
||||
command, refer to the [key bindings](#customizing-key-bindings) section to learn how.
|
||||
|
||||
![Command Palette](http://f.cl.ly/items/32041o3w471F3C0F0V2O/Screen%20Shot%202013-02-13%20at%207.27.41%20PM.png)
|
||||
|
||||
|
97
docs/internals/serialization.md
Normal file
97
docs/internals/serialization.md
Normal file
@ -0,0 +1,97 @@
|
||||
## Serialization in Atom
|
||||
|
||||
When a window is refreshed or restored from a previous session, the view and its
|
||||
associated objects are *deserialized* from a JSON representation that was stored
|
||||
during the window's previous shutdown. For your own views and objects to be
|
||||
compatible with refreshing, you'll need to make them play nicely with the
|
||||
serializing and deserializing.
|
||||
|
||||
### Package Serialization Hook
|
||||
|
||||
Your package's main module can optionally include a `serialize` method, which
|
||||
will be called before your package is deactivated. You should return JSON, which
|
||||
will be handed back to you as an argument to `activate` next time it is called.
|
||||
In the following example, the package keeps an instance of `MyObject` in the
|
||||
same state across refreshes.
|
||||
|
||||
```coffee-script
|
||||
module.exports =
|
||||
activate: (state) ->
|
||||
@myObject =
|
||||
if state
|
||||
deserialize(state)
|
||||
else
|
||||
new MyObject("Hello")
|
||||
|
||||
serialize: ->
|
||||
@myObject.serialize()
|
||||
```
|
||||
|
||||
### Serialization Methods
|
||||
|
||||
```coffee-script
|
||||
class MyObject
|
||||
registerDeserializer(this)
|
||||
@deserialize: ({data}) -> new MyObject(data)
|
||||
constructor: (@data) ->
|
||||
serialize: -> { deserializer: 'MyObject', data: @data }
|
||||
```
|
||||
|
||||
#### .serialize()
|
||||
Objects that you want to serialize should implement `.serialize()`. This method
|
||||
should return a serializable object, and it must contain a key named
|
||||
`deserializer` whose value is the name of a registered deserializer that can
|
||||
convert the rest of the data to an object. It's usually just the name of the
|
||||
class itself.
|
||||
|
||||
#### @deserialize(data)
|
||||
The other side of the coin is the `deserialize` method, which is usually a
|
||||
class-level method on the same class that implements `serialize`. This method's
|
||||
job is to convert a state object returned from a previous call `serialize` back
|
||||
into a genuine object.
|
||||
|
||||
#### registerDeserializer(klass)
|
||||
You need to call the global `registerDeserializer` method with your class in
|
||||
order to make it available to the deserialization system. Now you can call the
|
||||
global `deserialize` method with state returned from `serialize`, and your
|
||||
class's `deserialize` method will be selected automatically.
|
||||
|
||||
### Versioning
|
||||
|
||||
```coffee-script
|
||||
class MyObject
|
||||
@version: 2
|
||||
@deserialize: (state) -> ...
|
||||
serialize: -> { version: MyObject.version, ... }
|
||||
```
|
||||
|
||||
Your serializable class can optionally have a class-level `@version` property
|
||||
and include a `version` key in its serialized state. When deserializing, Atom
|
||||
will only attempt to call deserialize if the two versions match, and otherwise
|
||||
return undefined. We plan on implementing a migration system in the future, but
|
||||
this at least protects you from improperly deserializing old state. If you find
|
||||
yourself in dire need of the migration system, let us know.
|
||||
|
||||
### Deferred Package Deserializers
|
||||
|
||||
If your package defers loading on startup with an `activationEvents` property in
|
||||
its `package.cson`, your deserializers won't be loaded until your package is
|
||||
activated. If you want to deserialize an object from your package on startup,
|
||||
this could be a problem.
|
||||
|
||||
The solution is to also supply a `deferredDeserializers` array in your
|
||||
`package.cson` with the names of all your deserializers. When Atom attempts to
|
||||
deserialize some state whose `deserializer` matches one of these names, it will
|
||||
load your package first so it can register any necessary deserializers before
|
||||
proceeding.
|
||||
|
||||
For example, the markdown preview package doesn't fully load until a preview is
|
||||
triggered. But if you refresh a window with a preview pane, it loads the
|
||||
markdown package early so Atom can deserialize the view correctly.
|
||||
|
||||
```coffee-script
|
||||
# markdown-preview/package.cson
|
||||
'activationEvents': 'markdown-preview:toggle': '.editor'
|
||||
'deferredDeserializers': ['MarkdownPreviewView']
|
||||
...
|
||||
```
|
@ -18,6 +18,7 @@ class AtomCefClient;
|
||||
+ (CefSettings)createCefSettings;
|
||||
+ (NSDictionary *)parseArguments:(char **)argv count:(int)argc;
|
||||
- (void)open:(NSString *)path;
|
||||
- (void)openDev:(NSString *)path;
|
||||
- (void)open:(NSString *)path pidToKillWhenWindowCloses:(NSNumber *)pid;
|
||||
- (IBAction)runSpecs:(id)sender;
|
||||
- (IBAction)runBenchmarks:(id)sender;
|
||||
|
@ -13,18 +13,14 @@ void activateOpenApp();
|
||||
BOOL isAppAlreadyOpen();
|
||||
|
||||
int AtomMain(int argc, char* argv[]) {
|
||||
{
|
||||
// See if we're being run as a secondary process.
|
||||
|
||||
CefMainArgs main_args(argc, argv);
|
||||
CefRefPtr<CefApp> app(new AtomCefApp);
|
||||
int exitCode = CefExecuteProcess(main_args, app);
|
||||
if (exitCode >= 0)
|
||||
return exitCode;
|
||||
}
|
||||
// Check if we're being run as a secondary process.
|
||||
CefMainArgs main_args(argc, argv);
|
||||
CefRefPtr<CefApp> app(new AtomCefApp);
|
||||
int exitCode = CefExecuteProcess(main_args, app);
|
||||
if (exitCode >= 0)
|
||||
return exitCode;
|
||||
|
||||
// We're the main process.
|
||||
|
||||
@autoreleasepool {
|
||||
handleBeingOpenedAgain(argc, argv);
|
||||
|
||||
@ -33,7 +29,7 @@ int AtomMain(int argc, char* argv[]) {
|
||||
|
||||
NSString *mainNibName = [infoDictionary objectForKey:@"NSMainNibFile"];
|
||||
NSNib *mainNib = [[NSNib alloc] initWithNibNamed:mainNibName bundle:[NSBundle bundleWithIdentifier:@"com.github.atom.framework"]];
|
||||
[mainNib instantiateNibWithOwner:application topLevelObjects:nil];
|
||||
[mainNib instantiateWithOwner:application topLevelObjects:nil];
|
||||
|
||||
CefRunMessageLoop();
|
||||
}
|
||||
|
@ -16,6 +16,8 @@
|
||||
"plist": "git://github.com/nathansobo/node-plist.git"
|
||||
},
|
||||
|
||||
"private": true,
|
||||
|
||||
"scripts": {
|
||||
"preinstall": "true"
|
||||
}
|
||||
|
@ -3,18 +3,31 @@ AtomPackage = require 'atom-package'
|
||||
fs = require 'fs-utils'
|
||||
|
||||
describe "AtomPackage", ->
|
||||
[packageMainModule, pack] = []
|
||||
|
||||
beforeEach ->
|
||||
pack = new AtomPackage(fs.resolve(config.packageDirPaths..., 'package-with-activation-events'))
|
||||
pack.load()
|
||||
|
||||
describe ".load()", ->
|
||||
describe "if the package's metadata has a `deferredDeserializers` array", ->
|
||||
it "requires the package's main module attempting to use deserializers named in the array", ->
|
||||
expect(pack.mainModule).toBeNull()
|
||||
object = deserialize(deserializer: 'Foo', data: "Hello")
|
||||
expect(object.constructor.name).toBe 'Foo'
|
||||
expect(object.data).toBe 'Hello'
|
||||
expect(pack.mainModule).toBeDefined()
|
||||
expect(pack.mainModule.activateCallCount).toBe 0
|
||||
|
||||
describe ".activate()", ->
|
||||
beforeEach ->
|
||||
window.rootView = new RootView
|
||||
packageMainModule = require 'fixtures/packages/package-with-activation-events/main'
|
||||
spyOn(packageMainModule, 'activate').andCallThrough()
|
||||
|
||||
describe "when the package metadata includes activation events", ->
|
||||
[packageMainModule, pack] = []
|
||||
|
||||
beforeEach ->
|
||||
pack = new AtomPackage(fs.resolve(config.packageDirPaths..., 'package-with-activation-events'))
|
||||
packageMainModule = require 'fixtures/packages/package-with-activation-events/main'
|
||||
spyOn(packageMainModule, 'activate').andCallThrough()
|
||||
pack.load()
|
||||
pack.activate()
|
||||
|
||||
it "defers activating the package until an activation event bubbles to the root view", ->
|
||||
expect(packageMainModule.activate).not.toHaveBeenCalled()
|
||||
@ -44,12 +57,13 @@ describe "AtomPackage", ->
|
||||
|
||||
expect(packageMainModule.activate).not.toHaveBeenCalled()
|
||||
pack.load()
|
||||
pack.activate()
|
||||
expect(packageMainModule.activate).toHaveBeenCalled()
|
||||
|
||||
describe "when the package doesn't have an index.coffee", ->
|
||||
it "does not throw an exception or log an error", ->
|
||||
spyOn(console, "error")
|
||||
spyOn(console, "warn")
|
||||
spyOn(console, "warn").andCallThrough()
|
||||
pack = new AtomPackage(fs.resolve(config.packageDirPaths..., 'package-with-keymaps-manifest'))
|
||||
|
||||
expect(-> pack.load()).not.toThrow()
|
||||
|
@ -85,26 +85,26 @@ describe "the `atom` global", ->
|
||||
describe "activation", ->
|
||||
it "calls activate on the package main with its previous state", ->
|
||||
pack = window.loadPackage('package-with-module')
|
||||
spyOn(pack.packageMain, 'activate')
|
||||
spyOn(pack.mainModule, 'activate')
|
||||
|
||||
serializedState = rootView.serialize()
|
||||
rootView.deactivate()
|
||||
RootView.deserialize(serializedState)
|
||||
window.loadPackage('package-with-module')
|
||||
|
||||
expect(pack.packageMain.activate).toHaveBeenCalledWith(someNumber: 1)
|
||||
expect(pack.mainModule.activate).toHaveBeenCalledWith(someNumber: 1)
|
||||
|
||||
describe "deactivation", ->
|
||||
it "deactivates and removes the package module from the package module map", ->
|
||||
pack = window.loadPackage('package-with-module')
|
||||
expect(atom.activatedAtomPackages.length).toBe 1
|
||||
spyOn(pack.packageMain, "deactivate").andCallThrough()
|
||||
spyOn(pack.mainModule, "deactivate").andCallThrough()
|
||||
atom.deactivateAtomPackages()
|
||||
expect(pack.packageMain.deactivate).toHaveBeenCalled()
|
||||
expect(pack.mainModule.deactivate).toHaveBeenCalled()
|
||||
expect(atom.activatedAtomPackages.length).toBe 0
|
||||
|
||||
describe "serialization", ->
|
||||
it "uses previous serialization state on unactivated packages", ->
|
||||
it "uses previous serialization state on packages whose activation has been deferred", ->
|
||||
atom.atomPackageStates['package-with-activation-events'] = {previousData: 'exists'}
|
||||
unactivatedPackage = window.loadPackage('package-with-activation-events')
|
||||
activatedPackage = window.loadPackage('package-with-module')
|
||||
@ -116,7 +116,8 @@ describe "the `atom` global", ->
|
||||
'previousData': 'exists'
|
||||
|
||||
# ensure serialization occurs when the packageis activated
|
||||
unactivatedPackage.activatePackageMain()
|
||||
unactivatedPackage.deferActivation = false
|
||||
unactivatedPackage.activate()
|
||||
expect(atom.serializeAtomPackages()).toEqual
|
||||
'package-with-module':
|
||||
'someNumber': 1
|
||||
@ -125,8 +126,8 @@ describe "the `atom` global", ->
|
||||
|
||||
it "absorbs exceptions that are thrown by the package module's serialize methods", ->
|
||||
spyOn(console, 'error')
|
||||
window.loadPackage('package-with-module')
|
||||
window.loadPackage('package-with-serialize-error', activateImmediately: true)
|
||||
window.loadPackage('package-with-module', activateImmediately: true)
|
||||
window.loadPackage('package-with-serialize-error', activateImmediately: true)
|
||||
|
||||
packageStates = atom.serializeAtomPackages()
|
||||
expect(packageStates['package-with-module']).toEqual someNumber: 1
|
||||
|
@ -133,3 +133,21 @@ describe "Config", ->
|
||||
expect(fs.isFile(fs.join(config.configDirPath, 'themes/atom-light-ui/package.cson'))).toBeTruthy()
|
||||
expect(fs.isFile(fs.join(config.configDirPath, 'themes/atom-dark-syntax.css'))).toBeTruthy()
|
||||
expect(fs.isFile(fs.join(config.configDirPath, 'themes/atom-light-syntax.css'))).toBeTruthy()
|
||||
|
||||
describe "when the config file is not parseable", ->
|
||||
beforeEach ->
|
||||
config.configDirPath = '/tmp/dot-atom-dir'
|
||||
config.configFilePath = fs.join(config.configDirPath, "config.cson")
|
||||
expect(fs.exists(config.configDirPath)).toBeFalsy()
|
||||
|
||||
afterEach ->
|
||||
fs.remove('/tmp/dot-atom-dir') if fs.exists('/tmp/dot-atom-dir')
|
||||
|
||||
it "logs an error to the console and does not overwrite the config file", ->
|
||||
config.save.reset()
|
||||
spyOn(console, 'error')
|
||||
fs.write(config.configFilePath, "{{{{{")
|
||||
config.loadUserConfig()
|
||||
config.set("hair", "blonde") # trigger a save
|
||||
expect(console.error).toHaveBeenCalled()
|
||||
expect(config.save).not.toHaveBeenCalled()
|
||||
|
@ -136,6 +136,43 @@ describe "PaneContainer", ->
|
||||
for item in pane.getItems()
|
||||
expect(item.saved).toBeTruthy()
|
||||
|
||||
describe ".confirmClose()", ->
|
||||
it "resolves the returned promise after modified files are saved", ->
|
||||
pane1.itemAtIndex(0).isModified = -> true
|
||||
pane2.itemAtIndex(0).isModified = -> true
|
||||
spyOn(atom, "confirm").andCallFake (a, b, c, d, e, f, g, noSaveFn) -> noSaveFn()
|
||||
|
||||
promiseHandler = jasmine.createSpy("promiseHandler")
|
||||
failedPromiseHandler = jasmine.createSpy("failedPromiseHandler")
|
||||
promise = container.confirmClose()
|
||||
promise.done promiseHandler
|
||||
promise.fail failedPromiseHandler
|
||||
|
||||
waitsFor ->
|
||||
promiseHandler.wasCalled
|
||||
|
||||
runs ->
|
||||
expect(failedPromiseHandler).not.toHaveBeenCalled()
|
||||
expect(atom.confirm).toHaveBeenCalled()
|
||||
|
||||
it "rejects the returned promise if the user cancels saving", ->
|
||||
pane1.itemAtIndex(0).isModified = -> true
|
||||
pane2.itemAtIndex(0).isModified = -> true
|
||||
spyOn(atom, "confirm").andCallFake (a, b, c, d, e, cancelFn, f, g) -> cancelFn()
|
||||
|
||||
promiseHandler = jasmine.createSpy("promiseHandler")
|
||||
failedPromiseHandler = jasmine.createSpy("failedPromiseHandler")
|
||||
promise = container.confirmClose()
|
||||
promise.done promiseHandler
|
||||
promise.fail failedPromiseHandler
|
||||
|
||||
waitsFor ->
|
||||
failedPromiseHandler.wasCalled
|
||||
|
||||
runs ->
|
||||
expect(promiseHandler).not.toHaveBeenCalled()
|
||||
expect(atom.confirm).toHaveBeenCalled()
|
||||
|
||||
describe "serialization", ->
|
||||
it "can be serialized and deserialized, and correctly adjusts dimensions of deserialized panes after attach", ->
|
||||
newContainer = deserialize(container.serialize())
|
||||
@ -146,3 +183,10 @@ describe "PaneContainer", ->
|
||||
newContainer.height(200).width(300).attachToDom()
|
||||
expect(newContainer.find('.row > :contains(1)').width()).toBe 150
|
||||
expect(newContainer.find('.row > .column > :contains(2)').height()).toBe 100
|
||||
|
||||
it "removes empty panes on deserialization", ->
|
||||
# only deserialize pane 1's view successfully
|
||||
TestView.deserialize = ({name}) -> new TestView(name) if name is '1'
|
||||
newContainer = deserialize(container.serialize())
|
||||
expect(newContainer.find('.row, .column')).not.toExist()
|
||||
expect(newContainer.find('> :contains(1)')).toExist()
|
||||
|
@ -450,6 +450,14 @@ describe "Pane", ->
|
||||
pane.remove()
|
||||
expect(rootView.focus).not.toHaveBeenCalled()
|
||||
|
||||
describe ".getNextPane()", ->
|
||||
it "returns the next pane if one exists, wrapping around from the last pane to the first", ->
|
||||
pane.showItem(editSession1)
|
||||
expect(pane.getNextPane()).toBeUndefined
|
||||
pane2 = pane.splitRight()
|
||||
expect(pane.getNextPane()).toBe pane2
|
||||
expect(pane2.getNextPane()).toBe pane
|
||||
|
||||
describe "when the pane is focused", ->
|
||||
it "focuses the active item view", ->
|
||||
focusHandler = jasmine.createSpy("focusHandler")
|
||||
|
@ -134,8 +134,8 @@ describe "Project", ->
|
||||
project.getFilePaths().done (foundPaths) -> paths = foundPaths
|
||||
|
||||
runs ->
|
||||
expect(paths).not.toContain('a')
|
||||
expect(paths).toContain('b')
|
||||
expect(paths).not.toContain(project.resolve('a'))
|
||||
expect(paths).toContain(project.resolve('b'))
|
||||
|
||||
describe "when config.core.hideGitIgnoredFiles is true", ->
|
||||
it "ignores files that are present in .gitignore if the project is a git repo", ->
|
||||
|
@ -20,8 +20,21 @@ describe "@load(name)", ->
|
||||
expect($(".editor").css("background-color")).toBe("rgb(20, 20, 20)")
|
||||
|
||||
describe "AtomTheme", ->
|
||||
describe "when the theme is a file", ->
|
||||
it "loads and applies css", ->
|
||||
expect($(".editor").css("padding-bottom")).not.toBe "1234px"
|
||||
themePath = project.resolve('themes/theme-stylesheet.css')
|
||||
theme = Theme.load(themePath)
|
||||
expect($(".editor").css("padding-top")).toBe "1234px"
|
||||
|
||||
it "parses, loads and applies less", ->
|
||||
expect($(".editor").css("padding-bottom")).not.toBe "1234px"
|
||||
themePath = project.resolve('themes/theme-stylesheet.less')
|
||||
theme = Theme.load(themePath)
|
||||
expect($(".editor").css("padding-top")).toBe "4321px"
|
||||
|
||||
describe "when the theme contains a package.json file", ->
|
||||
it "loads and applies css from package.json in the correct order", ->
|
||||
it "loads and applies stylesheets from package.json in the correct order", ->
|
||||
expect($(".editor").css("padding-top")).not.toBe("101px")
|
||||
expect($(".editor").css("padding-right")).not.toBe("102px")
|
||||
expect($(".editor").css("padding-bottom")).not.toBe("103px")
|
||||
@ -32,16 +45,8 @@ describe "@load(name)", ->
|
||||
expect($(".editor").css("padding-right")).toBe("102px")
|
||||
expect($(".editor").css("padding-bottom")).toBe("103px")
|
||||
|
||||
describe "when the theme is a CSS file", ->
|
||||
it "loads and applies the stylesheet", ->
|
||||
expect($(".editor").css("padding-bottom")).not.toBe "1234px"
|
||||
|
||||
themePath = project.resolve('themes/theme-stylesheet.css')
|
||||
theme = Theme.load(themePath)
|
||||
expect($(".editor").css("padding-top")).toBe "1234px"
|
||||
|
||||
describe "when the theme does not contain a package.json file and is a directory", ->
|
||||
it "loads all CSS files in the directory", ->
|
||||
it "loads all stylesheet files in the directory", ->
|
||||
expect($(".editor").css("padding-top")).not.toBe "10px"
|
||||
expect($(".editor").css("padding-right")).not.toBe "20px"
|
||||
expect($(".editor").css("padding-bottom")).not.toBe "30px"
|
||||
|
@ -1,5 +1,6 @@
|
||||
$ = require 'jquery'
|
||||
fs = require 'fs-utils'
|
||||
{less} = require 'less'
|
||||
|
||||
describe "Window", ->
|
||||
projectPath = null
|
||||
@ -34,16 +35,31 @@ describe "Window", ->
|
||||
$(window).trigger 'focus'
|
||||
expect($("body")).not.toHaveClass("is-blurred")
|
||||
|
||||
describe ".close()", ->
|
||||
it "is triggered by the 'core:close' event", ->
|
||||
spyOn window, 'close'
|
||||
$(window).trigger 'core:close'
|
||||
expect(window.close).toHaveBeenCalled()
|
||||
describe "window:close event", ->
|
||||
describe "when no pane items are modified", ->
|
||||
it "calls window.close", ->
|
||||
spyOn window, 'close'
|
||||
$(window).trigger 'window:close'
|
||||
expect(window.close).toHaveBeenCalled()
|
||||
|
||||
it "is triggered by the 'window:close event'", ->
|
||||
spyOn window, 'close'
|
||||
$(window).trigger 'window:close'
|
||||
expect(window.close).toHaveBeenCalled()
|
||||
describe "when pane items are are modified", ->
|
||||
it "prompts user to save and and calls window.close", ->
|
||||
spyOn(window, 'close')
|
||||
spyOn(atom, "confirm").andCallFake (a, b, c, d, e, f, g, noSave) -> noSave()
|
||||
editSession = rootView.open("sample.js")
|
||||
editSession.insertText("I look different, I feel different.")
|
||||
$(window).trigger 'window:close'
|
||||
expect(window.close).toHaveBeenCalled()
|
||||
expect(atom.confirm).toHaveBeenCalled()
|
||||
|
||||
it "prompts user to save and aborts if dialog is canceled", ->
|
||||
spyOn(window, 'close')
|
||||
spyOn(atom, "confirm").andCallFake (a, b, c, d, e, cancel) -> cancel()
|
||||
editSession = rootView.open("sample.js")
|
||||
editSession.insertText("I look different, I feel different.")
|
||||
$(window).trigger 'window:close'
|
||||
expect(window.close).not.toHaveBeenCalled()
|
||||
expect(atom.confirm).toHaveBeenCalled()
|
||||
|
||||
describe ".reload()", ->
|
||||
beforeEach ->
|
||||
@ -62,22 +78,46 @@ describe "Window", ->
|
||||
expect(atom.confirm).toHaveBeenCalled()
|
||||
|
||||
describe "requireStylesheet(path)", ->
|
||||
it "synchronously loads the stylesheet at the given path and installs a style tag for it in the head", ->
|
||||
$('head style[id*="atom.css"]').remove()
|
||||
it "synchronously loads css at the given path and installs a style tag for it in the head", ->
|
||||
cssPath = project.resolve('css.css')
|
||||
lengthBefore = $('head style').length
|
||||
requireStylesheet('atom.css')
|
||||
|
||||
requireStylesheet(cssPath)
|
||||
expect($('head style').length).toBe lengthBefore + 1
|
||||
|
||||
styleElt = $('head style[id*="atom.css"]')
|
||||
|
||||
fullPath = require.resolve('atom.css')
|
||||
expect(styleElt.attr('id')).toBe fullPath
|
||||
expect(styleElt.text()).toBe fs.read(fullPath)
|
||||
element = $('head style[id*="css.css"]')
|
||||
expect(element.attr('id')).toBe cssPath
|
||||
expect(element.text()).toBe fs.read(cssPath)
|
||||
|
||||
# doesn't append twice
|
||||
requireStylesheet('atom.css')
|
||||
requireStylesheet(cssPath)
|
||||
expect($('head style').length).toBe lengthBefore + 1
|
||||
|
||||
$('head style[id*="css.css"]').remove()
|
||||
|
||||
it "synchronously loads and parses less files at the given path and installs a style tag for it in the head", ->
|
||||
lessPath = project.resolve('sample.less')
|
||||
lengthBefore = $('head style').length
|
||||
requireStylesheet(lessPath)
|
||||
expect($('head style').length).toBe lengthBefore + 1
|
||||
|
||||
element = $('head style[id*="sample.less"]')
|
||||
expect(element.attr('id')).toBe lessPath
|
||||
expect(element.text()).toBe """
|
||||
#header {
|
||||
color: #4d926f;
|
||||
}
|
||||
h2 {
|
||||
color: #4d926f;
|
||||
}
|
||||
|
||||
"""
|
||||
|
||||
# doesn't append twice
|
||||
requireStylesheet(lessPath)
|
||||
expect($('head style').length).toBe lengthBefore + 1
|
||||
$('head style[id*="sample.less"]').remove()
|
||||
|
||||
describe ".disableStyleSheet(path)", ->
|
||||
it "removes styling applied by given stylesheet path", ->
|
||||
cssPath = require.resolve(fs.join("fixtures", "css.css"))
|
||||
|
3
spec/fixtures/markdown/file.markdown
vendored
3
spec/fixtures/markdown/file.markdown
vendored
@ -0,0 +1,3 @@
|
||||
## File.markdown
|
||||
|
||||
:cool:
|
@ -1,7 +1,14 @@
|
||||
class Foo
|
||||
registerDeserializer(this)
|
||||
@deserialize: ({data}) -> new Foo(data)
|
||||
constructor: (@data) ->
|
||||
|
||||
module.exports =
|
||||
activateCallCount: 0
|
||||
activationEventCallCount: 0
|
||||
|
||||
activate: ->
|
||||
@activateCallCount++
|
||||
rootView.getActiveView()?.command 'activation-event', =>
|
||||
@activationEventCallCount++
|
||||
|
||||
|
@ -1,2 +1,3 @@
|
||||
'activationEvents': ['activation-event']
|
||||
'deferredDeserializers': ['Foo']
|
||||
'main': 'main'
|
||||
|
1
spec/fixtures/sample-with-error.less
vendored
Normal file
1
spec/fixtures/sample-with-error.less
vendored
Normal file
@ -0,0 +1 @@
|
||||
#header {
|
8
spec/fixtures/sample.less
vendored
Normal file
8
spec/fixtures/sample.less
vendored
Normal file
@ -0,0 +1,8 @@
|
||||
@color: #4D926F;
|
||||
|
||||
#header {
|
||||
color: @color;
|
||||
}
|
||||
h2 {
|
||||
color: @color;
|
||||
}
|
5
spec/fixtures/themes/theme-stylesheet.less
vendored
Normal file
5
spec/fixtures/themes/theme-stylesheet.less
vendored
Normal file
@ -0,0 +1,5 @@
|
||||
@padding: 4321px;
|
||||
|
||||
.editor {
|
||||
padding-top: @padding;
|
||||
}
|
@ -1,3 +1,3 @@
|
||||
{
|
||||
"stylesheets": ["first.css", "second.css", "last.css"]
|
||||
"stylesheets": ["first.css", "second.less", "last.css"]
|
||||
}
|
@ -1,5 +0,0 @@
|
||||
.editor {
|
||||
/* padding-top: 102px;*/
|
||||
padding-right: 102px;
|
||||
padding-bottom: 102px;
|
||||
}
|
7
spec/fixtures/themes/theme-with-package-file/second.less
vendored
Normal file
7
spec/fixtures/themes/theme-with-package-file/second.less
vendored
Normal file
@ -0,0 +1,7 @@
|
||||
@number: 102px;
|
||||
|
||||
.editor {
|
||||
/* padding-top: 102px;*/
|
||||
padding-right: @number;
|
||||
padding-bottom: @number;
|
||||
}
|
@ -1,3 +0,0 @@
|
||||
.editor {
|
||||
padding-bottom: 30px;
|
||||
}
|
5
spec/fixtures/themes/theme-without-package-file/c.less
vendored
Normal file
5
spec/fixtures/themes/theme-without-package-file/c.less
vendored
Normal file
@ -0,0 +1,5 @@
|
||||
@number: 30px;
|
||||
|
||||
.editor {
|
||||
padding-bottom: @number;
|
||||
}
|
@ -15,7 +15,7 @@ TokenizedBuffer = require 'tokenized-buffer'
|
||||
fs = require 'fs-utils'
|
||||
RootView = require 'root-view'
|
||||
Git = require 'git'
|
||||
requireStylesheet "jasmine.css"
|
||||
requireStylesheet "jasmine.less"
|
||||
fixturePackagesPath = fs.resolveOnLoadPath('fixtures/packages')
|
||||
keymap.loadBundledKeymaps()
|
||||
[bindingSetsToRestore, bindingSetsByFirstKeystrokeToRestore] = []
|
||||
@ -67,6 +67,8 @@ beforeEach ->
|
||||
spyOn($native, 'writeToPasteboard').andCallFake (text) -> pasteboardContent = text
|
||||
spyOn($native, 'readFromPasteboard').andCallFake -> pasteboardContent
|
||||
|
||||
addCustomMatchers(this)
|
||||
|
||||
afterEach ->
|
||||
keymap.bindingSets = bindingSetsToRestore
|
||||
keymap.bindingSetsByFirstKeystrokeToRestore = bindingSetsByFirstKeystrokeToRestore
|
||||
@ -85,12 +87,14 @@ afterEach ->
|
||||
atom.presentingModal = false
|
||||
waits(0) # yield to ui thread to make screen update more frequently
|
||||
|
||||
window.loadPackage = (name, options) ->
|
||||
window.loadPackage = (name, options={}) ->
|
||||
Package = require 'package'
|
||||
packagePath = _.find atom.getPackagePaths(), (packagePath) -> fs.base(packagePath) == name
|
||||
if pack = Package.build(packagePath)
|
||||
pack.load(options)
|
||||
atom.loadedPackages.push(pack)
|
||||
pack.deferActivation = false if options.activateImmediately
|
||||
pack.activate()
|
||||
pack
|
||||
|
||||
# Specs rely on TextMate bundles (but not atom packages)
|
||||
@ -120,6 +124,23 @@ jasmine.unspy = (object, methodName) ->
|
||||
throw new Error("Not a spy") unless object[methodName].originalValue?
|
||||
object[methodName] = object[methodName].originalValue
|
||||
|
||||
addCustomMatchers = (spec) ->
|
||||
spec.addMatchers
|
||||
toBeInstanceOf: (expected) ->
|
||||
notText = if @isNot then " not" else ""
|
||||
this.message = => "Expected #{jasmine.pp(@actual)} to#{notText} be instance of #{expected.name} class"
|
||||
@actual instanceof expected
|
||||
|
||||
toHaveLength: (expected) ->
|
||||
notText = if @isNot then " not" else ""
|
||||
this.message = => "Expected object with length #{@actual.length} to#{notText} have length #{expected}"
|
||||
@actual.length == expected
|
||||
|
||||
toExistOnDisk: (expected) ->
|
||||
notText = this.isNot and " not" or ""
|
||||
@message = -> return "Expected path '" + @actual + "'" + notText + " to exist."
|
||||
fs.exists(@actual)
|
||||
|
||||
window.keyIdentifierForKey = (key) ->
|
||||
if key.length > 1 # named key
|
||||
key
|
||||
|
@ -86,7 +86,7 @@ describe "fs", ->
|
||||
it "calls fn for every path in the tree at the given path", ->
|
||||
paths = []
|
||||
onPath = (path) ->
|
||||
paths.push(fs.join(fixturesDir, path))
|
||||
paths.push(path)
|
||||
true
|
||||
fs.traverseTreeSync fixturesDir, onPath, onPath
|
||||
expect(paths).toEqual fs.listTree(fixturesDir)
|
||||
@ -106,14 +106,16 @@ describe "fs", ->
|
||||
expect(path).not.toMatch /\/dir\//
|
||||
|
||||
it "returns entries if path is a symlink", ->
|
||||
symlinkPath = fs.join(fixturesDir, 'symlink-to-dir')
|
||||
symlinkPaths = []
|
||||
onSymlinkPath = (path) -> symlinkPaths.push(path)
|
||||
onSymlinkPath = (path) -> symlinkPaths.push(path.substring(symlinkPath.length + 1))
|
||||
|
||||
regularPath = fs.join(fixturesDir, 'dir')
|
||||
paths = []
|
||||
onPath = (path) -> paths.push(path)
|
||||
onPath = (path) -> paths.push(path.substring(regularPath.length + 1))
|
||||
|
||||
fs.traverseTreeSync(fs.join(fixturesDir, 'symlink-to-dir'), onSymlinkPath, onSymlinkPath)
|
||||
fs.traverseTreeSync(fs.join(fixturesDir, 'dir'), onPath, onPath)
|
||||
fs.traverseTreeSync(symlinkPath, onSymlinkPath, onSymlinkPath)
|
||||
fs.traverseTreeSync(regularPath, onPath, onPath)
|
||||
|
||||
expect(symlinkPaths).toEqual(paths)
|
||||
|
||||
|
@ -1,4 +1,5 @@
|
||||
$ = require 'jquery'
|
||||
_ = require 'underscore'
|
||||
{View, $$} = require 'space-pen'
|
||||
|
||||
describe 'jQuery extensions', ->
|
||||
@ -41,7 +42,7 @@ describe 'jQuery extensions', ->
|
||||
element.trigger 'foo'
|
||||
expect(events).toEqual [2,1,3]
|
||||
|
||||
describe "$.fn.events() and $.fn.document", ->
|
||||
describe "$.fn.events() and $.fn.document(...)", ->
|
||||
it "returns a list of all events being listened for on the target node or its ancestors, along with their documentation string", ->
|
||||
view = $$ ->
|
||||
@div id: 'a', =>
|
||||
@ -49,20 +50,18 @@ describe 'jQuery extensions', ->
|
||||
@div id: 'c'
|
||||
@div id: 'd'
|
||||
|
||||
view.document
|
||||
'a1': "This is event A2"
|
||||
'b2': "This is event b2"
|
||||
view.document 'a1', "This is event A2"
|
||||
view.document 'b2', "This is event b2"
|
||||
|
||||
view.document 'a1': "A1: Waste perfectly-good steak"
|
||||
view.document 'a1', "A1: Waste perfectly-good steak"
|
||||
view.on 'a1', ->
|
||||
view.on 'a2', ->
|
||||
view.on 'b1', -> # should not appear as a duplicate
|
||||
|
||||
divB = view.find('#b')
|
||||
|
||||
divB.document
|
||||
'b1': "B1: Super-sonic bomber"
|
||||
'b2': "B2: Looks evil. Kinda is."
|
||||
divB.document 'b1', "B1: Super-sonic bomber"
|
||||
divB.document 'b2', "B2: Looks evil. Kinda is."
|
||||
divB.on 'b1', ->
|
||||
divB.on 'b2', ->
|
||||
|
||||
@ -76,6 +75,80 @@ describe 'jQuery extensions', ->
|
||||
'a1': "A1: Waste perfectly-good steak"
|
||||
'a2': null
|
||||
|
||||
describe "$.fn.command(eventName, [selector, options,] handler)", ->
|
||||
[view, handler] = []
|
||||
|
||||
beforeEach ->
|
||||
view = $$ ->
|
||||
@div class: 'a', =>
|
||||
@div class: 'b'
|
||||
@div class: 'c'
|
||||
handler = jasmine.createSpy("commandHandler")
|
||||
|
||||
it "binds the handler to the given event / selector for all argument combinations", ->
|
||||
view.command 'test:foo', handler
|
||||
view.trigger 'test:foo'
|
||||
expect(handler).toHaveBeenCalled()
|
||||
handler.reset()
|
||||
|
||||
view.command 'test:bar', '.b', handler
|
||||
view.find('.b').trigger 'test:bar'
|
||||
view.find('.c').trigger 'test:bar'
|
||||
expect(handler.callCount).toBe 1
|
||||
handler.reset()
|
||||
|
||||
view.command 'test:baz', doc: 'Spaz', handler
|
||||
view.trigger 'test:baz'
|
||||
expect(handler).toHaveBeenCalled()
|
||||
handler.reset()
|
||||
|
||||
view.command 'test:quux', '.c', doc: 'Lorem', handler
|
||||
view.find('.b').trigger 'test:quux'
|
||||
view.find('.c').trigger 'test:quux'
|
||||
expect(handler.callCount).toBe 1
|
||||
|
||||
it "passes the 'data' option through when binding the event handler", ->
|
||||
view.command 'test:foo', data: "bar", handler
|
||||
view.trigger 'test:foo'
|
||||
expect(handler.argsForCall[0][0].data).toBe 'bar'
|
||||
|
||||
it "sets a custom docstring if the 'doc' option is specified", ->
|
||||
view.command 'test:foo', doc: "Foo!", handler
|
||||
expect(view.events()).toEqual 'test:foo': 'Test: Foo!'
|
||||
|
||||
it "capitalizes the 'github' prefix how we like it", ->
|
||||
view.command 'github:spelling', handler
|
||||
expect(view.events()).toEqual 'github:spelling': 'GitHub: Spelling'
|
||||
|
||||
describe "$.fn.scrollUp/Down/ToTop/ToBottom", ->
|
||||
it "scrolls the element in the specified way if possible", ->
|
||||
view = $$ -> @div => _.times 20, => @div('A')
|
||||
view.css(height: 100, width: 100, overflow: 'scroll')
|
||||
view.attachToDom()
|
||||
|
||||
view.scrollUp()
|
||||
expect(view.scrollTop()).toBe 0
|
||||
|
||||
view.scrollDown()
|
||||
expect(view.scrollTop()).toBeGreaterThan 0
|
||||
previousScrollTop = view.scrollTop()
|
||||
view.scrollDown()
|
||||
expect(view.scrollTop()).toBeGreaterThan previousScrollTop
|
||||
|
||||
view.scrollToBottom()
|
||||
expect(view.scrollTop()).toBe view.prop('scrollHeight') - 100
|
||||
previousScrollTop = view.scrollTop()
|
||||
view.scrollDown()
|
||||
expect(view.scrollTop()).toBe previousScrollTop
|
||||
view.scrollUp()
|
||||
expect(view.scrollTop()).toBeLessThan previousScrollTop
|
||||
previousScrollTop = view.scrollTop()
|
||||
view.scrollUp()
|
||||
expect(view.scrollTop()).toBeLessThan previousScrollTop
|
||||
|
||||
view.scrollToTop()
|
||||
expect(view.scrollTop()).toBe 0
|
||||
|
||||
describe "Event.prototype", ->
|
||||
class GrandchildView extends View
|
||||
@content: -> @div class: 'grandchild'
|
||||
|
@ -7,70 +7,22 @@ CSON = require 'cson'
|
||||
module.exports =
|
||||
class AtomPackage extends Package
|
||||
metadata: null
|
||||
packageMain: null
|
||||
mainModule: null
|
||||
deferActivation: false
|
||||
|
||||
load: ({activateImmediately}={}) ->
|
||||
load: ->
|
||||
try
|
||||
@loadMetadata()
|
||||
@loadKeymaps()
|
||||
@loadStylesheets()
|
||||
if @metadata.activationEvents and not activateImmediately
|
||||
@subscribeToActivationEvents()
|
||||
if @deferActivation = @metadata.activationEvents?
|
||||
@registerDeferredDeserializers()
|
||||
else
|
||||
@activatePackageMain()
|
||||
@requireMainModule()
|
||||
catch e
|
||||
console.warn "Failed to load package named '#{@name}'", e.stack
|
||||
this
|
||||
|
||||
disableEventHandlersOnBubblePath: (event) ->
|
||||
bubblePathEventHandlers = []
|
||||
disabledHandler = ->
|
||||
element = $(event.target)
|
||||
while element.length
|
||||
if eventHandlers = element.data('events')?[event.type]
|
||||
for eventHandler in eventHandlers
|
||||
eventHandler.disabledHandler = eventHandler.handler
|
||||
eventHandler.handler = disabledHandler
|
||||
bubblePathEventHandlers.push(eventHandler)
|
||||
element = element.parent()
|
||||
bubblePathEventHandlers
|
||||
|
||||
restoreEventHandlersOnBubblePath: (eventHandlers) ->
|
||||
for eventHandler in eventHandlers
|
||||
eventHandler.handler = eventHandler.disabledHandler
|
||||
delete eventHandler.disabledHandler
|
||||
|
||||
unsubscribeFromActivationEvents: (activateHandler) ->
|
||||
if _.isArray(@metadata.activationEvents)
|
||||
rootView.off(event, activateHandler) for event in @metadata.activationEvents
|
||||
else
|
||||
rootView.off(event, selector, activateHandler) for event, selector of @metadata.activationEvents
|
||||
|
||||
subscribeToActivationEvents: () ->
|
||||
activateHandler = (event) =>
|
||||
bubblePathEventHandlers = @disableEventHandlersOnBubblePath(event)
|
||||
@activatePackageMain()
|
||||
$(event.target).trigger(event)
|
||||
@restoreEventHandlersOnBubblePath(bubblePathEventHandlers)
|
||||
@unsubscribeFromActivationEvents(activateHandler)
|
||||
|
||||
if _.isArray(@metadata.activationEvents)
|
||||
rootView.command(event, activateHandler) for event in @metadata.activationEvents
|
||||
else
|
||||
rootView.command(event, selector, activateHandler) for event, selector of @metadata.activationEvents
|
||||
|
||||
activatePackageMain: ->
|
||||
mainPath = @path
|
||||
mainPath = fs.join(mainPath, @metadata.main) if @metadata.main
|
||||
try
|
||||
mainPath = require.resolve(mainPath)
|
||||
catch e
|
||||
return
|
||||
if fs.isFile(mainPath)
|
||||
@packageMain = require(mainPath)
|
||||
config.setDefaults(@name, @packageMain.configDefaults)
|
||||
atom.activateAtomPackage(this)
|
||||
|
||||
loadMetadata: ->
|
||||
if metadataPath = fs.resolveExtension(fs.join(@path, 'package'), ['cson', 'json'])
|
||||
@metadata = CSON.readObject(metadataPath)
|
||||
@ -90,3 +42,68 @@ class AtomPackage extends Package
|
||||
stylesheetDirPath = fs.join(@path, 'stylesheets')
|
||||
for stylesheetPath in fs.list(stylesheetDirPath)
|
||||
requireStylesheet(stylesheetPath)
|
||||
|
||||
activate: ->
|
||||
if @deferActivation
|
||||
@subscribeToActivationEvents()
|
||||
else
|
||||
try
|
||||
if @requireMainModule()
|
||||
config.setDefaults(@name, @mainModule.configDefaults)
|
||||
atom.activateAtomPackage(this)
|
||||
catch e
|
||||
console.warn "Failed to activate package named '#{@name}'", e.stack
|
||||
|
||||
requireMainModule: ->
|
||||
return @mainModule if @mainModule
|
||||
mainPath =
|
||||
if @metadata.main
|
||||
fs.join(@path, @metadata.main)
|
||||
else
|
||||
fs.join(@path, 'index')
|
||||
mainPath = fs.resolveExtension(mainPath, ["", _.keys(require.extensions)...])
|
||||
@mainModule = require(mainPath) if fs.isFile(mainPath)
|
||||
|
||||
registerDeferredDeserializers: ->
|
||||
for deserializerName in @metadata.deferredDeserializers ? []
|
||||
registerDeferredDeserializer deserializerName, => @requireMainModule()
|
||||
|
||||
subscribeToActivationEvents: () ->
|
||||
return unless @metadata.activationEvents?
|
||||
|
||||
activateHandler = (event) =>
|
||||
bubblePathEventHandlers = @disableEventHandlersOnBubblePath(event)
|
||||
@deferActivation = false
|
||||
@activate()
|
||||
$(event.target).trigger(event)
|
||||
@restoreEventHandlersOnBubblePath(bubblePathEventHandlers)
|
||||
@unsubscribeFromActivationEvents(activateHandler)
|
||||
|
||||
if _.isArray(@metadata.activationEvents)
|
||||
rootView.command(event, activateHandler) for event in @metadata.activationEvents
|
||||
else
|
||||
rootView.command(event, selector, activateHandler) for event, selector of @metadata.activationEvents
|
||||
|
||||
unsubscribeFromActivationEvents: (activateHandler) ->
|
||||
if _.isArray(@metadata.activationEvents)
|
||||
rootView.off(event, activateHandler) for event in @metadata.activationEvents
|
||||
else
|
||||
rootView.off(event, selector, activateHandler) for event, selector of @metadata.activationEvents
|
||||
|
||||
disableEventHandlersOnBubblePath: (event) ->
|
||||
bubblePathEventHandlers = []
|
||||
disabledHandler = ->
|
||||
element = $(event.target)
|
||||
while element.length
|
||||
if eventHandlers = element.data('events')?[event.type]
|
||||
for eventHandler in eventHandlers
|
||||
eventHandler.disabledHandler = eventHandler.handler
|
||||
eventHandler.handler = disabledHandler
|
||||
bubblePathEventHandlers.push(eventHandler)
|
||||
element = element.parent()
|
||||
bubblePathEventHandlers
|
||||
|
||||
restoreEventHandlersOnBubblePath: (eventHandlers) ->
|
||||
for eventHandler in eventHandlers
|
||||
eventHandler.handler = eventHandler.disabledHandler
|
||||
delete eventHandler.disabledHandler
|
||||
|
@ -6,10 +6,10 @@ module.exports =
|
||||
class AtomTheme extends Theme
|
||||
|
||||
loadStylesheet: (stylesheetPath)->
|
||||
@stylesheets[stylesheetPath] = fs.read(stylesheetPath)
|
||||
@stylesheets[stylesheetPath] = window.loadStylesheet(stylesheetPath)
|
||||
|
||||
load: ->
|
||||
if fs.extension(@path) is '.css'
|
||||
if fs.extension(@path) in ['.css', '.less']
|
||||
@loadStylesheet(@path)
|
||||
else
|
||||
metadataPath = fs.resolveExtension(fs.join(@path, 'package'), ['cson', 'json'])
|
||||
@ -18,6 +18,6 @@ class AtomTheme extends Theme
|
||||
if stylesheetNames
|
||||
@loadStylesheet(fs.join(@path, name)) for name in stylesheetNames
|
||||
else
|
||||
@loadStylesheet(stylesheetPath) for stylesheetPath in fs.list(@path, ['.css'])
|
||||
@loadStylesheet(stylesheetPath) for stylesheetPath in fs.list(@path, ['.css', '.less'])
|
||||
|
||||
super
|
||||
|
@ -24,10 +24,10 @@ _.extend atom,
|
||||
|
||||
activateAtomPackage: (pack) ->
|
||||
@activatedAtomPackages.push(pack)
|
||||
pack.packageMain.activate(@atomPackageStates[pack.name] ? {})
|
||||
pack.mainModule.activate(@atomPackageStates[pack.name] ? {})
|
||||
|
||||
deactivateAtomPackages: ->
|
||||
pack.packageMain.deactivate?() for pack in @activatedAtomPackages
|
||||
pack.mainModule.deactivate?() for pack in @activatedAtomPackages
|
||||
@activatedAtomPackages = []
|
||||
|
||||
serializeAtomPackages: ->
|
||||
@ -35,7 +35,7 @@ _.extend atom,
|
||||
for pack in @loadedPackages
|
||||
if pack in @activatedAtomPackages
|
||||
try
|
||||
packageStates[pack.name] = pack.packageMain.serialize?()
|
||||
packageStates[pack.name] = pack.mainModule.serialize?()
|
||||
catch e
|
||||
console?.error("Exception serializing '#{pack.name}' package's module\n", e.stack)
|
||||
else
|
||||
@ -61,6 +61,9 @@ _.extend atom,
|
||||
|
||||
new LoadTextMatePackagesTask(textMatePackages).start() if textMatePackages.length > 0
|
||||
|
||||
activatePackages: ->
|
||||
pack.activate() for pack in @loadedPackages
|
||||
|
||||
getLoadedPackages: ->
|
||||
_.clone(@loadedPackages)
|
||||
|
||||
|
@ -19,6 +19,7 @@ class Config
|
||||
userPackagesDirPath: userPackagesDirPath
|
||||
defaultSettings: null
|
||||
settings: null
|
||||
configFileHasErrors: null
|
||||
|
||||
constructor: ->
|
||||
@defaultSettings =
|
||||
@ -34,18 +35,17 @@ class Config
|
||||
fs.makeDirectory(@configDirPath)
|
||||
|
||||
templateConfigDirPath = fs.resolve(window.resourcePath, 'dot-atom')
|
||||
|
||||
onConfigDirFile = (path) =>
|
||||
templatePath = fs.join(templateConfigDirPath, path)
|
||||
configPath = fs.join(@configDirPath, path)
|
||||
fs.write(configPath, fs.read(templatePath))
|
||||
relativePath = path.substring(templateConfigDirPath.length + 1)
|
||||
configPath = fs.join(@configDirPath, relativePath)
|
||||
fs.write(configPath, fs.read(path))
|
||||
fs.traverseTreeSync(templateConfigDirPath, onConfigDirFile, (path) -> true)
|
||||
|
||||
configThemeDirPath = fs.join(@configDirPath, 'themes')
|
||||
onThemeDirFile = (path) ->
|
||||
templatePath = fs.join(bundledThemesDirPath, path)
|
||||
configPath = fs.join(configThemeDirPath, path)
|
||||
fs.write(configPath, fs.read(templatePath))
|
||||
relativePath = path.substring(bundledThemesDirPath.length + 1)
|
||||
configPath = fs.join(configThemeDirPath, relativePath)
|
||||
fs.write(configPath, fs.read(path))
|
||||
fs.traverseTreeSync(bundledThemesDirPath, onThemeDirFile, (path) -> true)
|
||||
|
||||
load: ->
|
||||
@ -54,8 +54,13 @@ class Config
|
||||
|
||||
loadUserConfig: ->
|
||||
if fs.exists(@configFilePath)
|
||||
userConfig = CSON.readObject(@configFilePath)
|
||||
_.extend(@settings, userConfig)
|
||||
try
|
||||
userConfig = CSON.readObject(@configFilePath)
|
||||
_.extend(@settings, userConfig)
|
||||
catch e
|
||||
@configFileHasErrors = true
|
||||
console.error "Failed to load user config '#{@configFilePath}'", e.message
|
||||
console.error e.stack
|
||||
|
||||
get: (keyPath) ->
|
||||
_.valueForKeyPath(@settings, keyPath) ?
|
||||
@ -91,6 +96,7 @@ class Config
|
||||
subscription
|
||||
|
||||
update: ->
|
||||
return if @configFileHasErrors
|
||||
@save()
|
||||
@trigger 'updated'
|
||||
|
||||
|
@ -61,7 +61,7 @@ class Editor extends View
|
||||
else
|
||||
{editSession, @mini} = (editSessionOrOptions ? {})
|
||||
|
||||
requireStylesheet 'editor.css'
|
||||
requireStylesheet 'editor.less'
|
||||
|
||||
@id = Editor.nextEditorId++
|
||||
@lineCache = []
|
||||
|
@ -9,6 +9,7 @@ class PaneContainer extends View
|
||||
@deserialize: ({root}) ->
|
||||
container = new PaneContainer
|
||||
container.append(deserialize(root)) if root
|
||||
container.removeEmptyPanes()
|
||||
container
|
||||
|
||||
@content: ->
|
||||
@ -61,6 +62,23 @@ class PaneContainer extends View
|
||||
saveAll: ->
|
||||
pane.saveItems() for pane in @getPanes()
|
||||
|
||||
confirmClose: ->
|
||||
deferred = $.Deferred()
|
||||
modifiedItems = []
|
||||
for pane in @getPanes()
|
||||
modifiedItems.push(item) for item in pane.getItems() when item.isModified?()
|
||||
|
||||
cancel = => deferred.reject()
|
||||
saveNextModifiedItem = =>
|
||||
if modifiedItems.length == 0
|
||||
deferred.resolve()
|
||||
else
|
||||
item = modifiedItems.pop()
|
||||
@paneAtIndex(0).promptToSaveItem item, saveNextModifiedItem, cancel
|
||||
|
||||
saveNextModifiedItem()
|
||||
deferred.promise()
|
||||
|
||||
getPanes: ->
|
||||
@find('.pane').views()
|
||||
|
||||
@ -93,5 +111,9 @@ class PaneContainer extends View
|
||||
root.css(width: '100%', height: '100%', top: 0, left: 0)
|
||||
root.adjustDimensions()
|
||||
|
||||
removeEmptyPanes: ->
|
||||
for pane in @getPanes() when pane.getItems().length == 0
|
||||
pane.remove()
|
||||
|
||||
afterAttach: ->
|
||||
@adjustPaneDimensions()
|
||||
|
@ -11,7 +11,8 @@ class Pane extends View
|
||||
@div class: 'item-views', outlet: 'itemViews'
|
||||
|
||||
@deserialize: ({items, focused, activeItemUri}) ->
|
||||
pane = new Pane(items.map((item) -> deserialize(item))...)
|
||||
deserializedItems = _.compact(items.map((item) -> deserialize(item)))
|
||||
pane = new Pane(deserializedItems...)
|
||||
pane.showItemForUri(activeItemUri) if activeItemUri
|
||||
pane.focusOnAttach = true if focused
|
||||
pane
|
||||
@ -21,7 +22,7 @@ class Pane extends View
|
||||
|
||||
initialize: (@items...) ->
|
||||
@viewsByClassName = {}
|
||||
@showItem(@items[0])
|
||||
@showItem(@items[0]) if @items.length > 0
|
||||
|
||||
@command 'core:close', @destroyActiveItem
|
||||
@command 'core:save', @saveActiveItem
|
||||
@ -46,7 +47,7 @@ class Pane extends View
|
||||
@command 'pane:split-down', => @splitDown()
|
||||
@command 'pane:close', => @destroyItems()
|
||||
@command 'pane:close-other-items', => @destroyInactiveItems()
|
||||
@on 'focus', => @activeView.focus(); false
|
||||
@on 'focus', => @activeView?.focus(); false
|
||||
@on 'focusin', => @makeActive()
|
||||
@on 'focusout', => @autosaveActiveItem()
|
||||
|
||||
@ -72,6 +73,12 @@ class Pane extends View
|
||||
isActive: ->
|
||||
@hasClass('active')
|
||||
|
||||
getNextPane: ->
|
||||
panes = @getContainer()?.getPanes()
|
||||
return unless panes.length > 1
|
||||
nextIndex = (panes.indexOf(this) + 1) % panes.length
|
||||
panes[nextIndex]
|
||||
|
||||
getItems: ->
|
||||
new Array(@items...)
|
||||
|
||||
@ -152,13 +159,13 @@ class Pane extends View
|
||||
destroyInactiveItems: ->
|
||||
@destroyItem(item) for item in @getItems() when item isnt @activeItem
|
||||
|
||||
promptToSaveItem: (item, nextAction) ->
|
||||
promptToSaveItem: (item, nextAction, cancelAction) ->
|
||||
uri = item.getUri()
|
||||
atom.confirm(
|
||||
"'#{item.getTitle()}' has changes, do you want to save them?"
|
||||
"'#{item.getTitle?() ? item.getUri()}' has changes, do you want to save them?"
|
||||
"Your changes will be lost if close this item without saving."
|
||||
"Save", => @saveItem(item, nextAction)
|
||||
"Cancel", null
|
||||
"Cancel", cancelAction
|
||||
"Don't Save", nextAction
|
||||
)
|
||||
|
||||
|
@ -75,6 +75,9 @@ class RootView extends View
|
||||
panes: @panes.serialize()
|
||||
packages: atom.serializeAtomPackages()
|
||||
|
||||
confirmClose: ->
|
||||
@panes.confirmClose()
|
||||
|
||||
handleFocus: (e) ->
|
||||
if @getActivePane()
|
||||
@getActivePane().focus()
|
||||
@ -115,7 +118,7 @@ class RootView extends View
|
||||
updateTitle: ->
|
||||
if projectPath = project.getPath()
|
||||
if item = @getActivePaneItem()
|
||||
@setTitle("#{item.getTitle()} - #{projectPath}")
|
||||
@setTitle("#{item.getTitle?() ? 'untitled'} - #{projectPath}")
|
||||
else
|
||||
@setTitle(projectPath)
|
||||
else
|
||||
|
@ -20,7 +20,7 @@ class SelectList extends View
|
||||
cancelling: false
|
||||
|
||||
initialize: ->
|
||||
requireStylesheet 'select-list.css'
|
||||
requireStylesheet 'select-list.less'
|
||||
|
||||
@miniEditor.getBuffer().on 'changed', => @schedulePopulateList()
|
||||
@miniEditor.on 'focusout', => @cancel() unless @cancelling
|
||||
|
@ -32,6 +32,8 @@ class TextMatePackage extends Package
|
||||
console.warn "Failed to load package at '#{@path}'", e.stack
|
||||
this
|
||||
|
||||
activate: -> # no-op
|
||||
|
||||
getGrammars: -> @grammars
|
||||
|
||||
readGrammars: ->
|
||||
|
@ -11,7 +11,7 @@ class Theme
|
||||
if fs.exists(name)
|
||||
path = name
|
||||
else
|
||||
path = fs.resolve(config.themeDirPaths..., name, ['', '.tmTheme', '.css'])
|
||||
path = fs.resolve(config.themeDirPaths..., name, ['', '.tmTheme', '.css', 'less'])
|
||||
|
||||
throw new Error("No theme exists named '#{name}'") unless path
|
||||
|
||||
|
@ -1,11 +1,13 @@
|
||||
fs = require 'fs-utils'
|
||||
$ = require 'jquery'
|
||||
ChildProcess = require 'child_process'
|
||||
{less} = require 'less'
|
||||
{spawn} = require 'child_process'
|
||||
require 'jquery-extensions'
|
||||
require 'underscore-extensions'
|
||||
require 'space-pen-extensions'
|
||||
|
||||
deserializers = {}
|
||||
deferredDeserializers = {}
|
||||
|
||||
# This method is called in any window needing a general environment, including specs
|
||||
window.setUpEnvironment = ->
|
||||
@ -23,17 +25,17 @@ window.setUpEnvironment = ->
|
||||
$(document).on 'keydown', keymap.handleKeyEvent
|
||||
keymap.bindDefaultKeys()
|
||||
|
||||
requireStylesheet 'reset.css'
|
||||
requireStylesheet 'atom.css'
|
||||
requireStylesheet 'tabs.css'
|
||||
requireStylesheet 'tree-view.css'
|
||||
requireStylesheet 'status-bar.css'
|
||||
requireStylesheet 'command-panel.css'
|
||||
requireStylesheet 'fuzzy-finder.css'
|
||||
requireStylesheet 'overlay.css'
|
||||
requireStylesheet 'popover-list.css'
|
||||
requireStylesheet 'notification.css'
|
||||
requireStylesheet 'markdown.css'
|
||||
requireStylesheet 'reset.less'
|
||||
requireStylesheet 'atom.less'
|
||||
requireStylesheet 'tabs.less'
|
||||
requireStylesheet 'tree-view.less'
|
||||
requireStylesheet 'status-bar.less'
|
||||
requireStylesheet 'command-panel.less'
|
||||
requireStylesheet 'fuzzy-finder.less'
|
||||
requireStylesheet 'overlay.less'
|
||||
requireStylesheet 'popover-list.less'
|
||||
requireStylesheet 'notification.less'
|
||||
requireStylesheet 'markdown.less'
|
||||
|
||||
if nativeStylesheetPath = require.resolve("#{platform}.css")
|
||||
requireStylesheet(nativeStylesheetPath)
|
||||
@ -52,10 +54,11 @@ window.startup = ->
|
||||
handleWindowEvents()
|
||||
config.load()
|
||||
atom.loadTextPackage()
|
||||
buildProjectAndRootView()
|
||||
keymap.loadBundledKeymaps()
|
||||
atom.loadThemes()
|
||||
atom.loadPackages()
|
||||
buildProjectAndRootView()
|
||||
atom.activatePackages()
|
||||
keymap.loadUserKeymaps()
|
||||
atom.requireUserInitScript()
|
||||
$(window).on 'beforeunload', -> shutdown(); false
|
||||
@ -81,14 +84,13 @@ window.installAtomCommand = (commandPath) ->
|
||||
bundledCommandPath = fs.resolve(window.resourcePath, 'atom.sh')
|
||||
if bundledCommandPath?
|
||||
fs.write(commandPath, fs.read(bundledCommandPath))
|
||||
ChildProcess.exec("chmod u+x '#{commandPath}'")
|
||||
spawn("chmod u+x '#{commandPath}'")
|
||||
|
||||
window.handleWindowEvents = ->
|
||||
$(window).on 'core:close', => window.close()
|
||||
$(window).command 'window:close', => window.close()
|
||||
$(window).command 'window:toggle-full-screen', => atom.toggleFullScreen()
|
||||
$(window).on 'focus', -> $("body").removeClass('is-blurred')
|
||||
$(window).on 'blur', -> $("body").addClass('is-blurred')
|
||||
$(window).command 'window:close', => confirmClose()
|
||||
|
||||
window.buildProjectAndRootView = ->
|
||||
RootView = require 'root-view'
|
||||
@ -115,10 +117,21 @@ window.stylesheetElementForId = (id) ->
|
||||
|
||||
window.requireStylesheet = (path) ->
|
||||
if fullPath = require.resolve(path)
|
||||
window.applyStylesheet(fullPath, fs.read(fullPath))
|
||||
unless fullPath
|
||||
content = window.loadStylesheet(fullPath)
|
||||
window.applyStylesheet(fullPath, content)
|
||||
else
|
||||
console.log "bad", path
|
||||
throw new Error("Could not find a file at path '#{path}'")
|
||||
|
||||
window.loadStylesheet = (path) ->
|
||||
content = fs.read(path)
|
||||
if fs.extension(path) == '.less'
|
||||
(new less.Parser).parse content, (e, tree) ->
|
||||
throw new Error(e.message, file, e.line) if e
|
||||
content = tree.toCSS()
|
||||
|
||||
content
|
||||
|
||||
window.removeStylesheet = (path) ->
|
||||
unless fullPath = require.resolve(path)
|
||||
throw new Error("Could not find a file at path '#{path}'")
|
||||
@ -151,6 +164,9 @@ window.registerDeserializers = (args...) ->
|
||||
window.registerDeserializer = (klass) ->
|
||||
deserializers[klass.name] = klass
|
||||
|
||||
window.registerDeferredDeserializer = (name, fn) ->
|
||||
deferredDeserializers[name] = fn
|
||||
|
||||
window.unregisterDeserializer = (klass) ->
|
||||
delete deserializers[klass.name]
|
||||
|
||||
@ -160,7 +176,11 @@ window.deserialize = (state) ->
|
||||
deserializer.deserialize(state)
|
||||
|
||||
window.getDeserializer = (state) ->
|
||||
deserializers[state?.deserializer]
|
||||
name = state?.deserializer
|
||||
if deferredDeserializers[name]
|
||||
deferredDeserializers[name]()
|
||||
delete deferredDeserializers[name]
|
||||
deserializers[name]
|
||||
|
||||
window.measure = (description, fn) ->
|
||||
start = new Date().getTime()
|
||||
@ -175,3 +195,6 @@ window.profile = (description, fn) ->
|
||||
value = fn()
|
||||
console.profileEnd(description)
|
||||
value
|
||||
|
||||
confirmClose = ->
|
||||
rootView.confirmClose().done -> window.close()
|
||||
|
@ -7,7 +7,7 @@ describe "CommandLogger", ->
|
||||
beforeEach ->
|
||||
window.rootView = new RootView
|
||||
rootView.open('sample.js')
|
||||
commandLogger = window.loadPackage('command-logger').packageMain
|
||||
commandLogger = window.loadPackage('command-logger').mainModule
|
||||
commandLogger.eventLog = {}
|
||||
editor = rootView.getActiveView()
|
||||
|
||||
|
@ -115,7 +115,7 @@ class CommandPanelView extends View
|
||||
escapedCommand: ->
|
||||
@miniEditor.getText()
|
||||
|
||||
execute: (command=@escapedCommand())->
|
||||
execute: (command=@escapedCommand()) ->
|
||||
@loadingMessage.show()
|
||||
@errorMessages.empty()
|
||||
|
||||
|
@ -16,7 +16,7 @@ class SelectAllMatchesInProject extends Command
|
||||
promise = project.scan @regex, ({path, range}) ->
|
||||
operations.push(new Operation(
|
||||
project: project
|
||||
buffer: project.bufferForPath(path)
|
||||
path: path
|
||||
bufferRange: range
|
||||
))
|
||||
|
||||
|
@ -4,7 +4,7 @@ module.exports =
|
||||
class OperationView extends View
|
||||
@content: ({operation} = {}) ->
|
||||
{prefix, suffix, match, range} = operation.preview()
|
||||
@li 'data-index': operation.index, class: 'operation', =>
|
||||
@li class: 'operation', =>
|
||||
@span range.start.row + 1, class: 'line-number'
|
||||
@span class: 'preview', =>
|
||||
@span prefix
|
||||
|
@ -1,22 +1,30 @@
|
||||
module.exports =
|
||||
class Operation
|
||||
constructor: ({@project, @buffer, bufferRange, @newText, @preserveSelection, @errorMessage}) ->
|
||||
@buffer.retain()
|
||||
@marker = @buffer.markRange(bufferRange)
|
||||
constructor: ({@project, @path, @buffer, @bufferRange, @newText, @preserveSelection, @errorMessage}) ->
|
||||
if @buffer?
|
||||
@buffer.retain()
|
||||
@getMarker()
|
||||
|
||||
getMarker: ->
|
||||
@marker ?= @getBuffer().markRange(@bufferRange)
|
||||
|
||||
getBuffer: ->
|
||||
@buffer ?= @project.bufferForPath(@path).retain()
|
||||
|
||||
getPath: ->
|
||||
@project.relativize(@buffer.getPath())
|
||||
path = @path ? @getBuffer().getPath()
|
||||
@project.relativize(path)
|
||||
|
||||
getBufferRange: ->
|
||||
@buffer.getMarkerRange(@marker)
|
||||
@getBuffer().getMarkerRange(@getMarker())
|
||||
|
||||
execute: (editSession) ->
|
||||
@buffer.change(@getBufferRange(), @newText) if @newText
|
||||
@getBuffer().change(@getBufferRange(), @newText) if @newText
|
||||
@getBufferRange() unless @preserveSelection
|
||||
|
||||
preview: ->
|
||||
range = @buffer.getMarkerRange(@marker)
|
||||
line = @buffer.lineForRow(range.start.row)
|
||||
range = @getBuffer().getMarkerRange(@getMarker())
|
||||
line = @getBuffer().lineForRow(range.start.row)
|
||||
prefix = line[0...range.start.column]
|
||||
match = line[range.start.column...range.end.column]
|
||||
suffix = line[range.end.column..]
|
||||
@ -24,5 +32,5 @@ class Operation
|
||||
{prefix, suffix, match, range}
|
||||
|
||||
destroy: ->
|
||||
@buffer.destroyMarker(@marker)
|
||||
@buffer.release()
|
||||
@buffer?.destroyMarker(@marker) if @marker?
|
||||
@buffer?.release()
|
||||
|
@ -5,16 +5,14 @@ $ = require 'jquery'
|
||||
|
||||
module.exports =
|
||||
class PathView extends View
|
||||
@content: ({path, operations, previewList} = {}) ->
|
||||
@content: ({path, previewList} = {}) ->
|
||||
classes = ['path']
|
||||
classes.push('readme') if fs.isReadmePath(path)
|
||||
@li class: classes.join(' '), =>
|
||||
@div outlet: 'pathDetails', class: 'path-details', =>
|
||||
@span class: 'path-name', path
|
||||
@span "(#{operations.length})", class: 'path-match-number'
|
||||
@span outlet: 'description', class: 'path-match-number'
|
||||
@ul outlet: 'matches', class: 'matches', =>
|
||||
for operation in operations
|
||||
@subview "operation#{operation.index}", new OperationView({operation, previewList})
|
||||
|
||||
initialize: ({@previewList}) ->
|
||||
@pathDetails.on 'mousedown', => @toggle(true)
|
||||
@ -27,6 +25,10 @@ class PathView extends View
|
||||
@toggle(true)
|
||||
false
|
||||
|
||||
addOperation: (operation) ->
|
||||
@matches.append new OperationView({operation, @previewList})
|
||||
@description.text("(#{@matches.find('li').length})")
|
||||
|
||||
isSelected: ->
|
||||
@hasClass('selected') or @find('.selected').length
|
||||
|
||||
|
@ -11,12 +11,17 @@ class PreviewList extends ScrollView
|
||||
@ol class: 'preview-list', tabindex: -1
|
||||
|
||||
operations: null
|
||||
viewsForPath: null
|
||||
pixelOverdraw: 100
|
||||
lastRenderedOperationIndex: null
|
||||
|
||||
initialize: ->
|
||||
super
|
||||
|
||||
@on 'core:move-down', => @selectNextOperation(); false
|
||||
@on 'core:move-up', => @selectPreviousOperation(); false
|
||||
@on 'scroll', =>
|
||||
@renderOperations() if @scrollBottom() >= (@prop('scrollHeight'))
|
||||
|
||||
@command 'command-panel:collapse-all', => @collapseAllPaths()
|
||||
@command 'command-panel:expand-all', => @expandAllPaths()
|
||||
@ -25,6 +30,7 @@ class PreviewList extends ScrollView
|
||||
@children().each (index, element) -> $(element).view().expand()
|
||||
|
||||
collapseAllPaths: ->
|
||||
@renderOperations(renderAll: true)
|
||||
@children().each (index, element) -> $(element).view().collapse()
|
||||
|
||||
destroy: ->
|
||||
@ -35,23 +41,31 @@ class PreviewList extends ScrollView
|
||||
populate: (operations) ->
|
||||
@destroyOperations() if @operations
|
||||
@operations = operations
|
||||
@lastRenderedOperationIndex = 0
|
||||
@empty()
|
||||
|
||||
operation.index = index for operation, index in operations
|
||||
operationsByPath = _.groupBy(operations, (operation) -> operation.getPath())
|
||||
for path, operations of operationsByPath
|
||||
@append new PathView({path, operations, previewList: this})
|
||||
@viewsForPath = {}
|
||||
|
||||
@show()
|
||||
@find('.operation:first').addClass('selected')
|
||||
@setLineNumberWidth()
|
||||
@renderOperations()
|
||||
|
||||
setLineNumberWidth: ->
|
||||
lineNumbers = @find('.line-number')
|
||||
maxWidth = 0
|
||||
lineNumbers.each (index, element) ->
|
||||
maxWidth = Math.max($(element).outerWidth(), maxWidth)
|
||||
lineNumbers.width(maxWidth)
|
||||
@find('.operation:first').addClass('selected')
|
||||
|
||||
renderOperations: ({renderAll}={}) ->
|
||||
renderAll ?= false
|
||||
startingScrollHeight = @prop('scrollHeight')
|
||||
for operation in @operations[@lastRenderedOperationIndex..]
|
||||
pathView = @pathViewForPath(operation.getPath())
|
||||
pathView.addOperation(operation)
|
||||
@lastRenderedOperationIndex++
|
||||
break if not renderAll and @prop('scrollHeight') >= startingScrollHeight + @pixelOverdraw and @prop('scrollHeight') > @height() + @pixelOverdraw
|
||||
|
||||
pathViewForPath: (path) ->
|
||||
pathView = @viewsForPath[path]
|
||||
if not pathView
|
||||
pathView = new PathView({path: path, previewList: this})
|
||||
@viewsForPath[path] = pathView
|
||||
@append(pathView)
|
||||
pathView
|
||||
|
||||
selectNextOperation: ->
|
||||
selectedView = @find('.selected').view()
|
||||
|
@ -11,7 +11,7 @@ describe "CommandPanel", ->
|
||||
rootView.enableKeymap()
|
||||
editSession = rootView.getActivePaneItem()
|
||||
buffer = editSession.buffer
|
||||
commandPanelMain = window.loadPackage('command-panel', activateImmediately: true).packageMain
|
||||
commandPanelMain = window.loadPackage('command-panel', activateImmediately: true).mainModule
|
||||
commandPanel = commandPanelMain.commandPanelView
|
||||
commandPanel.history = []
|
||||
commandPanel.historyIndex = 0
|
||||
|
45
src/packages/command-panel/spec/preview-list-spec.coffee
Normal file
45
src/packages/command-panel/spec/preview-list-spec.coffee
Normal file
@ -0,0 +1,45 @@
|
||||
RootView = require 'root-view'
|
||||
CommandPanelView = require 'command-panel/lib/command-panel-view'
|
||||
_ = require 'underscore'
|
||||
|
||||
describe "Preview List", ->
|
||||
[previewList, commandPanelMain, commandPanelView] = []
|
||||
|
||||
beforeEach ->
|
||||
window.rootView = new RootView()
|
||||
rootView.attachToDom()
|
||||
commandPanelMain = window.loadPackage('command-panel', activateImmediately: true).mainModule
|
||||
commandPanelView = commandPanelMain.commandPanelView
|
||||
previewList = commandPanelView.previewList
|
||||
rootView.trigger 'command-panel:toggle'
|
||||
|
||||
describe "when the list is scrollable", ->
|
||||
it "adds more operations to the DOM when `scrollBottom` nears the `pixelOverdraw`", ->
|
||||
waitsForPromise ->
|
||||
commandPanelView.execute('X x/so/')
|
||||
|
||||
runs ->
|
||||
expect(previewList.prop('scrollHeight')).toBeGreaterThan previewList.height()
|
||||
previousScrollHeight = previewList.prop('scrollHeight')
|
||||
previousOperationCount = previewList.find("li").length
|
||||
|
||||
previewList.scrollTop(previewList.pixelOverdraw / 2)
|
||||
previewList.trigger('scroll') # Not sure why scroll event isn't being triggered on it's own
|
||||
expect(previewList.prop('scrollHeight')).toBe previousScrollHeight
|
||||
expect(previewList.find("li").length).toBe previousOperationCount
|
||||
|
||||
previewList.scrollToBottom()
|
||||
previewList.trigger('scroll') # Not sure why scroll event isn't being triggered on it's own
|
||||
expect(previewList.prop('scrollHeight')).toBeGreaterThan previousScrollHeight
|
||||
expect(previewList.find("li").length).toBeGreaterThan previousOperationCount
|
||||
|
||||
it "renders all operations if the preview items are collapsed", ->
|
||||
waitsForPromise ->
|
||||
commandPanelView.execute('X x/so/')
|
||||
|
||||
runs ->
|
||||
expect(previewList.prop('scrollHeight')).toBeGreaterThan previewList.height()
|
||||
previousScrollHeight = previewList.prop('scrollHeight')
|
||||
previousOperationCount = previewList.find("li").length
|
||||
previewList.collapseAllPaths()
|
||||
expect(previewList.find("li").length).toBeGreaterThan previousOperationCount
|
@ -17,7 +17,7 @@ describe "EditorStats", ->
|
||||
beforeEach ->
|
||||
window.rootView = new RootView
|
||||
rootView.open('sample.js')
|
||||
editorStats = window.loadPackage('editor-stats').packageMain.stats
|
||||
editorStats = window.loadPackage('editor-stats').mainModule.stats
|
||||
|
||||
describe "when a keyup event is triggered", ->
|
||||
beforeEach ->
|
||||
|
@ -1,45 +0,0 @@
|
||||
.editor-stats-wrapper {
|
||||
padding: 5px;
|
||||
box-sizing: border-box;
|
||||
border-top: 1px solid rgba(255, 255, 255, 0.05);
|
||||
z-index: 9999;
|
||||
}
|
||||
|
||||
.editor-stats {
|
||||
height: 50px;
|
||||
width: 100%;
|
||||
background: #1d1f21;
|
||||
border: 1px solid rgba(0, 0, 0, 0.3);
|
||||
border-bottom: 1px solid rgba(255, 255, 255, 0.1);
|
||||
border-right: 1px solid rgba(255, 255, 255, 0.1);
|
||||
}
|
||||
|
||||
.editor-stats rect.bar {
|
||||
fill: rgba(255, 255, 255, 0.2);
|
||||
shape-rendering: crispedges;
|
||||
}
|
||||
|
||||
.editor-stats rect.bar.max {
|
||||
fill: rgba(0, 163, 255, 1);
|
||||
}
|
||||
|
||||
.editor-stats text {
|
||||
font-size: 10px;
|
||||
fill: rgba(255, 255, 255, 0.2);
|
||||
font-family: Courier;
|
||||
}
|
||||
|
||||
.editor-stats .minor text {
|
||||
display: none;
|
||||
}
|
||||
|
||||
.editor-stats line {
|
||||
stroke: #ccc;
|
||||
stroke-opacity: 0.05;
|
||||
stroke-width: 1px;
|
||||
shape-rendering: crispedges;
|
||||
}
|
||||
|
||||
.editor-stats path.domain {
|
||||
fill: none;
|
||||
}
|
45
src/packages/editor-stats/stylesheets/editor-stats.less
Normal file
45
src/packages/editor-stats/stylesheets/editor-stats.less
Normal file
@ -0,0 +1,45 @@
|
||||
.editor-stats-wrapper {
|
||||
padding: 5px;
|
||||
box-sizing: border-box;
|
||||
border-top: 1px solid rgba(255, 255, 255, 0.05);
|
||||
z-index: 9999;
|
||||
}
|
||||
|
||||
.editor-stats {
|
||||
height: 50px;
|
||||
width: 100%;
|
||||
background: #1d1f21;
|
||||
border: 1px solid rgba(0, 0, 0, 0.3);
|
||||
border-bottom: 1px solid rgba(255, 255, 255, 0.1);
|
||||
border-right: 1px solid rgba(255, 255, 255, 0.1);
|
||||
|
||||
.bar {
|
||||
fill: rgba(255, 255, 255, 0.2);
|
||||
shape-rendering: crispedges;
|
||||
|
||||
&.max {
|
||||
fill: rgba(0, 163, 255, 1);
|
||||
}
|
||||
}
|
||||
|
||||
text {
|
||||
font-size: 10px;
|
||||
fill: rgba(255, 255, 255, 0.2);
|
||||
font-family: Courier;
|
||||
}
|
||||
|
||||
.minor text {
|
||||
display: none;
|
||||
}
|
||||
|
||||
line {
|
||||
stroke: #ccc;
|
||||
stroke-opacity: 0.05;
|
||||
stroke-width: 1px;
|
||||
shape-rendering: crispedges;
|
||||
}
|
||||
|
||||
path.domain {
|
||||
display: none;
|
||||
}
|
||||
}
|
@ -35,9 +35,10 @@ module.exports =
|
||||
|
||||
createView: ->
|
||||
unless @fuzzyFinderView
|
||||
@loadPathsTask?.abort()
|
||||
FuzzyFinderView = require 'fuzzy-finder/lib/fuzzy-finder-view'
|
||||
@fuzzyFinderView = new FuzzyFinderView()
|
||||
if @projectPaths? and not @fuzzyFinderView.projectPaths?
|
||||
if @projectPaths?.length > 0 and not @fuzzyFinderView.projectPaths?
|
||||
@fuzzyFinderView.projectPaths = @projectPaths
|
||||
@fuzzyFinderView.reloadProjectPaths = false
|
||||
@fuzzyFinderView
|
||||
|
@ -13,7 +13,7 @@ describe 'FuzzyFinder', ->
|
||||
window.rootView = new RootView
|
||||
rootView.open('sample.js')
|
||||
rootView.enableKeymap()
|
||||
finderView = window.loadPackage("fuzzy-finder").packageMain.createView()
|
||||
finderView = window.loadPackage("fuzzy-finder").mainModule.createView()
|
||||
|
||||
describe "file-finder behavior", ->
|
||||
describe "toggling", ->
|
||||
|
@ -1,5 +1,2 @@
|
||||
'.editor':
|
||||
'ctrl-m': 'markdown-preview:toggle'
|
||||
|
||||
'.markdown-preview':
|
||||
'ctrl-m': 'markdown-preview:toggle'
|
||||
'ctrl-m': 'markdown-preview:show'
|
||||
|
@ -1,49 +1,36 @@
|
||||
ScrollView = require 'scroll-view'
|
||||
fs = require 'fs-utils'
|
||||
$ = require 'jquery'
|
||||
ScrollView = require 'scroll-view'
|
||||
{$$$} = require 'space-pen'
|
||||
|
||||
module.exports =
|
||||
class MarkdownPreviewView extends ScrollView
|
||||
@activate: ->
|
||||
@instance = new MarkdownPreviewView
|
||||
registerDeserializer(this)
|
||||
|
||||
@deserialize: ({path}) ->
|
||||
new MarkdownPreviewView(project.bufferForPath(path))
|
||||
|
||||
@content: ->
|
||||
@div class: 'markdown-preview', tabindex: -1, =>
|
||||
@div class: 'markdown-body', outlet: 'markdownBody'
|
||||
@div class: 'markdown-preview', tabindex: -1
|
||||
|
||||
initialize: ->
|
||||
initialize: (@buffer) ->
|
||||
super
|
||||
@fetchRenderedMarkdown()
|
||||
@on 'core:move-up', => @scrollUp()
|
||||
@on 'core:move-down', => @scrollDown()
|
||||
|
||||
rootView.command 'markdown-preview:toggle', => @toggle()
|
||||
@on 'blur', => @detach() unless document.activeElement is this[0]
|
||||
@command 'core:cancel', => @detach()
|
||||
serialize: ->
|
||||
deserializer: 'MarkdownPreviewView'
|
||||
path: @buffer.getPath()
|
||||
|
||||
toggle: ->
|
||||
if @hasParent()
|
||||
@detach()
|
||||
else
|
||||
@attach()
|
||||
getTitle: ->
|
||||
"Markdown Preview – #{@buffer.getBaseName()}"
|
||||
|
||||
attach: ->
|
||||
return unless @isMarkdownEditor()
|
||||
rootView.append(this)
|
||||
@markdownBody.html(@getLoadingHtml())
|
||||
@loadHtml()
|
||||
@focus()
|
||||
getUri: ->
|
||||
"markdown-preview:#{@buffer.getPath()}"
|
||||
|
||||
detach: ->
|
||||
return if @detaching
|
||||
@detaching = true
|
||||
super
|
||||
rootView.focus()
|
||||
@detaching = false
|
||||
|
||||
getActiveText: ->
|
||||
rootView.getActiveView()?.getText()
|
||||
|
||||
getErrorHtml: (error) ->
|
||||
$$$ ->
|
||||
setErrorHtml: ->
|
||||
@html $$$ ->
|
||||
@h2 'Previewing Markdown Failed'
|
||||
@h3 'Possible Reasons'
|
||||
@ul =>
|
||||
@ -52,29 +39,18 @@ class MarkdownPreviewView extends ScrollView
|
||||
@a 'github.com', href: 'https://github.com'
|
||||
@span '.'
|
||||
|
||||
getLoadingHtml: ->
|
||||
$$$ ->
|
||||
@div class: 'markdown-spinner', 'Loading Markdown...'
|
||||
setLoading: ->
|
||||
@html($$$ -> @div class: 'markdown-spinner', 'Loading Markdown...')
|
||||
|
||||
loadHtml: (text) ->
|
||||
payload =
|
||||
mode: 'markdown'
|
||||
text: @getActiveText()
|
||||
request =
|
||||
fetchRenderedMarkdown: (text) ->
|
||||
@setLoading()
|
||||
$.ajax
|
||||
url: 'https://api.github.com/markdown'
|
||||
type: 'POST'
|
||||
dataType: 'html'
|
||||
contentType: 'application/json; charset=UTF-8'
|
||||
data: JSON.stringify(payload)
|
||||
success: (html) => @setHtml(html)
|
||||
error: (jqXhr, error) => @setHtml(@getErrorHtml(error))
|
||||
$.ajax(request)
|
||||
|
||||
setHtml: (html) ->
|
||||
@markdownBody.html(html) if @hasParent()
|
||||
|
||||
isMarkdownEditor: (path) ->
|
||||
editor = rootView.getActiveView()
|
||||
return unless editor?
|
||||
return true if editor.getGrammar().scopeName is 'source.gfm'
|
||||
path and fs.isMarkdownExtension(fs.extension(path))
|
||||
data: JSON.stringify
|
||||
mode: 'markdown'
|
||||
text: @buffer.getText()
|
||||
success: (html) => @html(html)
|
||||
error: => @setErrorHtml()
|
||||
|
25
src/packages/markdown-preview/lib/markdown-preview.coffee
Normal file
25
src/packages/markdown-preview/lib/markdown-preview.coffee
Normal file
@ -0,0 +1,25 @@
|
||||
EditSession = require 'edit-session'
|
||||
MarkdownPreviewView = require 'markdown-preview/lib/markdown-preview-view'
|
||||
|
||||
module.exports =
|
||||
activate: ->
|
||||
rootView.command 'markdown-preview:show', '.editor', => @show()
|
||||
|
||||
show: ->
|
||||
activePane = rootView.getActivePane()
|
||||
item = activePane.activeItem
|
||||
|
||||
if not item instanceof EditSession
|
||||
console.warn("Can not render markdown for #{item.getUri()}")
|
||||
return
|
||||
|
||||
editSession = item
|
||||
if nextPane = activePane.getNextPane()
|
||||
if preview = nextPane.itemForUri("markdown-preview:#{editSession.getPath()}")
|
||||
nextPane.showItem(preview)
|
||||
preview.fetchRenderedMarkdown()
|
||||
else
|
||||
nextPane.showItem(new MarkdownPreviewView(editSession.buffer))
|
||||
else
|
||||
activePane.splitRight(new MarkdownPreviewView(editSession.buffer))
|
||||
activePane.focus()
|
@ -1,3 +1,4 @@
|
||||
'main': 'lib/markdown-preview-view'
|
||||
'main': 'lib/markdown-preview'
|
||||
'activationEvents':
|
||||
'markdown-preview:toggle': '.editor'
|
||||
'markdown-preview:show': '.editor'
|
||||
'deferredDeserializers': ['MarkdownPreviewView']
|
||||
|
@ -1,84 +1,67 @@
|
||||
$ = require 'jquery'
|
||||
RootView = require 'root-view'
|
||||
MarkdownPreview = require 'markdown-preview/lib/markdown-preview-view'
|
||||
_ = require 'underscore'
|
||||
MarkdownPreviewView = require 'markdown-preview/lib/markdown-preview-view'
|
||||
{$$} = require 'space-pen'
|
||||
|
||||
describe "MarkdownPreview", ->
|
||||
describe "MarkdownPreview package", ->
|
||||
beforeEach ->
|
||||
project.setPath(project.resolve('markdown'))
|
||||
window.rootView = new RootView
|
||||
window.loadPackage("markdown-preview")
|
||||
spyOn(MarkdownPreview.prototype, 'loadHtml')
|
||||
window.loadPackage("markdown-preview", activateImmediately: true)
|
||||
spyOn(MarkdownPreviewView.prototype, 'fetchRenderedMarkdown')
|
||||
|
||||
describe "markdown-preview:toggle event", ->
|
||||
it "toggles on/off a preview for a .md file", ->
|
||||
rootView.open('file.md')
|
||||
editor = rootView.getActiveView()
|
||||
expect(rootView.find('.markdown-preview')).not.toExist()
|
||||
editor.trigger('markdown-preview:toggle')
|
||||
describe "markdown-preview:show", ->
|
||||
beforeEach ->
|
||||
rootView.open("file.markdown")
|
||||
|
||||
markdownPreviewView = rootView.find('.markdown-preview')?.view()
|
||||
expect(rootView.find('.markdown-preview')).toExist()
|
||||
expect(markdownPreviewView.loadHtml).toHaveBeenCalled()
|
||||
markdownPreviewView.trigger('markdown-preview:toggle')
|
||||
expect(rootView.find('.markdown-preview')).not.toExist()
|
||||
describe "when the active item is an edit session", ->
|
||||
beforeEach ->
|
||||
rootView.attachToDom()
|
||||
|
||||
it "displays a preview for a .markdown file", ->
|
||||
rootView.open('file.markdown')
|
||||
editor = rootView.getActiveView()
|
||||
expect(rootView.find('.markdown-preview')).not.toExist()
|
||||
editor.trigger('markdown-preview:toggle')
|
||||
expect(rootView.find('.markdown-preview')).toExist()
|
||||
markdownPreviewView = rootView.find('.markdown-preview')?.view()
|
||||
expect(markdownPreviewView.loadHtml).toHaveBeenCalled()
|
||||
describe "when a preview item has not been created for the edit session's uri", ->
|
||||
describe "when there is more than one pane", ->
|
||||
it "shows a markdown preview for the current buffer on the next pane", ->
|
||||
rootView.getActivePane().splitRight()
|
||||
[pane1, pane2] = rootView.getPanes()
|
||||
pane1.focus()
|
||||
|
||||
it "displays a preview for a file with the source.gfm grammar scope", ->
|
||||
gfmGrammar = _.find syntax.grammars, (grammar) -> grammar.scopeName is 'source.gfm'
|
||||
rootView.open('file.js')
|
||||
editor = rootView.getActiveView()
|
||||
project.addGrammarOverrideForPath(editor.getPath(), gfmGrammar)
|
||||
editor.reloadGrammar()
|
||||
expect(rootView.find('.markdown-preview')).not.toExist()
|
||||
editor.trigger('markdown-preview:toggle')
|
||||
expect(rootView.find('.markdown-preview')).toExist()
|
||||
markdownPreviewView = rootView.find('.markdown-preview')?.view()
|
||||
expect(markdownPreviewView.loadHtml).toHaveBeenCalled()
|
||||
rootView.getActiveView().trigger 'markdown-preview:show'
|
||||
|
||||
it "does not display a preview for non-markdown file", ->
|
||||
rootView.open('file.js')
|
||||
editor = rootView.getActiveView()
|
||||
expect(rootView.find('.markdown-preview')).not.toExist()
|
||||
editor.trigger('markdown-preview:toggle')
|
||||
expect(rootView.find('.markdown-preview')).not.toExist()
|
||||
expect(MarkdownPreview.prototype.loadHtml).not.toHaveBeenCalled()
|
||||
preview = pane2.activeItem
|
||||
expect(preview).toBeInstanceOf(MarkdownPreviewView)
|
||||
expect(preview.buffer).toBe rootView.getActivePaneItem().buffer
|
||||
expect(pane1).toMatchSelector(':has(:focus)')
|
||||
|
||||
describe "core:cancel event", ->
|
||||
it "removes markdown preview", ->
|
||||
rootView.open('file.md')
|
||||
editor = rootView.getActiveView()
|
||||
expect(rootView.find('.markdown-preview')).not.toExist()
|
||||
editor.trigger('markdown-preview:toggle')
|
||||
describe "when there is only one pane", ->
|
||||
it "splits the current pane to the right with a markdown preview for the current buffer", ->
|
||||
expect(rootView.getPanes()).toHaveLength 1
|
||||
|
||||
markdownPreviewView = rootView.find('.markdown-preview')?.view()
|
||||
expect(markdownPreviewView).toExist()
|
||||
markdownPreviewView.trigger('core:cancel')
|
||||
expect(rootView.find('.markdown-preview')).not.toExist()
|
||||
rootView.getActiveView().trigger 'markdown-preview:show'
|
||||
|
||||
describe "when the editor receives focus", ->
|
||||
it "removes the markdown preview view", ->
|
||||
rootView.attachToDom()
|
||||
rootView.open('file.md')
|
||||
editor = rootView.getActiveView()
|
||||
expect(rootView.find('.markdown-preview')).not.toExist()
|
||||
editor.trigger('markdown-preview:toggle')
|
||||
expect(rootView.getPanes()).toHaveLength 2
|
||||
[pane1, pane2] = rootView.getPanes()
|
||||
|
||||
markdownPreviewView = rootView.find('.markdown-preview')
|
||||
editor.focus()
|
||||
expect(markdownPreviewView).toExist()
|
||||
expect(rootView.find('.markdown-preview')).not.toExist()
|
||||
expect(pane2.items).toHaveLength 1
|
||||
preview = pane2.activeItem
|
||||
expect(preview).toBeInstanceOf(MarkdownPreviewView)
|
||||
expect(preview.buffer).toBe rootView.getActivePaneItem().buffer
|
||||
expect(pane1).toMatchSelector(':has(:focus)')
|
||||
|
||||
describe "when no editor is open", ->
|
||||
it "does not attach", ->
|
||||
expect(rootView.getActiveView()).toBeFalsy()
|
||||
rootView.trigger('markdown-preview:toggle')
|
||||
expect(rootView.find('.markdown-preview')).not.toExist()
|
||||
describe "when a preview item has already been created for the edit session's uri", ->
|
||||
it "updates and shows the existing preview item if it isn't displayed", ->
|
||||
rootView.getActiveView().trigger 'markdown-preview:show'
|
||||
[pane1, pane2] = rootView.getPanes()
|
||||
pane2.focus()
|
||||
expect(rootView.getActivePane()).toBe pane2
|
||||
preview = pane2.activeItem
|
||||
expect(preview).toBeInstanceOf(MarkdownPreviewView)
|
||||
rootView.open()
|
||||
expect(pane2.activeItem).not.toBe preview
|
||||
pane1.focus()
|
||||
|
||||
preview.fetchRenderedMarkdown.reset()
|
||||
rootView.getActiveView().trigger 'markdown-preview:show'
|
||||
expect(preview.fetchRenderedMarkdown).toHaveBeenCalled()
|
||||
expect(rootView.getPanes()).toHaveLength 2
|
||||
expect(pane2.getItems()).toHaveLength 2
|
||||
expect(pane2.activeItem).toBe preview
|
||||
expect(pane1).toMatchSelector(':has(:focus)')
|
||||
|
@ -0,0 +1,39 @@
|
||||
MarkdownPreviewView = require 'markdown-preview/lib/markdown-preview-view'
|
||||
$ = require 'jquery'
|
||||
{$$$} = require 'space-pen'
|
||||
|
||||
describe "MarkdownPreviewView", ->
|
||||
[buffer, preview] = []
|
||||
|
||||
beforeEach ->
|
||||
spyOn($, 'ajax')
|
||||
project.setPath(project.resolve('markdown'))
|
||||
buffer = project.bufferForPath('file.markdown')
|
||||
preview = new MarkdownPreviewView(buffer)
|
||||
|
||||
afterEach ->
|
||||
buffer.release()
|
||||
|
||||
describe "on construction", ->
|
||||
ajaxArgs = null
|
||||
|
||||
beforeEach ->
|
||||
ajaxArgs = $.ajax.argsForCall[0][0]
|
||||
|
||||
it "shows a loading spinner and fetches the rendered markdown", ->
|
||||
expect(preview.find('.markdown-spinner')).toExist()
|
||||
expect($.ajax).toHaveBeenCalled()
|
||||
|
||||
expect(JSON.parse(ajaxArgs.data).text).toBe buffer.getText()
|
||||
|
||||
ajaxArgs.success($$$ -> @div "WWII", class: 'private-ryan')
|
||||
expect(preview.find(".private-ryan")).toExist()
|
||||
|
||||
it "shows an error message on error", ->
|
||||
ajaxArgs.error()
|
||||
expect(preview.text()).toContain "Failed"
|
||||
|
||||
describe "serialization", ->
|
||||
it "reassociates with the same buffer when deserialized", ->
|
||||
newPreview = deserialize(preview.serialize())
|
||||
expect(newPreview.buffer).toBe buffer
|
@ -1,438 +0,0 @@
|
||||
.markdown-preview {
|
||||
font-family: "Helvetica Neue", Helvetica, sans-serif;
|
||||
font-size: 14px;
|
||||
line-height: 1.6;
|
||||
position: absolute;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
top: 0px;
|
||||
left: 0px;
|
||||
background-color: #fff;
|
||||
overflow: auto;
|
||||
z-index: 3;
|
||||
box-sizing: border-box;
|
||||
padding: 20px;
|
||||
}
|
||||
|
||||
.markdown-body {
|
||||
min-width: 680px;
|
||||
}
|
||||
|
||||
.markdown-body pre,
|
||||
.markdown-body code,
|
||||
.markdown-body tt {
|
||||
font-size: 12px;
|
||||
font-family: Consolas, "Liberation Mono", Courier, monospace;
|
||||
}
|
||||
|
||||
.markdown-body a {
|
||||
color: #4183c4;
|
||||
}
|
||||
|
||||
.markdown-body ol > li {
|
||||
list-style-type: decimal;
|
||||
}
|
||||
|
||||
.markdown-body ul > li {
|
||||
list-style-type: disc;
|
||||
}
|
||||
|
||||
.markdown-spinner {
|
||||
margin: auto;
|
||||
background-image: url(images/octocat-spinner-128.gif);
|
||||
background-repeat: no-repeat;
|
||||
background-size: 64px;
|
||||
background-position: top center;
|
||||
padding-top: 70px;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
|
||||
/* this code below was copied from https://github.com/assets/stylesheets/primer/components/markdown.css */
|
||||
/* we really need to get primer in here somehow. */
|
||||
.markdown-body {
|
||||
font-size: 14px;
|
||||
line-height: 1.6;
|
||||
overflow: hidden; }
|
||||
.markdown-body > *:first-child {
|
||||
margin-top: 0 !important; }
|
||||
.markdown-body > *:last-child {
|
||||
margin-bottom: 0 !important; }
|
||||
.markdown-body a.absent {
|
||||
color: #c00; }
|
||||
.markdown-body a.anchor {
|
||||
display: block;
|
||||
padding-left: 30px;
|
||||
margin-left: -30px;
|
||||
cursor: pointer;
|
||||
position: absolute;
|
||||
top: 0;
|
||||
left: 0;
|
||||
bottom: 0; }
|
||||
.markdown-body h1, .markdown-body h2, .markdown-body h3, .markdown-body h4, .markdown-body h5, .markdown-body h6 {
|
||||
margin: 20px 0 10px;
|
||||
padding: 0;
|
||||
font-weight: bold;
|
||||
-webkit-font-smoothing: antialiased;
|
||||
cursor: text;
|
||||
position: relative; }
|
||||
.markdown-body h1 .mini-icon-link, .markdown-body h2 .mini-icon-link, .markdown-body h3 .mini-icon-link, .markdown-body h4 .mini-icon-link, .markdown-body h5 .mini-icon-link, .markdown-body h6 .mini-icon-link {
|
||||
display: none;
|
||||
color: #000; }
|
||||
.markdown-body h1:hover a.anchor, .markdown-body h2:hover a.anchor, .markdown-body h3:hover a.anchor, .markdown-body h4:hover a.anchor, .markdown-body h5:hover a.anchor, .markdown-body h6:hover a.anchor {
|
||||
text-decoration: none;
|
||||
line-height: 1;
|
||||
padding-left: 0;
|
||||
margin-left: -22px;
|
||||
top: 15%; }
|
||||
.markdown-body h1:hover a.anchor .mini-icon-link, .markdown-body h2:hover a.anchor .mini-icon-link, .markdown-body h3:hover a.anchor .mini-icon-link, .markdown-body h4:hover a.anchor .mini-icon-link, .markdown-body h5:hover a.anchor .mini-icon-link, .markdown-body h6:hover a.anchor .mini-icon-link {
|
||||
display: inline-block; }
|
||||
.markdown-body h1 tt, .markdown-body h1 code, .markdown-body h2 tt, .markdown-body h2 code, .markdown-body h3 tt, .markdown-body h3 code, .markdown-body h4 tt, .markdown-body h4 code, .markdown-body h5 tt, .markdown-body h5 code, .markdown-body h6 tt, .markdown-body h6 code {
|
||||
font-size: inherit; }
|
||||
.markdown-body h1 {
|
||||
font-size: 28px;
|
||||
color: #000; }
|
||||
.markdown-body h2 {
|
||||
font-size: 24px;
|
||||
border-bottom: 1px solid #ccc;
|
||||
color: #000; }
|
||||
.markdown-body h3 {
|
||||
font-size: 18px; }
|
||||
.markdown-body h4 {
|
||||
font-size: 16px; }
|
||||
.markdown-body h5 {
|
||||
font-size: 14px; }
|
||||
.markdown-body h6 {
|
||||
color: #777;
|
||||
font-size: 14px; }
|
||||
.markdown-body p,
|
||||
.markdown-body blockquote,
|
||||
.markdown-body ul, .markdown-body ol, .markdown-body dl,
|
||||
.markdown-body table,
|
||||
.markdown-body pre {
|
||||
margin: 15px 0; }
|
||||
.markdown-body hr {
|
||||
background: transparent url("https://a248.e.akamai.net/assets.github.com/assets/primer/markdown/dirty-shade-0e7d81b119cc9beae17b0c98093d121fa0050a74.png") repeat-x 0 0;
|
||||
border: 0 none;
|
||||
color: #ccc;
|
||||
height: 4px;
|
||||
padding: 0; }
|
||||
.markdown-body > h2:first-child, .markdown-body > h1:first-child, .markdown-body > h1:first-child + h2, .markdown-body > h3:first-child, .markdown-body > h4:first-child, .markdown-body > h5:first-child, .markdown-body > h6:first-child {
|
||||
margin-top: 0;
|
||||
padding-top: 0; }
|
||||
.markdown-body a:first-child h1, .markdown-body a:first-child h2, .markdown-body a:first-child h3, .markdown-body a:first-child h4, .markdown-body a:first-child h5, .markdown-body a:first-child h6 {
|
||||
margin-top: 0;
|
||||
padding-top: 0; }
|
||||
.markdown-body h1 + p,
|
||||
.markdown-body h2 + p,
|
||||
.markdown-body h3 + p,
|
||||
.markdown-body h4 + p,
|
||||
.markdown-body h5 + p,
|
||||
.markdown-body h6 + p {
|
||||
margin-top: 0; }
|
||||
.markdown-body li p.first {
|
||||
display: inline-block; }
|
||||
.markdown-body ul, .markdown-body ol {
|
||||
padding-left: 30px; }
|
||||
.markdown-body ul.no-list, .markdown-body ol.no-list {
|
||||
list-style-type: none;
|
||||
padding: 0; }
|
||||
.markdown-body ul li > :first-child,
|
||||
.markdown-body ul li ul:first-of-type, .markdown-body ol li > :first-child,
|
||||
.markdown-body ol li ul:first-of-type {
|
||||
margin-top: 0px; }
|
||||
.markdown-body ul ul,
|
||||
.markdown-body ul ol,
|
||||
.markdown-body ol ol,
|
||||
.markdown-body ol ul {
|
||||
margin-bottom: 0; }
|
||||
.markdown-body dl {
|
||||
padding: 0; }
|
||||
.markdown-body dl dt {
|
||||
font-size: 14px;
|
||||
font-weight: bold;
|
||||
font-style: italic;
|
||||
padding: 0;
|
||||
margin: 15px 0 5px; }
|
||||
.markdown-body dl dt:first-child {
|
||||
padding: 0; }
|
||||
.markdown-body dl dt > :first-child {
|
||||
margin-top: 0px; }
|
||||
.markdown-body dl dt > :last-child {
|
||||
margin-bottom: 0px; }
|
||||
.markdown-body dl dd {
|
||||
margin: 0 0 15px;
|
||||
padding: 0 15px; }
|
||||
.markdown-body dl dd > :first-child {
|
||||
margin-top: 0px; }
|
||||
.markdown-body dl dd > :last-child {
|
||||
margin-bottom: 0px; }
|
||||
.markdown-body blockquote {
|
||||
border-left: 4px solid #DDD;
|
||||
padding: 0 15px;
|
||||
color: #777; }
|
||||
.markdown-body blockquote > :first-child {
|
||||
margin-top: 0px; }
|
||||
.markdown-body blockquote > :last-child {
|
||||
margin-bottom: 0px; }
|
||||
.markdown-body table th {
|
||||
font-weight: bold; }
|
||||
.markdown-body table th, .markdown-body table td {
|
||||
border: 1px solid #ccc;
|
||||
padding: 6px 13px; }
|
||||
.markdown-body table tr {
|
||||
border-top: 1px solid #ccc;
|
||||
background-color: #fff; }
|
||||
.markdown-body table tr:nth-child(2n) {
|
||||
background-color: #f8f8f8; }
|
||||
.markdown-body img {
|
||||
max-width: 100%;
|
||||
-moz-box-sizing: border-box;
|
||||
box-sizing: border-box; }
|
||||
.markdown-body span.frame {
|
||||
display: block;
|
||||
overflow: hidden; }
|
||||
.markdown-body span.frame > span {
|
||||
border: 1px solid #ddd;
|
||||
display: block;
|
||||
float: left;
|
||||
overflow: hidden;
|
||||
margin: 13px 0 0;
|
||||
padding: 7px;
|
||||
width: auto; }
|
||||
.markdown-body span.frame span img {
|
||||
display: block;
|
||||
float: left; }
|
||||
.markdown-body span.frame span span {
|
||||
clear: both;
|
||||
color: #333;
|
||||
display: block;
|
||||
padding: 5px 0 0; }
|
||||
.markdown-body span.align-center {
|
||||
display: block;
|
||||
overflow: hidden;
|
||||
clear: both; }
|
||||
.markdown-body span.align-center > span {
|
||||
display: block;
|
||||
overflow: hidden;
|
||||
margin: 13px auto 0;
|
||||
text-align: center; }
|
||||
.markdown-body span.align-center span img {
|
||||
margin: 0 auto;
|
||||
text-align: center; }
|
||||
.markdown-body span.align-right {
|
||||
display: block;
|
||||
overflow: hidden;
|
||||
clear: both; }
|
||||
.markdown-body span.align-right > span {
|
||||
display: block;
|
||||
overflow: hidden;
|
||||
margin: 13px 0 0;
|
||||
text-align: right; }
|
||||
.markdown-body span.align-right span img {
|
||||
margin: 0;
|
||||
text-align: right; }
|
||||
.markdown-body span.float-left {
|
||||
display: block;
|
||||
margin-right: 13px;
|
||||
overflow: hidden;
|
||||
float: left; }
|
||||
.markdown-body span.float-left span {
|
||||
margin: 13px 0 0; }
|
||||
.markdown-body span.float-right {
|
||||
display: block;
|
||||
margin-left: 13px;
|
||||
overflow: hidden;
|
||||
float: right; }
|
||||
.markdown-body span.float-right > span {
|
||||
display: block;
|
||||
overflow: hidden;
|
||||
margin: 13px auto 0;
|
||||
text-align: right; }
|
||||
.markdown-body code, .markdown-body tt {
|
||||
margin: 0 2px;
|
||||
padding: 0px 5px;
|
||||
border: 1px solid #eaeaea;
|
||||
background-color: #f8f8f8;
|
||||
border-radius: 3px; }
|
||||
.markdown-body code {
|
||||
white-space: nowrap; }
|
||||
.markdown-body pre > code {
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
white-space: pre;
|
||||
border: none;
|
||||
background: transparent; }
|
||||
.markdown-body .highlight pre, .markdown-body pre {
|
||||
background-color: #f8f8f8;
|
||||
border: 1px solid #ccc;
|
||||
font-size: 13px;
|
||||
line-height: 19px;
|
||||
overflow: auto;
|
||||
padding: 6px 10px;
|
||||
border-radius: 3px; }
|
||||
.markdown-body pre code, .markdown-body pre tt {
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
background-color: transparent;
|
||||
border: none; }
|
||||
|
||||
/* this code was copied from https://github.com/assets/stylesheets/primer/components/pygments.css */
|
||||
/* the .markdown-body class was then added to all rules */
|
||||
.markdown-body .highlight {
|
||||
background: #ffffff; }
|
||||
.markdown-body .highlight .c {
|
||||
color: #999988;
|
||||
font-style: italic; }
|
||||
.markdown-body .highlight .err {
|
||||
color: #a61717;
|
||||
background-color: #e3d2d2; }
|
||||
.markdown-body .highlight .k {
|
||||
font-weight: bold; }
|
||||
.markdown-body .highlight .o {
|
||||
font-weight: bold; }
|
||||
.markdown-body .highlight .cm {
|
||||
color: #999988;
|
||||
font-style: italic; }
|
||||
.markdown-body .highlight .cp {
|
||||
color: #999999;
|
||||
font-weight: bold; }
|
||||
.markdown-body .highlight .c1 {
|
||||
color: #999988;
|
||||
font-style: italic; }
|
||||
.markdown-body .highlight .cs {
|
||||
color: #999999;
|
||||
font-weight: bold;
|
||||
font-style: italic; }
|
||||
.markdown-body .highlight .gd {
|
||||
color: #000000;
|
||||
background-color: #ffdddd; }
|
||||
.markdown-body .highlight .gd .x {
|
||||
color: #000000;
|
||||
background-color: #ffaaaa; }
|
||||
.markdown-body .highlight .ge {
|
||||
font-style: italic; }
|
||||
.markdown-body .highlight .gr {
|
||||
color: #aa0000; }
|
||||
.markdown-body .highlight .gh {
|
||||
color: #999999; }
|
||||
.markdown-body .highlight .gi {
|
||||
color: #000000;
|
||||
background-color: #ddffdd; }
|
||||
.markdown-body .highlight .gi .x {
|
||||
color: #000000;
|
||||
background-color: #aaffaa; }
|
||||
.markdown-body .highlight .go {
|
||||
color: #888888; }
|
||||
.markdown-body .highlight .gp {
|
||||
color: #555555; }
|
||||
.markdown-body .highlight .gs {
|
||||
font-weight: bold; }
|
||||
.markdown-body .highlight .gu {
|
||||
color: #800080;
|
||||
font-weight: bold; }
|
||||
.markdown-body .highlight .gt {
|
||||
color: #aa0000; }
|
||||
.markdown-body .highlight .kc {
|
||||
font-weight: bold; }
|
||||
.markdown-body .highlight .kd {
|
||||
font-weight: bold; }
|
||||
.markdown-body .highlight .kn {
|
||||
font-weight: bold; }
|
||||
.markdown-body .highlight .kp {
|
||||
font-weight: bold; }
|
||||
.markdown-body .highlight .kr {
|
||||
font-weight: bold; }
|
||||
.markdown-body .highlight .kt {
|
||||
color: #445588;
|
||||
font-weight: bold; }
|
||||
.markdown-body .highlight .m {
|
||||
color: #009999; }
|
||||
.markdown-body .highlight .s {
|
||||
color: #d14; }
|
||||
.markdown-body .highlight .na {
|
||||
color: #008080; }
|
||||
.markdown-body .highlight .nb {
|
||||
color: #0086B3; }
|
||||
.markdown-body .highlight .nc {
|
||||
color: #445588;
|
||||
font-weight: bold; }
|
||||
.markdown-body .highlight .no {
|
||||
color: #008080; }
|
||||
.markdown-body .highlight .ni {
|
||||
color: #800080; }
|
||||
.markdown-body .highlight .ne {
|
||||
color: #990000;
|
||||
font-weight: bold; }
|
||||
.markdown-body .highlight .nf {
|
||||
color: #990000;
|
||||
font-weight: bold; }
|
||||
.markdown-body .highlight .nn {
|
||||
color: #555555; }
|
||||
.markdown-body .highlight .nt {
|
||||
color: #000080; }
|
||||
.markdown-body .highlight .nv {
|
||||
color: #008080; }
|
||||
.markdown-body .highlight .ow {
|
||||
font-weight: bold; }
|
||||
.markdown-body .highlight .w {
|
||||
color: #bbbbbb; }
|
||||
.markdown-body .highlight .mf {
|
||||
color: #009999; }
|
||||
.markdown-body .highlight .mh {
|
||||
color: #009999; }
|
||||
.markdown-body .highlight .mi {
|
||||
color: #009999; }
|
||||
.markdown-body .highlight .mo {
|
||||
color: #009999; }
|
||||
.markdown-body .highlight .sb {
|
||||
color: #d14; }
|
||||
.markdown-body .highlight .sc {
|
||||
color: #d14; }
|
||||
.markdown-body .highlight .sd {
|
||||
color: #d14; }
|
||||
.markdown-body .highlight .s2 {
|
||||
color: #d14; }
|
||||
.markdown-body .highlight .se {
|
||||
color: #d14; }
|
||||
.markdown-body .highlight .sh {
|
||||
color: #d14; }
|
||||
.markdown-body .highlight .si {
|
||||
color: #d14; }
|
||||
.markdown-body .highlight .sx {
|
||||
color: #d14; }
|
||||
.markdown-body .highlight .sr {
|
||||
color: #009926; }
|
||||
.markdown-body .highlight .s1 {
|
||||
color: #d14; }
|
||||
.markdown-body .highlight .ss {
|
||||
color: #990073; }
|
||||
.markdown-body .highlight .bp {
|
||||
color: #999999; }
|
||||
.markdown-body .highlight .vc {
|
||||
color: #008080; }
|
||||
.markdown-body .highlight .vg {
|
||||
color: #008080; }
|
||||
.markdown-body .highlight .vi {
|
||||
color: #008080; }
|
||||
.markdown-body .highlight .il {
|
||||
color: #009999; }
|
||||
.markdown-body .highlight .gc {
|
||||
color: #999;
|
||||
background-color: #EAF2F5; }
|
||||
|
||||
.type-csharp .markdown-body .highlight .k {
|
||||
color: #0000FF; }
|
||||
.type-csharp .markdown-body .highlight .kt {
|
||||
color: #0000FF; }
|
||||
.type-csharp .markdown-body .highlight .nf {
|
||||
color: #000000;
|
||||
font-weight: normal; }
|
||||
.type-csharp .markdown-body .highlight .nc {
|
||||
color: #2B91AF; }
|
||||
.type-csharp .markdown-body .highlight .nn {
|
||||
color: #000000; }
|
||||
.type-csharp .markdown-body .highlight .s {
|
||||
color: #A31515; }
|
||||
.type-csharp .markdown-body .highlight .sc {
|
||||
color: #A31515; }
|
403
src/packages/markdown-preview/stylesheets/markdown-preview.less
Normal file
403
src/packages/markdown-preview/stylesheets/markdown-preview.less
Normal file
@ -0,0 +1,403 @@
|
||||
.markdown-preview {
|
||||
font-family: "Helvetica Neue", Helvetica, sans-serif;
|
||||
font-size: 14px;
|
||||
line-height: 1.6;
|
||||
background-color: #fff;
|
||||
overflow: scroll;
|
||||
box-sizing: border-box;
|
||||
padding: 20px;
|
||||
}
|
||||
|
||||
.markdown-spinner {
|
||||
margin: auto;
|
||||
background-image: url(images/octocat-spinner-128.gif);
|
||||
background-repeat: no-repeat;
|
||||
background-size: 64px;
|
||||
background-position: top center;
|
||||
padding-top: 70px;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
// This is styling for generic markdownized text. Anything you put in a
|
||||
// container with .markdown-preview on it should render generally well. It also
|
||||
// includes some GitHub Flavored Markdown specific styling (like @mentions)
|
||||
.markdown-preview {
|
||||
pre,
|
||||
code,
|
||||
tt {
|
||||
font-size: 12px;
|
||||
font-family: Consolas, "Liberation Mono", Courier, monospace;
|
||||
}
|
||||
|
||||
a {
|
||||
color: #4183c4;
|
||||
}
|
||||
|
||||
ol > li {
|
||||
list-style-type: decimal;
|
||||
}
|
||||
|
||||
ul > li {
|
||||
list-style-type: disc;
|
||||
}
|
||||
|
||||
& > *:first-child {
|
||||
margin-top: 0 !important;
|
||||
}
|
||||
|
||||
& > *:last-child {
|
||||
margin-bottom: 0 !important;
|
||||
}
|
||||
|
||||
// Link Colors
|
||||
a.absent {
|
||||
color: #c00;
|
||||
}
|
||||
|
||||
a.anchor {
|
||||
display: block;
|
||||
padding-left: 30px;
|
||||
margin-left: -30px;
|
||||
cursor: pointer;
|
||||
position: absolute;
|
||||
top: 0;
|
||||
left: 0;
|
||||
bottom: 0;
|
||||
}
|
||||
|
||||
// Headings
|
||||
h1, h2, h3, h4, h5, h6 {
|
||||
margin: 20px 0 10px;
|
||||
padding: 0;
|
||||
font-weight: bold;
|
||||
-webkit-font-smoothing: antialiased;
|
||||
cursor: text;
|
||||
position: relative;
|
||||
|
||||
.mini-icon-link {
|
||||
display: none;
|
||||
color: #000;
|
||||
}
|
||||
|
||||
&:hover a.anchor {
|
||||
text-decoration: none;
|
||||
line-height: 1;
|
||||
padding-left: 0;
|
||||
margin-left: -22px;
|
||||
top: 15%;
|
||||
|
||||
.mini-icon-link {
|
||||
display: inline-block;
|
||||
}
|
||||
}
|
||||
tt, code {
|
||||
font-size: inherit;
|
||||
}
|
||||
}
|
||||
|
||||
h1 {
|
||||
font-size: 28px;
|
||||
color: #000;
|
||||
}
|
||||
|
||||
h2 {
|
||||
font-size: 24px;
|
||||
border-bottom: 1px solid #ccc;
|
||||
color: #000;
|
||||
}
|
||||
|
||||
h3 {
|
||||
font-size: 18px;
|
||||
}
|
||||
|
||||
h4 {
|
||||
font-size: 16px;
|
||||
}
|
||||
|
||||
h5 {
|
||||
font-size: 14px;
|
||||
}
|
||||
|
||||
h6 {
|
||||
color: #777;
|
||||
font-size: 14px;
|
||||
}
|
||||
|
||||
p,
|
||||
blockquote,
|
||||
ul, ol, dl,
|
||||
table,
|
||||
pre {
|
||||
margin: 15px 0;
|
||||
}
|
||||
|
||||
hr {
|
||||
background: transparent;
|
||||
border: 0 none;
|
||||
color: #ccc;
|
||||
height: 4px;
|
||||
padding: 0;
|
||||
}
|
||||
|
||||
& > h2:first-child,
|
||||
& > h1:first-child,
|
||||
& > h1:first-child + h2,
|
||||
& > h3:first-child,
|
||||
& > h4:first-child,
|
||||
& > h5:first-child,
|
||||
& > h6:first-child {
|
||||
margin-top: 0;
|
||||
padding-top: 0;
|
||||
}
|
||||
|
||||
// fixes margin on shit like:
|
||||
// <a name='the-heading'>
|
||||
// <h1>The Heading</h1></a>
|
||||
a:first-child {
|
||||
h1, h2, h3, h4, h5, h6 {
|
||||
margin-top: 0;
|
||||
padding-top: 0;
|
||||
}
|
||||
}
|
||||
|
||||
h1 + p,
|
||||
h2 + p,
|
||||
h3 + p,
|
||||
h4 + p,
|
||||
h5 + p,
|
||||
h6 + p {
|
||||
margin-top: 0;
|
||||
}
|
||||
|
||||
// ReST first graf in nested list
|
||||
li p.first {
|
||||
display: inline-block;
|
||||
}
|
||||
|
||||
// Lists, Blockquotes & Such
|
||||
ul, ol {
|
||||
padding-left: 30px;
|
||||
|
||||
&.no-list {
|
||||
list-style-type: none;
|
||||
padding: 0;
|
||||
}
|
||||
|
||||
li > :first-child,
|
||||
li ul:first-of-type {
|
||||
margin-top: 0px;
|
||||
}
|
||||
}
|
||||
|
||||
ul ul,
|
||||
ul ol,
|
||||
ol ol,
|
||||
ol ul {
|
||||
margin-bottom: 0;
|
||||
}
|
||||
|
||||
dl {
|
||||
padding: 0;
|
||||
}
|
||||
|
||||
dl dt {
|
||||
font-size: 14px;
|
||||
font-weight: bold;
|
||||
font-style: italic;
|
||||
padding: 0;
|
||||
margin: 15px 0 5px;
|
||||
|
||||
&:first-child {
|
||||
padding: 0;
|
||||
}
|
||||
& > :first-child {
|
||||
margin-top: 0px;
|
||||
}
|
||||
|
||||
& > :last-child {
|
||||
margin-bottom: 0px;
|
||||
}
|
||||
}
|
||||
|
||||
dl dd {
|
||||
margin: 0 0 15px;
|
||||
padding: 0 15px;
|
||||
& > :first-child {
|
||||
margin-top: 0px;
|
||||
}
|
||||
|
||||
& > :last-child {
|
||||
margin-bottom: 0px;
|
||||
}
|
||||
}
|
||||
|
||||
blockquote {
|
||||
border-left: 4px solid #DDD;
|
||||
padding: 0 15px;
|
||||
color: #777;
|
||||
|
||||
& > :first-child {
|
||||
margin-top: 0px;
|
||||
}
|
||||
|
||||
& > :last-child {
|
||||
margin-bottom: 0px;
|
||||
}
|
||||
}
|
||||
|
||||
// Tables
|
||||
table {
|
||||
|
||||
th {
|
||||
font-weight: bold;
|
||||
}
|
||||
|
||||
th, td {
|
||||
border: 1px solid #ccc;
|
||||
padding: 6px 13px;
|
||||
}
|
||||
|
||||
tr {
|
||||
border-top: 1px solid #ccc;
|
||||
background-color: #fff;
|
||||
|
||||
&:nth-child(2n) {
|
||||
background-color: #f8f8f8;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Images & Stuff
|
||||
img {
|
||||
max-width: 100%;
|
||||
@include box-sizing();
|
||||
}
|
||||
|
||||
// Gollum Image Tags
|
||||
|
||||
// Framed
|
||||
span.frame {
|
||||
display: block;
|
||||
overflow: hidden;
|
||||
|
||||
& > span {
|
||||
border: 1px solid #ddd;
|
||||
display: block;
|
||||
float: left;
|
||||
overflow: hidden;
|
||||
margin: 13px 0 0;
|
||||
padding: 7px;
|
||||
width: auto;
|
||||
}
|
||||
|
||||
span img {
|
||||
display: block;
|
||||
float: left;
|
||||
}
|
||||
|
||||
span span {
|
||||
clear: both;
|
||||
color: #333;
|
||||
display: block;
|
||||
padding: 5px 0 0;
|
||||
}
|
||||
}
|
||||
|
||||
span.align-center {
|
||||
display: block;
|
||||
overflow: hidden;
|
||||
clear: both;
|
||||
|
||||
& > span {
|
||||
display: block;
|
||||
overflow: hidden;
|
||||
margin: 13px auto 0;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
span img {
|
||||
margin: 0 auto;
|
||||
text-align: center;
|
||||
}
|
||||
}
|
||||
|
||||
span.align-right {
|
||||
display: block;
|
||||
overflow: hidden;
|
||||
clear: both;
|
||||
|
||||
& > span {
|
||||
display: block;
|
||||
overflow: hidden;
|
||||
margin: 13px 0 0;
|
||||
text-align: right;
|
||||
}
|
||||
|
||||
span img {
|
||||
margin: 0;
|
||||
text-align: right;
|
||||
}
|
||||
}
|
||||
|
||||
span.float-left {
|
||||
display: block;
|
||||
margin-right: 13px;
|
||||
overflow: hidden;
|
||||
float: left;
|
||||
|
||||
span {
|
||||
margin: 13px 0 0;
|
||||
}
|
||||
}
|
||||
|
||||
span.float-right {
|
||||
display: block;
|
||||
margin-left: 13px;
|
||||
overflow: hidden;
|
||||
float: right;
|
||||
|
||||
& > span {
|
||||
display: block;
|
||||
overflow: hidden;
|
||||
margin: 13px auto 0;
|
||||
text-align: right;
|
||||
}
|
||||
}
|
||||
|
||||
// Inline code snippets
|
||||
code, tt {
|
||||
margin: 0 2px;
|
||||
padding: 0px 5px;
|
||||
border: 1px solid #eaeaea;
|
||||
background-color: #f8f8f8;
|
||||
border-radius:3px;
|
||||
}
|
||||
|
||||
code { white-space: nowrap; }
|
||||
|
||||
// Code tags within code blocks (<pre>s)
|
||||
pre > code {
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
white-space: pre;
|
||||
border: none;
|
||||
background: transparent;
|
||||
}
|
||||
|
||||
.highlight pre, pre {
|
||||
background-color: #f8f8f8;
|
||||
border: 1px solid #ccc;
|
||||
font-size: 13px;
|
||||
line-height: 19px;
|
||||
overflow: auto;
|
||||
padding: 6px 10px;
|
||||
border-radius:3px;
|
||||
}
|
||||
|
||||
pre code, pre tt {
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
background-color: transparent;
|
||||
border: none;
|
||||
}
|
||||
}
|
201
src/packages/markdown-preview/stylesheets/pygments.less
Normal file
201
src/packages/markdown-preview/stylesheets/pygments.less
Normal file
@ -0,0 +1,201 @@
|
||||
.highlight {
|
||||
background: #ffffff;
|
||||
|
||||
// Comment
|
||||
.c { color: #999988; font-style: italic }
|
||||
|
||||
// Error
|
||||
.err { color: #a61717; background-color: #e3d2d2 }
|
||||
|
||||
// Keyword
|
||||
.k { font-weight: bold }
|
||||
|
||||
// Operator
|
||||
.o { font-weight: bold }
|
||||
|
||||
// Comment.Multiline
|
||||
.cm { color: #999988; font-style: italic }
|
||||
|
||||
// Comment.Preproc
|
||||
.cp { color: #999999; font-weight: bold }
|
||||
|
||||
// Comment.Single
|
||||
.c1 { color: #999988; font-style: italic }
|
||||
|
||||
// Comment.Special
|
||||
.cs { color: #999999; font-weight: bold; font-style: italic }
|
||||
|
||||
// Generic.Deleted
|
||||
.gd { color: #000000; background-color: #ffdddd }
|
||||
|
||||
// Generic.Deleted.Specific
|
||||
.gd .x { color: #000000; background-color: #ffaaaa }
|
||||
|
||||
// Generic.Emph
|
||||
.ge { font-style: italic }
|
||||
|
||||
// Generic.Error
|
||||
.gr { color: #aa0000 }
|
||||
|
||||
// Generic.Heading
|
||||
.gh { color: #999999 }
|
||||
|
||||
// Generic.Inserted
|
||||
.gi { color: #000000; background-color: #ddffdd }
|
||||
|
||||
// Generic.Inserted.Specific
|
||||
.gi .x { color: #000000; background-color: #aaffaa }
|
||||
|
||||
// Generic.Output
|
||||
.go { color: #888888 }
|
||||
|
||||
// Generic.Prompt
|
||||
.gp { color: #555555 }
|
||||
|
||||
// Generic.Strong
|
||||
.gs { font-weight: bold }
|
||||
|
||||
// Generic.Subheading
|
||||
.gu { color: #800080; font-weight: bold; }
|
||||
|
||||
// Generic.Traceback
|
||||
.gt { color: #aa0000 }
|
||||
|
||||
// Keyword.Constant
|
||||
.kc { font-weight: bold }
|
||||
|
||||
// Keyword.Declaration
|
||||
.kd { font-weight: bold }
|
||||
|
||||
// Keyword.Namespace
|
||||
.kn { font-weight: bold }
|
||||
|
||||
// Keyword.Pseudo
|
||||
.kp { font-weight: bold }
|
||||
|
||||
// Keyword.Reserved
|
||||
.kr { font-weight: bold }
|
||||
|
||||
// Keyword.Type
|
||||
.kt { color: #445588; font-weight: bold }
|
||||
|
||||
// Literal.Number
|
||||
.m { color: #009999 }
|
||||
|
||||
// Literal.String
|
||||
.s { color: #d14 }
|
||||
|
||||
// Name
|
||||
.n { color: #333333 }
|
||||
|
||||
// Name.Attribute
|
||||
.na { color: #008080 }
|
||||
|
||||
// Name.Builtin
|
||||
.nb { color: #0086B3 }
|
||||
|
||||
// Name.Class
|
||||
.nc { color: #445588; font-weight: bold }
|
||||
|
||||
// Name.Constant
|
||||
.no { color: #008080 }
|
||||
|
||||
// Name.Entity
|
||||
.ni { color: #800080 }
|
||||
|
||||
// Name.Exception
|
||||
.ne { color: #990000; font-weight: bold }
|
||||
|
||||
// Name.Function
|
||||
.nf { color: #990000; font-weight: bold }
|
||||
|
||||
// Name.Namespace
|
||||
.nn { color: #555555 }
|
||||
|
||||
// Name.Tag
|
||||
.nt { color: #000080 }
|
||||
|
||||
// Name.Variable
|
||||
.nv { color: #008080 }
|
||||
|
||||
// Operator.Word
|
||||
.ow { font-weight: bold }
|
||||
|
||||
// Text.Whitespace
|
||||
.w { color: #bbbbbb }
|
||||
|
||||
// Literal.Number.Float
|
||||
.mf { color: #009999 }
|
||||
|
||||
// Literal.Number.Hex
|
||||
.mh { color: #009999 }
|
||||
|
||||
// Literal.Number.Integer
|
||||
.mi { color: #009999 }
|
||||
|
||||
// Literal.Number.Oct
|
||||
.mo { color: #009999 }
|
||||
|
||||
// Literal.String.Backtick
|
||||
.sb { color: #d14 }
|
||||
|
||||
// Literal.String.Char
|
||||
.sc { color: #d14 }
|
||||
|
||||
// Literal.String.Doc
|
||||
.sd { color: #d14 }
|
||||
|
||||
// Literal.String.Double
|
||||
.s2 { color: #d14 }
|
||||
|
||||
// Literal.String.Escape
|
||||
.se { color: #d14 }
|
||||
|
||||
// Literal.String.Heredoc
|
||||
.sh { color: #d14 }
|
||||
|
||||
// Literal.String.Interpol
|
||||
.si { color: #d14 }
|
||||
|
||||
// Literal.String.Other
|
||||
.sx { color: #d14 }
|
||||
|
||||
// Literal.String.Regex
|
||||
.sr { color: #009926 }
|
||||
|
||||
// Literal.String.Single
|
||||
.s1 { color: #d14 }
|
||||
|
||||
// Literal.String.Symbol
|
||||
.ss { color: #990073 }
|
||||
|
||||
// Name.Builtin.Pseudo
|
||||
.bp { color: #999999 }
|
||||
|
||||
// Name.Variable.Class
|
||||
.vc { color: #008080 }
|
||||
|
||||
// Name.Variable.Global
|
||||
.vg { color: #008080 }
|
||||
|
||||
// Name.Variable.Instance
|
||||
.vi { color: #008080 }
|
||||
|
||||
// Literal.Number.Integer.Long
|
||||
.il { color: #009999 }
|
||||
|
||||
.gc {
|
||||
color: #999;
|
||||
background-color: #EAF2F5;
|
||||
}
|
||||
}
|
||||
|
||||
.type-csharp .highlight {
|
||||
.k { color: #0000FF }
|
||||
.kt { color: #0000FF }
|
||||
.nf { color: #000000; font-weight: normal }
|
||||
.nc { color: #2B91AF }
|
||||
.nn { color: #000000 }
|
||||
.s { color: #A31515 }
|
||||
.sc { color: #A31515 }
|
||||
}
|
@ -62,6 +62,7 @@ class PackageGeneratorView extends View
|
||||
for path in fs.listTree(templatePath)
|
||||
relativePath = path.replace(templatePath, "")
|
||||
relativePath = relativePath.replace(/^\//, '')
|
||||
relativePath = relativePath.replace(/\.template$/, '')
|
||||
relativePath = @replacePackageNamePlaceholders(relativePath, packageName)
|
||||
|
||||
sourcePath = fs.join(@getPackagePath(), relativePath)
|
||||
|
@ -37,12 +37,6 @@ describe 'Package Generator', ->
|
||||
packagePath = "/tmp/atom-packages/#{packageName}"
|
||||
fs.remove(packagePath) if fs.exists(packagePath)
|
||||
|
||||
@addMatchers
|
||||
toExistOnDisk: (expected) ->
|
||||
notText = this.isNot and " not" or ""
|
||||
@message = -> return "Expected path '" + @actual + "'" + notText + " to exist."
|
||||
fs.exists(@actual)
|
||||
|
||||
afterEach ->
|
||||
fs.remove(packagePath) if fs.exists(packagePath)
|
||||
|
||||
|
@ -7,7 +7,7 @@ describe "Spell check", ->
|
||||
window.rootView = new RootView
|
||||
rootView.open('sample.js')
|
||||
config.set('spell-check.grammars', [])
|
||||
window.loadPackage('spell-check')
|
||||
window.loadPackage('spell-check', activateImmediately: true)
|
||||
rootView.attachToDom()
|
||||
editor = rootView.getActiveView()
|
||||
|
||||
|
@ -127,8 +127,10 @@ class TreeView extends ScrollView
|
||||
@root = null
|
||||
|
||||
selectActiveFile: ->
|
||||
activeFilePath = rootView.getActiveView()?.getPath()
|
||||
@selectEntryForPath(activeFilePath) if activeFilePath
|
||||
if activeFilePath = rootView.getActiveView()?.getPath?()
|
||||
@selectEntryForPath(activeFilePath)
|
||||
else
|
||||
@deselect()
|
||||
|
||||
revealActiveFile: ->
|
||||
@attach()
|
||||
@ -298,9 +300,12 @@ class TreeView extends ScrollView
|
||||
return false unless entry.get(0)
|
||||
entry = entry.view() unless entry instanceof View
|
||||
@selectedPath = entry.getPath()
|
||||
@treeViewList.find('.selected').removeClass('selected')
|
||||
@deselect()
|
||||
entry.addClass('selected')
|
||||
|
||||
deselect: ->
|
||||
@treeViewList.find('.selected').removeClass('selected')
|
||||
|
||||
scrollTop: (top) ->
|
||||
if top
|
||||
@treeViewList.scrollTop(top)
|
||||
|
@ -1,4 +1,5 @@
|
||||
$ = require 'jquery'
|
||||
{$$} = require 'space-pen'
|
||||
_ = require 'underscore'
|
||||
TreeView = require 'tree-view/lib/tree-view'
|
||||
RootView = require 'root-view'
|
||||
@ -49,7 +50,7 @@ describe "TreeView", ->
|
||||
rootView.deactivate()
|
||||
window.rootView = new RootView()
|
||||
rootView.open()
|
||||
treeView = window.loadPackage("tree-view").packageMain.createView()
|
||||
treeView = window.loadPackage("tree-view").mainModule.createView()
|
||||
|
||||
it "does not attach to the root view or create a root node when initialized", ->
|
||||
expect(treeView.hasParent()).toBeFalsy()
|
||||
@ -75,13 +76,13 @@ describe "TreeView", ->
|
||||
rootView.deactivate()
|
||||
window.rootView = new RootView
|
||||
rootView.open('tree-view.js')
|
||||
treeView = window.loadPackage("tree-view").packageMain.createView()
|
||||
treeView = window.loadPackage("tree-view").mainModule.createView()
|
||||
expect(treeView.hasParent()).toBeFalsy()
|
||||
expect(treeView.root).toExist()
|
||||
|
||||
describe "when the root view is opened to a directory", ->
|
||||
it "attaches to the root view", ->
|
||||
treeView = window.loadPackage("tree-view").packageMain.createView()
|
||||
treeView = window.loadPackage("tree-view").mainModule.createView()
|
||||
expect(treeView.hasParent()).toBeTruthy()
|
||||
expect(treeView.root).toExist()
|
||||
|
||||
@ -301,18 +302,25 @@ describe "TreeView", ->
|
||||
expect(subdir).toHaveClass 'expanded'
|
||||
expect(rootView.getActiveView().isFocused).toBeFalsy()
|
||||
|
||||
describe "when a new file is opened in the active editor", ->
|
||||
it "selects the file in the tree view if the file's entry visible", ->
|
||||
sampleJs.click()
|
||||
rootView.open(fs.resolveOnLoadPath('fixtures/tree-view/tree-view.txt'))
|
||||
describe "when the active item changes on the active pane", ->
|
||||
describe "when the item has a path", ->
|
||||
it "selects the entry with that path in the tree view if it is visible", ->
|
||||
sampleJs.click()
|
||||
rootView.open(require.resolve('fixtures/tree-view/tree-view.txt'))
|
||||
|
||||
expect(sampleTxt).toHaveClass 'selected'
|
||||
expect(treeView.find('.selected').length).toBe 1
|
||||
expect(sampleTxt).toHaveClass 'selected'
|
||||
expect(treeView.find('.selected').length).toBe 1
|
||||
|
||||
it "selects the file's parent dir if the file's entry is not visible", ->
|
||||
rootView.open('dir1/sub-dir1/sub-file1')
|
||||
dirView = treeView.root.find('.directory:contains(dir1)').view()
|
||||
expect(dirView).toHaveClass 'selected'
|
||||
it "selects the path's parent dir if its entry is not visible", ->
|
||||
rootView.open('dir1/sub-dir1/sub-file1')
|
||||
dirView = treeView.root.find('.directory:contains(dir1)').view()
|
||||
expect(dirView).toHaveClass 'selected'
|
||||
|
||||
describe "when the item has no path", ->
|
||||
it "deselects the previously selected entry", ->
|
||||
sampleJs.click()
|
||||
rootView.getActivePane().showItem($$ -> @div('hello'))
|
||||
expect(rootView.find('.selected')).not.toExist()
|
||||
|
||||
describe "when a different editor becomes active", ->
|
||||
it "selects the file in that is open in that editor", ->
|
||||
|
@ -1,57 +0,0 @@
|
||||
.tree-view-wrapper {
|
||||
position: relative;
|
||||
height: 100%;
|
||||
cursor: default;
|
||||
-webkit-user-select: none;
|
||||
min-width: 50px;
|
||||
z-index: 2;
|
||||
}
|
||||
|
||||
.tree-view {
|
||||
position: relative;
|
||||
cursor: default;
|
||||
-webkit-user-select: none;
|
||||
overflow: auto;
|
||||
height: 100%;
|
||||
}
|
||||
|
||||
.tree-view-wrapper .tree-view-resizer {
|
||||
position: absolute;
|
||||
top: 0;
|
||||
right: 0;
|
||||
bottom: 0;
|
||||
width: 10px;
|
||||
cursor: col-resize;
|
||||
z-index: 3;
|
||||
}
|
||||
|
||||
.tree-view .entry {
|
||||
text-wrap: none;
|
||||
white-space: nowrap;
|
||||
}
|
||||
|
||||
.tree-view .entry > .header,
|
||||
.tree-view .entry > .name {
|
||||
z-index: 1;
|
||||
position: relative;
|
||||
display: inline-block;
|
||||
}
|
||||
|
||||
.tree-view .selected > .highlight {
|
||||
position: absolute;
|
||||
left: 0;
|
||||
right: 0;
|
||||
height: 24px;
|
||||
}
|
||||
|
||||
.tree-view .disclosure-arrow {
|
||||
display: inline-block;
|
||||
}
|
||||
|
||||
.tree-view-dialog {
|
||||
position: absolute;
|
||||
bottom: 0;
|
||||
left: 0;
|
||||
right: 0;
|
||||
z-index: 99;
|
||||
}
|
57
src/packages/tree-view/stylesheets/tree-view.less
Normal file
57
src/packages/tree-view/stylesheets/tree-view.less
Normal file
@ -0,0 +1,57 @@
|
||||
.tree-view-wrapper {
|
||||
position: relative;
|
||||
height: 100%;
|
||||
cursor: default;
|
||||
-webkit-user-select: none;
|
||||
min-width: 50px;
|
||||
z-index: 2;
|
||||
|
||||
.tree-view-resizer {
|
||||
position: absolute;
|
||||
top: 0;
|
||||
right: 0;
|
||||
bottom: 0;
|
||||
width: 10px;
|
||||
cursor: col-resize;
|
||||
z-index: 3;
|
||||
}
|
||||
}
|
||||
|
||||
.tree-view {
|
||||
position: relative;
|
||||
cursor: default;
|
||||
-webkit-user-select: none;
|
||||
overflow: auto;
|
||||
height: 100%;
|
||||
|
||||
.entry {
|
||||
text-wrap: none;
|
||||
white-space: nowrap;
|
||||
|
||||
& > .header,
|
||||
> .name {
|
||||
z-index: 1;
|
||||
position: relative;
|
||||
display: inline-block;
|
||||
}
|
||||
}
|
||||
|
||||
.selected > .highlight {
|
||||
position: absolute;
|
||||
left: 0;
|
||||
right: 0;
|
||||
height: 24px;
|
||||
}
|
||||
|
||||
.disclosure-arrow {
|
||||
display: inline-block;
|
||||
}
|
||||
}
|
||||
|
||||
.tree-view-dialog {
|
||||
position: absolute;
|
||||
bottom: 0;
|
||||
left: 0;
|
||||
right: 0;
|
||||
z-index: 99;
|
||||
}
|
@ -1,3 +1,4 @@
|
||||
require 'underscore-extensions'
|
||||
_ = require 'underscore'
|
||||
fs = require 'fs'
|
||||
fsUtils = require 'fs-utils'
|
||||
|
@ -86,11 +86,11 @@ module.exports =
|
||||
paths = []
|
||||
if extensions
|
||||
onPath = (path) =>
|
||||
paths.push(@join(rootPath, path)) if _.contains(extensions, @extension(path))
|
||||
paths.push(path) if _.contains(extensions, @extension(path))
|
||||
false
|
||||
else
|
||||
onPath = (path) =>
|
||||
paths.push(@join(rootPath, path))
|
||||
paths.push(path)
|
||||
false
|
||||
@traverseTreeSync(rootPath, onPath, onPath)
|
||||
paths
|
||||
@ -98,7 +98,7 @@ module.exports =
|
||||
listTree: (rootPath) ->
|
||||
paths = []
|
||||
onPath = (path) =>
|
||||
paths.push(@join(rootPath, path))
|
||||
paths.push(path)
|
||||
true
|
||||
@traverseTreeSync(rootPath, onPath, onPath)
|
||||
paths
|
||||
@ -162,9 +162,9 @@ module.exports =
|
||||
absolutePath = @join(rootPath, file)
|
||||
stats = fs.statSync(absolutePath)
|
||||
if stats.isDirectory()
|
||||
traverse(absolutePath, relativePath, onFile, onDirectory) if onDirectory(relativePath)
|
||||
traverse(absolutePath, relativePath, onFile, onDirectory) if onDirectory(absolutePath)
|
||||
else if stats.isFile()
|
||||
onFile(relativePath)
|
||||
onFile(absolutePath)
|
||||
|
||||
traverse(rootPath, '', onFile, onDirectory)
|
||||
|
||||
|
@ -7,6 +7,12 @@ $.fn.scrollBottom = (newValue) ->
|
||||
else
|
||||
@scrollTop() + @height()
|
||||
|
||||
$.fn.scrollDown = ->
|
||||
@scrollTop(@scrollTop() + $(window).height() / 20)
|
||||
|
||||
$.fn.scrollUp = ->
|
||||
@scrollTop(@scrollTop() - $(window).height() / 20)
|
||||
|
||||
$.fn.scrollToTop = ->
|
||||
@scrollTop(0)
|
||||
|
||||
@ -53,7 +59,9 @@ $.fn.trueHeight = ->
|
||||
$.fn.trueWidth = ->
|
||||
this[0].getBoundingClientRect().width
|
||||
|
||||
$.fn.document = (eventDescriptions) ->
|
||||
$.fn.document = (eventName, docString) ->
|
||||
eventDescriptions = {}
|
||||
eventDescriptions[eventName] = docString
|
||||
@data('documentation', {}) unless @data('documentation')
|
||||
_.extend(@data('documentation'), eventDescriptions)
|
||||
|
||||
@ -69,12 +77,20 @@ $.fn.events = ->
|
||||
else
|
||||
events
|
||||
|
||||
$.fn.command = (args...) ->
|
||||
eventName = args[0]
|
||||
documentation = {}
|
||||
documentation[eventName] = _.humanizeEventName(eventName)
|
||||
@document(documentation)
|
||||
@on(args...)
|
||||
$.fn.command = (eventName, selector, options, handler) ->
|
||||
if not options?
|
||||
handler = selector
|
||||
selector = null
|
||||
else if not handler?
|
||||
handler = options
|
||||
options = null
|
||||
|
||||
if selector? and typeof(selector) is 'object'
|
||||
options = selector
|
||||
selector = null
|
||||
|
||||
@document(eventName, _.humanizeEventName(eventName, options?["doc"]))
|
||||
@on(eventName, selector, options?['data'], handler)
|
||||
|
||||
$.fn.iconSize = (size) ->
|
||||
@width(size).height(size).css('font-size', size)
|
||||
|
@ -52,16 +52,20 @@ _.mixin
|
||||
regex = RegExp('[' + specials.join('\\') + ']', 'g')
|
||||
string.replace(regex, "\\$&");
|
||||
|
||||
humanizeEventName: (eventName) ->
|
||||
if /:/.test(eventName)
|
||||
[namespace, name] = eventName.split(':')
|
||||
return "#{@humanizeEventName(namespace)}: #{@humanizeEventName(name)}"
|
||||
humanizeEventName: (eventName, eventDoc) ->
|
||||
[namespace, event] = eventName.split(':')
|
||||
return _.capitalize(namespace) unless event?
|
||||
|
||||
words = eventName.split('-')
|
||||
words.map(_.capitalize).join(' ')
|
||||
namespaceDoc = _.undasherize(namespace)
|
||||
eventDoc ?= _.undasherize(event)
|
||||
|
||||
"#{namespaceDoc}: #{eventDoc}"
|
||||
|
||||
capitalize: (word) ->
|
||||
word[0].toUpperCase() + word[1..]
|
||||
if word.toLowerCase() is 'github'
|
||||
'GitHub'
|
||||
else
|
||||
word[0].toUpperCase() + word[1..]
|
||||
|
||||
pluralize: (count=0, singular, plural=singular+'s') ->
|
||||
if count is 1
|
||||
@ -80,6 +84,9 @@ _.mixin
|
||||
else
|
||||
"-"
|
||||
|
||||
undasherize: (string) ->
|
||||
string.split('-').map(_.capitalize).join(' ')
|
||||
|
||||
underscore: (string) ->
|
||||
string = string[0].toLowerCase() + string[1..]
|
||||
string.replace /([A-Z])|(-)/g, (m, letter, dash) ->
|
||||
|
@ -1,81 +0,0 @@
|
||||
html, body {
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
#root-view {
|
||||
height: 100%;
|
||||
overflow: hidden;
|
||||
position: relative;
|
||||
}
|
||||
|
||||
#root-view #horizontal {
|
||||
display: -webkit-flex;
|
||||
height: 100%;
|
||||
}
|
||||
|
||||
#root-view #vertical {
|
||||
display: -webkit-flex;
|
||||
-webkit-flex: 1;
|
||||
-webkit-flex-flow: column;
|
||||
}
|
||||
|
||||
#panes {
|
||||
position: relative;
|
||||
-webkit-flex: 1;
|
||||
}
|
||||
|
||||
#panes .column {
|
||||
position: absolute;
|
||||
top: 0;
|
||||
bottom: 0;
|
||||
left: 0;
|
||||
right: 0;
|
||||
overflow-y: hidden;
|
||||
}
|
||||
|
||||
#panes .row {
|
||||
position: absolute;
|
||||
top: 0;
|
||||
bottom: 0;
|
||||
left: 0;
|
||||
right: 0;
|
||||
overflow-x: hidden;
|
||||
}
|
||||
|
||||
#panes .pane {
|
||||
position: absolute;
|
||||
display: -webkit-flex;
|
||||
-webkit-flex-flow: column;
|
||||
top: 0;
|
||||
bottom: 0;
|
||||
left: 0;
|
||||
right: 0;
|
||||
box-sizing: border-box;
|
||||
}
|
||||
|
||||
#panes .pane .item-views {
|
||||
-webkit-flex: 1;
|
||||
display: -webkit-flex;
|
||||
-webkit-flex-flow: column;
|
||||
}
|
||||
|
||||
@font-face {
|
||||
font-family: 'Octicons Regular';
|
||||
src: url("octicons-regular-webfont.woff") format("woff");
|
||||
font-weight: normal;
|
||||
font-style: normal;
|
||||
}
|
||||
|
||||
.is-loading {
|
||||
background-image: url(images/spinner.svg);
|
||||
background-repeat: no-repeat;
|
||||
width: 14px;
|
||||
height: 14px;
|
||||
opacity: 0.5;
|
||||
background-size: contain;
|
||||
position: relative;
|
||||
display: inline-block;
|
||||
padding-left: 19px;
|
||||
}
|
86
static/atom.less
Normal file
86
static/atom.less
Normal file
@ -0,0 +1,86 @@
|
||||
html, body {
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
#root-view {
|
||||
height: 100%;
|
||||
overflow: hidden;
|
||||
position: relative;
|
||||
|
||||
#horizontal {
|
||||
display: -webkit-flex;
|
||||
height: 100%;
|
||||
}
|
||||
|
||||
#vertical {
|
||||
display: -webkit-flex;
|
||||
-webkit-flex: 1;
|
||||
-webkit-flex-flow: column;
|
||||
}
|
||||
}
|
||||
|
||||
#panes {
|
||||
position: relative;
|
||||
-webkit-flex: 1;
|
||||
|
||||
.column {
|
||||
position: absolute;
|
||||
top: 0;
|
||||
bottom: 0;
|
||||
left: 0;
|
||||
right: 0;
|
||||
overflow-y: hidden;
|
||||
}
|
||||
|
||||
.row {
|
||||
position: absolute;
|
||||
top: 0;
|
||||
bottom: 0;
|
||||
left: 0;
|
||||
right: 0;
|
||||
overflow-x: hidden;
|
||||
}
|
||||
|
||||
.pane {
|
||||
position: absolute;
|
||||
display: -webkit-flex;
|
||||
-webkit-flex-flow: column;
|
||||
top: 0;
|
||||
bottom: 0;
|
||||
left: 0;
|
||||
right: 0;
|
||||
box-sizing: border-box;
|
||||
}
|
||||
|
||||
.pane .item-views {
|
||||
-webkit-flex: 1;
|
||||
display: -webkit-flex;
|
||||
-webkit-flex-flow: column;
|
||||
}
|
||||
|
||||
.pane .item-views > * {
|
||||
-webkit-flex: 1;
|
||||
min-height: 0;
|
||||
}
|
||||
}
|
||||
|
||||
@font-face {
|
||||
font-family: 'Octicons Regular';
|
||||
src: url("octicons-regular-webfont.woff") format("woff");
|
||||
font-weight: normal;
|
||||
font-style: normal;
|
||||
}
|
||||
|
||||
.is-loading {
|
||||
background-image: url(images/spinner.svg);
|
||||
background-repeat: no-repeat;
|
||||
width: 14px;
|
||||
height: 14px;
|
||||
opacity: 0.5;
|
||||
background-size: contain;
|
||||
position: relative;
|
||||
display: inline-block;
|
||||
padding-left: 19px;
|
||||
}
|
@ -1,134 +0,0 @@
|
||||
.command-panel {
|
||||
position: relative;
|
||||
padding: 0;
|
||||
}
|
||||
|
||||
.command-panel .is-loading {
|
||||
display: block;
|
||||
margin: 0 auto 10px auto;
|
||||
width: 100px;
|
||||
background-color: #111;
|
||||
background-size: auto;
|
||||
background-position: 5px 5px;
|
||||
padding: 5px 5px 10px 30px;
|
||||
border-radius: 3px;
|
||||
border: 1px solid rgba(255, 255, 255, 0.1);
|
||||
border-top: 1px solid rgba(0, 0, 0, 1);
|
||||
border-left: 1px solid rgba(0, 0, 0, 1);
|
||||
}
|
||||
|
||||
.command-panel .preview-count {
|
||||
display: inline-block;
|
||||
margin-top: 4px;
|
||||
font-size: 11px;
|
||||
}
|
||||
|
||||
.command-panel .preview-list {
|
||||
max-height: 300px;
|
||||
overflow: auto;
|
||||
margin: 0 0 10px 0;
|
||||
position: relative;
|
||||
cursor: default;
|
||||
}
|
||||
|
||||
.command-panel .header:after {
|
||||
content: ".";
|
||||
display: block;
|
||||
visibility: hidden;
|
||||
clear: both;
|
||||
height: 0;
|
||||
}
|
||||
|
||||
.command-panel .expand-collapse {
|
||||
float: right;
|
||||
}
|
||||
|
||||
.command-panel .expand-collapse li {
|
||||
display: inline-block;
|
||||
cursor: pointer;
|
||||
font-size: 11px;
|
||||
margin-left: 5px;
|
||||
padding: 5px 10px;
|
||||
border-radius: 3px;
|
||||
}
|
||||
|
||||
.command-panel .preview-count,
|
||||
.command-panel .expand-collapse {
|
||||
-webkit-user-select: none;
|
||||
}
|
||||
|
||||
.command-panel .preview-list .path {
|
||||
position: relative;
|
||||
-webkit-user-select: none;
|
||||
}
|
||||
|
||||
.command-panel .preview-list .path-details:before {
|
||||
font-family: 'Octicons Regular';
|
||||
font-size: 12px;
|
||||
width: 12px;
|
||||
height: 12px;
|
||||
margin-right: 5px;
|
||||
margin-left: 5px;
|
||||
-webkit-font-smoothing: antialiased;
|
||||
content: "\f05b";
|
||||
position: relative;
|
||||
top: 0;
|
||||
}
|
||||
|
||||
.command-panel .preview-list .is-collapsed .path-details:before {
|
||||
content: "\f05a";
|
||||
}
|
||||
|
||||
.command-panel .preview-list .path-name:before {
|
||||
font-family: 'Octicons Regular';
|
||||
font-size: 16px;
|
||||
width: 16px;
|
||||
height: 16px;
|
||||
margin-right: 5px;
|
||||
-webkit-font-smoothing: antialiased;
|
||||
content: "\f011";
|
||||
position: relative;
|
||||
top: 1px;
|
||||
}
|
||||
|
||||
.command-panel .preview-list .path.readme .path-name:before {
|
||||
content: "\f007";
|
||||
}
|
||||
|
||||
.command-panel .preview-list .operation {
|
||||
padding-top: 2px;
|
||||
padding-bottom: 2px;
|
||||
padding-left: 10px;
|
||||
}
|
||||
|
||||
.command-panel .preview-list .line-number {
|
||||
margin-right: 1ex;
|
||||
text-align: right;
|
||||
display: inline-block;
|
||||
}
|
||||
|
||||
.command-panel .preview-list .path-match-number {
|
||||
padding-left: 8px;
|
||||
}
|
||||
|
||||
.command-panel .preview-list .preview {
|
||||
word-break: break-all;
|
||||
}
|
||||
|
||||
.command-panel .preview-list .preview .match {
|
||||
-webkit-border-radius: 2px;
|
||||
padding: 1px;
|
||||
}
|
||||
|
||||
.command-panel .prompt-and-editor .editor {
|
||||
position: relative;
|
||||
}
|
||||
|
||||
.command-panel .prompt-and-editor {
|
||||
display: -webkit-flex;
|
||||
}
|
||||
|
||||
.error-messages {
|
||||
padding: 5px 1em;
|
||||
color: white;
|
||||
}
|
132
static/command-panel.less
Normal file
132
static/command-panel.less
Normal file
@ -0,0 +1,132 @@
|
||||
.command-panel {
|
||||
position: relative;
|
||||
padding: 0;
|
||||
|
||||
.is-loading {
|
||||
display: block;
|
||||
margin: 0 auto 10px auto;
|
||||
width: 100px;
|
||||
background-color: #111111;
|
||||
background-size: auto;
|
||||
background-position: 5px 5px;
|
||||
padding: 5px 5px 10px 30px;
|
||||
border-radius: 3px;
|
||||
border: 1px solid rgba(255,255,255,0.1);
|
||||
border-top: 1px solid rgba(0,0,0,1);
|
||||
border-left: 1px solid rgba(0,0,0,1);
|
||||
}
|
||||
|
||||
.preview-count {
|
||||
display: inline-block;
|
||||
margin-top: 4px;
|
||||
font-size: 11px;
|
||||
-webkit-user-select: none;
|
||||
}
|
||||
|
||||
.preview-list {
|
||||
max-height: 300px;
|
||||
overflow: auto;
|
||||
margin: 0 0 10px 0;
|
||||
position: relative;
|
||||
cursor: default;
|
||||
|
||||
.path {
|
||||
position: relative;
|
||||
-webkit-user-select: none;
|
||||
}
|
||||
|
||||
.path-details:before {
|
||||
font-family: 'Octicons Regular';
|
||||
font-size: 12px;
|
||||
width: 12px;
|
||||
height: 12px;
|
||||
margin-right: 5px;
|
||||
margin-left: 5px;
|
||||
-webkit-font-smoothing: antialiased;
|
||||
content: "\f05b";
|
||||
position: relative;
|
||||
top: 0;
|
||||
}
|
||||
|
||||
.is-collapsed .path-details:before {
|
||||
content: "\f05a";
|
||||
}
|
||||
|
||||
.path-name:before {
|
||||
font-family: 'Octicons Regular';
|
||||
font-size: 16px;
|
||||
width: 16px;
|
||||
height: 16px;
|
||||
margin-right: 5px;
|
||||
-webkit-font-smoothing: antialiased;
|
||||
content: "\f011";
|
||||
position: relative;
|
||||
top: 1px;
|
||||
}
|
||||
|
||||
.path.readme .path-name:before {
|
||||
content: "\f007";
|
||||
}
|
||||
|
||||
.operation {
|
||||
padding-top: 2px;
|
||||
padding-bottom: 2px;
|
||||
padding-left: 10px;
|
||||
}
|
||||
|
||||
.line-number {
|
||||
margin-right: 1ex;
|
||||
text-align: right;
|
||||
display: inline-block;
|
||||
}
|
||||
|
||||
.path-match-number {
|
||||
padding-left: 8px;
|
||||
}
|
||||
|
||||
.preview {
|
||||
word-break: break-all;
|
||||
|
||||
.match {
|
||||
-webkit-border-radius: 2px;
|
||||
padding: 1px;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.header:after {
|
||||
content: ".";
|
||||
display: block;
|
||||
visibility: hidden;
|
||||
clear: both;
|
||||
height: 0;
|
||||
}
|
||||
|
||||
.expand-collapse {
|
||||
float: right;
|
||||
-webkit-user-select: none;
|
||||
|
||||
li {
|
||||
display: inline-block;
|
||||
cursor: pointer;
|
||||
font-size: 11px;
|
||||
margin-left: 5px;
|
||||
padding: 5px 10px;
|
||||
border-radius: 3px;
|
||||
}
|
||||
}
|
||||
|
||||
.prompt-and-editor {
|
||||
display: -webkit-flex;
|
||||
|
||||
.editor {
|
||||
position: relative;
|
||||
-webkit-flex: 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.error-messages {
|
||||
padding: 5px 1em;
|
||||
color: white;
|
||||
}
|
@ -6,7 +6,6 @@
|
||||
z-index: 0;
|
||||
font-family: Inconsolata, Monaco, Courier;
|
||||
line-height: 1.3;
|
||||
-webkit-flex: 1;
|
||||
}
|
||||
|
||||
.editor.mini {
|
@ -1,23 +0,0 @@
|
||||
.source.gfm {
|
||||
-webkit-font-smoothing: antialiased;
|
||||
}
|
||||
|
||||
.gfm .markup.heading {
|
||||
font-weight: bold;
|
||||
}
|
||||
|
||||
.gfm .bold {
|
||||
font-weight: bold;
|
||||
}
|
||||
|
||||
.gfm .italic {
|
||||
font-style: italic;
|
||||
}
|
||||
|
||||
.gfm .comment.quote {
|
||||
font-style: italic;
|
||||
}
|
||||
|
||||
.gfm .raw {
|
||||
-webkit-font-smoothing: subpixel-antialiased;
|
||||
}
|
25
static/markdown.less
Normal file
25
static/markdown.less
Normal file
@ -0,0 +1,25 @@
|
||||
.source {
|
||||
.gfm {
|
||||
-webkit-font-smoothing: antialiased;
|
||||
|
||||
.markup.heading {
|
||||
font-weight: bold;
|
||||
}
|
||||
|
||||
.bold {
|
||||
font-weight: bold;
|
||||
}
|
||||
|
||||
.italic {
|
||||
font-style: italic;
|
||||
}
|
||||
|
||||
.comment.quote {
|
||||
font-style: italic;
|
||||
}
|
||||
|
||||
.raw {
|
||||
-webkit-font-smoothing: subpixel-antialiased;
|
||||
}
|
||||
}
|
||||
}
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue
Block a user