From 74331337b29c7347d43cbb2ea95dfd8d8c1a4fbc Mon Sep 17 00:00:00 2001 From: Chris Wanstrath Date: Sat, 12 Nov 2011 21:43:04 -0800 Subject: [PATCH] ModalSelector. for find file and stuff --- extensions/filefinder/filefinder-pane.coffee | 102 ------------ extensions/filefinder/filefinder.coffee | 11 +- extensions/filefinder/filefinder.html | 65 -------- extensions/filefinder/stringscore.js | 122 -------------- src/atom/modal-selector.coffee | 159 +++++++++++++++++++ 5 files changed, 166 insertions(+), 293 deletions(-) delete mode 100644 extensions/filefinder/filefinder-pane.coffee delete mode 100644 extensions/filefinder/filefinder.html delete mode 100644 extensions/filefinder/stringscore.js create mode 100644 src/atom/modal-selector.coffee diff --git a/extensions/filefinder/filefinder-pane.coffee b/extensions/filefinder/filefinder-pane.coffee deleted file mode 100644 index 93cc68e7a..000000000 --- a/extensions/filefinder/filefinder-pane.coffee +++ /dev/null @@ -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 "
  • #{file}
  • " - $('#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' - diff --git a/extensions/filefinder/filefinder.coffee b/extensions/filefinder/filefinder.coffee index 69a15fc9c..3c66751a4 100644 --- a/extensions/filefinder/filefinder.coffee +++ b/extensions/filefinder/filefinder.coffee @@ -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() \ No newline at end of file + @pane?.toggle() diff --git a/extensions/filefinder/filefinder.html b/extensions/filefinder/filefinder.html deleted file mode 100644 index 67df1afbe..000000000 --- a/extensions/filefinder/filefinder.html +++ /dev/null @@ -1,65 +0,0 @@ - - -
    - -
    - -
    \ No newline at end of file diff --git a/extensions/filefinder/stringscore.js b/extensions/filefinder/stringscore.js deleted file mode 100644 index a734adc9f..000000000 --- a/extensions/filefinder/stringscore.js +++ /dev/null @@ -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 - * Copyright (C) 2010-2011 Yesudeep Mangalapilly - * 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; -}; diff --git a/src/atom/modal-selector.coffee b/src/atom/modal-selector.coffee new file mode 100644 index 000000000..ef5a765af --- /dev/null +++ b/src/atom/modal-selector.coffee @@ -0,0 +1,159 @@ +$ = require 'jquery' +_ = require 'underscore' + +Modal = require 'modal' + +jQuery = $ +require 'stringscore' + +module.exports = +class ModalSelector extends Modal + selectorHTML: ''' + + ''' + + 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 "
  • #{name}
  • " + $('#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); +} + ''' \ No newline at end of file