ModalSelector. for find file and stuff

This commit is contained in:
Chris Wanstrath
2011-11-12 21:43:04 -08:00
parent ab6ecf13f9
commit 74331337b2
5 changed files with 166 additions and 293 deletions

View File

@@ -1,102 +0,0 @@
$ = require 'jquery'
_ = require 'underscore'
File = require 'fs'
Pane = require 'pane'
jQuery = $
Modal = require 'modal'
require 'filefinder/stringscore'
module.exports =
class FilefinderPane extends Pane
html: require "filefinder/filefinder.html"
constructor: (@filefinder) ->
$('#filefinder input').live 'keydown', @onKeydown
@modal = new Modal @html
onKeydown: (e) =>
keys = up: 38, down: 40, enter: 13
if e.keyCode is keys.enter
@openSelected()
false
else if e.keyCode is keys.up
@moveUp()
else if e.keyCode is keys.down
@moveDown()
else
@filterFiles()
toggle: ->
if @modal.showing
@modal.hide()
else
@showFinder()
paths: ->
_paths = []
for dir in File.list window.url
continue if /\.git|Cocoa/.test dir
if File.isDirectory dir
_paths.push File.listDirectoryTree dir
else
_paths.push dir
_.reject _.flatten(_paths), (dir) -> File.isDirectory dir
showFinder: ->
@modal.show()
@files = []
for file in @paths()
@files.push file.replace "#{window.url}/", ''
@filterFiles()
findMatchingFiles: (query) ->
return [] if not query
results = []
for file in @files
score = file.score query
if score > 0
# Basename matches count for more.
if not query.match '/'
if name.match '/'
score += name.replace(/^.*\//, '').score query
else
score *= 2
results.push [score, file]
sorted = results.sort (a, b) -> b[0] - a[0]
_.map sorted, (el) -> el[1]
filterFiles: ->
if query = $('#filefinder input').val()
files = @findMatchingFiles query
else
files = @files
$('#filefinder ul').empty()
for file in files[0..10]
$('#filefinder ul').append "<li>#{file}</li>"
$('#filefinder input').focus()
$('#filefinder li:first').addClass 'selected'
openSelected: ->
dir = window.url
file = $('#filefinder .selected').text()
window.open "#{dir}/#{file}"
@toggle()
moveUp: ->
selected = $('#filefinder .selected')
if selected.prev().length
selected.prev().addClass 'selected'
selected.removeClass 'selected'
moveDown: ->
selected = $('#filefinder .selected')
if selected.next().length
selected.next().addClass 'selected'
selected.removeClass 'selected'

View File

@@ -1,5 +1,7 @@
_ = require 'underscore'
Extension = require 'extension'
FilefinderPane = require 'filefinder/filefinder-pane'
ModalSelector = require 'modal-selector'
module.exports =
class Filefinder extends Extension
@@ -7,8 +9,9 @@ class Filefinder extends Extension
atom.keybinder.load require.resolve "filefinder/key-bindings.coffee"
atom.on 'project:open', @startup
startup: =>
@pane = new FilefinderPane this
startup: (@project) =>
@pane = new ModalSelector _.map @project.urls(), (url) ->
name: (url.replace "#{window.url}/", ''), url: url
toggle: ->
@pane?.toggle()
@pane?.toggle()

View File

@@ -1,65 +0,0 @@
<style>
#modal .content {
background: #ededed;
padding: 0;
}
#modal .close {
display: none;
}
#filefinder .filelist {
height: 100px;
overflow: hidden;
padding: 10px 0;
}
#filefinder input[type=search] {
width: 95%;
margin: 10px;
}
#modal .content {
min-width: 200px;
height: 100%;
background-color: #DDE3EA;
color: #000;
border-right: 1px solid #B4B4B4;
cursor: default;
-webkit-user-select: none;
overflow: auto;
}
#modal .content .cwd {
padding-top: 5px;
padding-left: 5px;
font-weight: bold;
color: #708193;
text-transform: uppercase;
text-shadow: 0 1px 0 rgba(255, 255, 255, 0.5);
}
#modal .content ul {
margin: 0;
padding-top: 2px;
list-style-type: none;
}
#modal .content li {
padding: 0;
padding-left: 5px;
line-height: 20px;
font-size: 14px;
}
#modal .content li.selected {
background-image: -webkit-gradient(linear,0% 0,0% 100%,from(#BCCBEB),to(#8094BB));
border-top: 1px solid #A0AFCD;
color: #fff;
text-shadow: 0 1px 0 rgba(0, 0, 0, 0.5);
}
</style>
<div id='filefinder'>
<input type='search'>
<br>
<ul class='filelist'>
</ul>
</div>

View File

@@ -1,122 +0,0 @@
/*!
* string_score.js: String Scoring Algorithm 0.1.9
*
* http://joshaven.com/string_score
* https://github.com/joshaven/string_score
*
* Copyright (C) 2009-2011 Joshaven Potter <yourtech@gmail.com>
* Copyright (C) 2010-2011 Yesudeep Mangalapilly <yesudeep@gmail.com>
* MIT license: http://www.opensource.org/licenses/mit-license.php
*
* Date: Tue Mar 1 2011
*/
/**
* Scores a string against another string.
* 'Hello World'.score('he'); //=> 0.5931818181818181
* 'Hello World'.score('Hello'); //=> 0.7318181818181818
*/
String.prototype.score = function(abbreviation, fuzziness) {
var total_character_score = 0,
abbreviation_length = abbreviation.length,
string = this,
string_length = string.length,
start_of_string_bonus,
abbreviation_score,
fuzzies=1,
final_score;
// If the string is equal to the abbreviation, perfect match.
if (string == abbreviation) {return 1.0;}
// Walk through abbreviation and add up scores.
for (var i = 0,
character_score/* = 0*/,
index_in_string/* = 0*/,
c/* = ''*/,
index_c_lowercase/* = 0*/,
index_c_uppercase/* = 0*/,
min_index/* = 0*/;
i < abbreviation_length;
++i) {
// Find the first case-insensitive match of a character.
c = abbreviation[i];
//index_in_string = __first_valid_index(
// string.indexOf(c.toLowerCase()),
// string.indexOf(c.toUpperCase())
//);
// Inlined the above call below.
index_c_lowercase = string.indexOf(c.toLowerCase());
index_c_uppercase = string.indexOf(c.toUpperCase());
min_index = Math.min(index_c_lowercase, index_c_uppercase);
index_in_string = (min_index > -1) ?
min_index :
Math.max(index_c_lowercase, index_c_uppercase);
// End inlining.
if (index_in_string === -1) {
if (fuzziness) {
fuzzies += 1-fuzziness;
break;
} else {
return 0;
}
} else {
character_score = 0.1;
}
// Set base score for matching 'c'.
// Same case bonus.
if (string[index_in_string] === c) {
character_score += 0.1;
}
// Consecutive letter & start-of-string Bonus
if (index_in_string === 0) {
// Increase the score when matching first character of the
// remainder of the string
character_score += 0.6;
if (i === 0) {
// If match is the first character of the string
// & the first character of abbreviation, add a
// start-of-string match bonus.
start_of_string_bonus = 1 //true;
}
}
// Acronym Bonus
// Weighing Logic: Typing the first character of an acronym is as if you
// preceded it with two perfect character matches.
if (string.charAt(index_in_string - 1) === ' ') {
character_score += 0.8; // * Math.min(index_in_string, 5); // Cap bonus at 0.4 * 5
}
// Left trim the already matched part of the string
// (forces sequential matching).
string = string.substring(index_in_string + 1, string_length);
total_character_score += character_score;
} // end of for loop
// Uncomment to weigh smaller words higher.
// return total_character_score / string_length;
abbreviation_score = total_character_score / abbreviation_length;
//percentage_of_matched_string = abbreviation_length / string_length;
//word_score = abbreviation_score * percentage_of_matched_string;
// Reduce penalty for longer strings.
//final_score = (word_score + abbreviation_score) / 2;
final_score = ((abbreviation_score * (abbreviation_length / string_length)) + abbreviation_score) / 2;
final_score = final_score / fuzzies;
if (start_of_string_bonus && (final_score + 0.15 < 1)) {
final_score += 0.15;
}
return final_score;
};

View File

@@ -0,0 +1,159 @@
$ = require 'jquery'
_ = require 'underscore'
Modal = require 'modal'
jQuery = $
require 'stringscore'
module.exports =
class ModalSelector extends Modal
selectorHTML: '''
<div id="modal-selector">
<input type="search">
<br>
<ul class="list">
</ul>
</div>
'''
showing: false
# The items to filter. An Array of {name:name, url:url} objects.
list: []
constructor: (@list) ->
super @selectorHTML
head = $('head')[0]
style = document.createElement 'style'
rules = document.createTextNode @selectorCSS
style.type = 'text/css'
style.appendChild rules
head.appendChild style
$('#modal-selector input').live 'keydown', @onKeydown
onKeydown: (e) =>
keys = up: 38, down: 40, enter: 13
if e.keyCode is keys.enter
@openSelected()
false
else if e.keyCode is keys.up
@moveUp()
else if e.keyCode is keys.down
@moveDown()
else
@filter()
show: ->
console.log 'cool'
super
@filter()
filter: ->
if query = $('#modal-selector input').val()
items = @findMatchingItems query
else
items = @list
$('#modal-selector ul').empty()
for {name, url} in items[0..10]
$('#modal-selector ul').append "<li data-url=#{url}>#{name}</li>"
$('#modal-selector input').focus()
$('#modal-selector li:first').addClass 'selected'
findMatchingItems: (query) ->
return [] if not query
results = []
for item in @list
{name, url} = item
score = name.score query
if score > 0
# Basename matches count for more.
if not query.match '/'
if name.match '/'
score += name.replace(/^.*\//, '').score query
else
score *= 2
results.push [score, item]
sorted = results.sort (a, b) -> b[0] - a[0]
_.map sorted, (el) -> el[1]
openSelected: ->
text = $('#modal-selector .selected').text()
window.open _.find(@list, (item) -> item.name is text).url
@toggle()
moveUp: ->
selected = $('#modal-selector .selected')
if selected.prev().length
selected.prev().addClass 'selected'
selected.removeClass 'selected'
moveDown: ->
selected = $('#modal-selector .selected')
if selected.next().length
selected.next().addClass 'selected'
selected.removeClass 'selected'
selectorCSS: '''
#modal .content {
background: #ededed;
padding: 0;
}
#modal .close {
display: none;
}
#modal-selector .list {
height: 100px;
overflow: hidden;
padding: 10px 0;
}
#modal-selector input[type=search] {
width: 95%;
margin: 10px;
}
#modal .content {
min-width: 200px;
height: 100%;
background-color: #DDE3EA;
color: #000;
border-right: 1px solid #B4B4B4;
cursor: default;
-webkit-user-select: none;
overflow: auto;
}
#modal .content .cwd {
padding-top: 5px;
padding-left: 5px;
font-weight: bold;
color: #708193;
text-transform: uppercase;
text-shadow: 0 1px 0 rgba(255, 255, 255, 0.5);
}
#modal .content ul {
margin: 0;
padding-top: 2px;
list-style-type: none;
}
#modal .content li {
padding: 0;
padding-left: 5px;
line-height: 20px;
font-size: 14px;
}
#modal .content li.selected {
background-image: -webkit-gradient(linear,0% 0,0% 100%,from(#BCCBEB),to(#8094BB));
border-top: 1px solid #A0AFCD;
color: #fff;
text-shadow: 0 1px 0 rgba(0, 0, 0, 0.5);
}
'''