From 5fa87f83e7dbc475349475aca31d10815cbf4ea9 Mon Sep 17 00:00:00 2001 From: Garen Torikian Date: Sun, 23 Dec 2012 01:14:55 -0800 Subject: [PATCH] Bring in Harutyun's code --- demo/kitchen-sink/autocompleter.js | 240 ------------------- lib/ace/autocomplete.js | 372 +++++++++++++++++------------ 2 files changed, 219 insertions(+), 393 deletions(-) delete mode 100644 demo/kitchen-sink/autocompleter.js diff --git a/demo/kitchen-sink/autocompleter.js b/demo/kitchen-sink/autocompleter.js deleted file mode 100644 index 61fd316e..00000000 --- a/demo/kitchen-sink/autocompleter.js +++ /dev/null @@ -1,240 +0,0 @@ -define(function(require, exports, module) { -"use strict"; - -var dom = require("ace/lib/dom"); -var layout = require("./layout"); -var HashHandler = require("ace/keyboard/hash_handler").HashHandler; -var EditSession = require("ace/edit_session").EditSession; -var TextMode = require("ace/mode/text").Mode; - -var mode = new TextMode(); -mode.$tokenizer = { - getLineTokens: function(line) { - - } -}; -var Autocompleter = function() { - this.keyboardHandler = new HashHandler(); - this.keyboardHandler.bindKeys(this.commands); - - this.$blurListener = this.blurListener.bind(this); - this.$changeListener = this.changeListener.bind(this); -}; - -(function(){ - this.$init = function(e) { - var el = dom.createElement("div"); - var popup = new layout.singleLineEditor(el); - document.body.appendChild(el); - el.style.width="200px" - el.style.zIndex="20000" - el.style.background="white" - el.style.border="lightgray solid" - el.style.position="fixed" - el.style.display = "none" - popup.renderer.content.style.cursor="default" - - var nop = function(){}; - - popup.focus = nop; - popup.$isFocused = true; - - popup.renderer.$cursorLayer.restartTimer = nop - popup.renderer.$cursorLayer.update = nop - popup.renderer.$cursorLayer.element.style.display = "none"; - - popup.renderer.maxLines = 6 - popup.renderer.$keepTextAreaAtCursor=false - - - popup.setHighlightActiveLine(true) - popup.setSession(new EditSession("")) - - popup.on("mousedown", function(e) { - var pos = e.getDocumentPosition(); - popup.moveCursorToPosition(pos); - popup.selection.clearSelection(); - e.stop(); - }) - - /* popup.session.setMode({ - $ - }) */ - - popup.getRow = function() { - var line = this.getCursorPosition().row; - if (line == 0 && !this.getHighlightActiveLine()) - line = -1; - return line; - }; - popup.setRow = function(line) { - popup.setHighlightActiveLine(line != -1); - popup.gotoLine(line + 1); - }; - - popup.on("click", function(e) { - this.insertMatch(); - }.bind(this)); - - popup.setData = function(list) { - var value = "" - if (list) { - if (typeof list[0] == "string") - value = list.join("\n"); - else - value = list.map(function(x){return x.value}).join("\n"); - } - - this.setValue(value, -1); - }; - - popup.setHighlight = function(re) { - ace.session.highlight(re) - ace.session._emit("changeFrontMarker") - }; - - this.popup = popup; - }; - - this.openPopup = function(editor) { - if (!this.popup) - this.$init(); - - this.popup.setData(this.completions.filtered) - - var renderer = editor.renderer; - var lineHeight = renderer.layerConfig.lineHeight; - var pos = renderer.$cursorLayer.getPixelPosition(null, true) - var rect = editor.container.getBoundingClientRect() - pos.top += rect.top - renderer.layerConfig.offset; - pos.left += rect.left; - pos.left += renderer.$gutterLayer.gutterWidth; - - var el = this.popup.container; - if (pos.top > window.innerHeight / 2 + lineHeight) { - el.style.top = "" - el.style.bottom = window.innerHeight - pos.top + "px"; - } else { - pos.top += lineHeight; - el.style.top = pos.top + "px"; - el.style.bottom = "" - } - - el.style.left = pos.left + "px"; - el.style.display = ""; - }; - - this.attachToEditor = function(editor) { - if (this.editor) - this.detach(); - this.editor = editor; - if (editor.autocompleter != this) { - if (editor.autocompleter) - editor.autocompleter.detach(); - editor.autocompleter = this; - } - editor.keyBinding.addKeyboardHandler(this.keyboardHandler) - editor.on("changeSelection", this.$changeListener) - editor.on("blur", this.$blurListener) - }; - this.detach = function() { - this.editor.keyBinding.removeKeyboardHandler(this.keyboardHandler); - this.editor.removeEventListener("changeSelection", this.changeListener); - this.editor.removeEventListener("blur", this.changeListener); - this.popup.container.style.display = "none"; - }; - - this.changeListener = function(e) { - console.log(e) - }; - - this.blurListener = function() { - if (document.activeElement != this.editor.textInput.getElement()) - this.detach(); - }; - - this.goTo = function(where) { - var row = this.popup.getRow(); - var max = this.popup.session.getLength() - 1 - switch(where) { - case "up": row = row < 0 ? max : row-1; break; - case "down": row = row >= max ? -1 : row+1; break; - case "start": row = 0; break; - case "end": row = max; break - } - this.popup.setRow(row) - }; - - this.insertMatch = function(row) { - if (row == undefined) - row = this.popup.getRow(); - var text = this.completions.filtered[row]; - if (text.value) - text = text.value; - this.editor.insert(text); - this.detach(); - }; - - this.commands = { - "up": function(editor) { editor.autocompleter.goTo("up"); }, - "down": function(editor) { editor.autocompleter.goTo("down"); }, - "ctrl-up": function(editor) { editor.autocompleter.goTo("start"); }, - "ctrl-down": function(editor) { editor.autocompleter.goTo("end"); }, - - "esc": function(editor) { editor.autocompleter.detach(); }, - "Return": function(editor) { editor.autocompleter.insertMatch(); }, - "Shift-Return": function(editor) { editor.autocompleter.insertMatch(true); }, - "Tab": function(editor) {}, - "Shift-Tab": function(editor) {}, - }; - - this.complete = function(editor) { - this.attachToEditor(editor); - var data = this.gatherCompletions(editor); - this.completions = new FilteredList(data); - this.completions.setFilter("a") - if (data) { - if (data.length == 1) - this.insertMatch(0); - else - this.openPopup(editor); - } - }; - - this.gatherCompletions = function() { - return ["asdaf", "foo", "bar", "baz"] - } - - -}).call(Autocompleter.prototype); - -Autocompleter.startCommand = { - name: "startAutocomplete", - exec: function(editor) { - if (!editor.autocompleter) - editor.autocompleter = new Autocompleter(); - editor.autocompleter.complete(editor); - }, - bindKey: "Ctrl-Space|Shift-Space|Alt-Space" -} -Autocompleter.addTo = function(editor) { - editor.commands.addCommand(Autocompleter.startCommand); -} - -var FilteredList = function(array, mutateData) { - this.all = array; - this.filtered = array.concat(); - this.filterText = ""; -}; -(function(){ - this.setFilter = function(str) { - - }; - -}).call(FilteredList.prototype); - -exports.Autocompleter = Autocompleter; -exports.FilteredList = FilteredList; - -}); - diff --git a/lib/ace/autocomplete.js b/lib/ace/autocomplete.js index 25b2d73c..61fd316e 100644 --- a/lib/ace/autocomplete.js +++ b/lib/ace/autocomplete.js @@ -1,174 +1,240 @@ -/* ***** BEGIN LICENSE BLOCK ***** - * Distributed under the BSD license: - * - * Copyright (c) 2012, Ajax.org B.V. - * All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions are met: - * * Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * * Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in the - * documentation and/or other materials provided with the distribution. - * * Neither the name of Ajax.org B.V. nor the - * names of its contributors may be used to endorse or promote products - * derived from this software without specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND - * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED - * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE - * DISCLAIMED. IN NO EVENT SHALL AJAX.ORG B.V. BE LIABLE FOR ANY - * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES - * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; - * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND - * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT - * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS - * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - * - * ***** END LICENSE BLOCK ***** */ - define(function(require, exports, module) { "use strict"; -var oop = require("./lib/oop"); -var Range = require('ace/range').Range; +var dom = require("ace/lib/dom"); +var layout = require("./layout"); +var HashHandler = require("ace/keyboard/hash_handler").HashHandler; +var EditSession = require("ace/edit_session").EditSession; +var TextMode = require("ace/mode/text").Mode; -var Autocomplete = function(editor) { - var self = this; - this.$editor = editor; +var mode = new TextMode(); +mode.$tokenizer = { + getLineTokens: function(line) { + + } +}; +var Autocompleter = function() { + this.keyboardHandler = new HashHandler(); + this.keyboardHandler.bindKeys(this.commands); - this.originalGoLineUp = editor.commands.commands.golineup.exec; - this.originalGoLineDown = editor.commands.commands.golinedown.exec; - this.originalIndent = editor.commands.commands.indent.exec; - this.originalOnTextInput = editor.onTextInput; - - // Create the suggest list - this.autocompleteContainer = document.createElement('div'); - this.autocompleteContainer.className = 'ace_autocomplete'; - - this.selection = this.autocompleteContainer.appendChild(document.createElement("select")); + this.$blurListener = this.blurListener.bind(this); + this.$changeListener = this.changeListener.bind(this); }; - -(function() { - - this.current = function() { - var children = this.selection.childNodes; - for (var i = 0; i < children.length; i++) { - var li = children[i]; - if (li.className == 'ace_autocomplete_selected') { - return li; - } - }; - } - - this.focusNext = function() { - var curr = this.current(); - curr.className = ''; - var focus = curr.nextSibling || curr.parentNode.firstChild; - focus.className = 'ace_autocomplete_selected'; - focus.selected = true; - } - - this.focusPrev = function() { - var curr = this.current(); - curr.className = ''; - var focus = curr.previousSibling || curr.parentNode.lastChild; - focus.className = 'ace_autocomplete_selected'; - focus.selected = true; - } - - this.ensureFocus = function() { - if(!this.current()) { - this.selection.firstChild.className = 'ace_autocomplete_selected'; - } - } - - this.replace = function() { - var _self = this; - - var range = new Range(this.row, this.column, this.row, this.column + 1000); - - var selectedValue; - if (document.all) { - selectedValue = this.current().innerText; - } else { - selectedValue = this.current().textContent; - } - - this.$editor.session.replace(range, selectedValue); - // Deactivate asynchrounously, so that in case of ENTER - we don't reactivate immediately. - setTimeout(function() { - _self.deactivate(); - }, 0); - }; - - this.deactivate = function() { - this.autocompleteContainer.parentNode.removeChild(this.autocompleteContainer); - - this.$editor.commands.commands.golineup.exec = this.originalGoLineUp; - this.$editor.commands.commands.golinedown.exec = this.originalGoLineDown; - this.$editor.commands.commands.indent.exec = this.originalIndent; - this.$editor.onTextInput = this.originalOnTextInput; +(function(){ + this.$init = function(e) { + var el = dom.createElement("div"); + var popup = new layout.singleLineEditor(el); + document.body.appendChild(el); + el.style.width="200px" + el.style.zIndex="20000" + el.style.background="white" + el.style.border="lightgray solid" + el.style.position="fixed" + el.style.display = "none" + popup.renderer.content.style.cursor="default" - this.active = false; - }; - - // Shows the list and reassigns keys - this.activate = function(row, column) { - if (this.active) return; + var nop = function(){}; + + popup.focus = nop; + popup.$isFocused = true; + + popup.renderer.$cursorLayer.restartTimer = nop + popup.renderer.$cursorLayer.update = nop + popup.renderer.$cursorLayer.element.style.display = "none"; - var _self = this; + popup.renderer.maxLines = 6 + popup.renderer.$keepTextAreaAtCursor=false + - this.active = true; - this.row = row; - this.column = column; + popup.setHighlightActiveLine(true) + popup.setSession(new EditSession("")) - // Position the list - var coords = this.$editor.renderer.textToScreenCoordinates(row, column); - this.autocompleteContainer.style.top = coords.pageY + 18 + "px"; - this.autocompleteContainer.style.left = coords.pageX + -2 + "px"; - this.autocompleteContainer.style.display = "block"; + popup.on("mousedown", function(e) { + var pos = e.getDocumentPosition(); + popup.moveCursorToPosition(pos); + popup.selection.clearSelection(); + e.stop(); + }) + + /* popup.session.setMode({ + $ + }) */ - this.$editor.commands.commands.golinedown.exec = function(env, args, request) { _self.focusNext(); }; - this.$editor.commands.commands.golineup.exec = function(env, args, request) { _self.focusPrev(); }; - this.$editor.commands.commands.indent.exec = function(env, args, request) { _self.replace(); }; - - this.$editor.onTextInput = function(text) { - if (text == "\n") { - _self.replace(); - } else { - _self.originalOnTextInput.call(_self.$editor, text); - } + popup.getRow = function() { + var line = this.getCursorPosition().row; + if (line == 0 && !this.getHighlightActiveLine()) + line = -1; + return line; }; + popup.setRow = function(line) { + popup.setHighlightActiveLine(line != -1); + popup.gotoLine(line + 1); + }; + + popup.on("click", function(e) { + this.insertMatch(); + }.bind(this)); + + popup.setData = function(list) { + var value = "" + if (list) { + if (typeof list[0] == "string") + value = list.join("\n"); + else + value = list.map(function(x){return x.value}).join("\n"); + } + + this.setValue(value, -1); + }; + + popup.setHighlight = function(re) { + ace.session.highlight(re) + ace.session._emit("changeFrontMarker") + }; + + this.popup = popup; + }; + + this.openPopup = function(editor) { + if (!this.popup) + this.$init(); + + this.popup.setData(this.completions.filtered) + + var renderer = editor.renderer; + var lineHeight = renderer.layerConfig.lineHeight; + var pos = renderer.$cursorLayer.getPixelPosition(null, true) + var rect = editor.container.getBoundingClientRect() + pos.top += rect.top - renderer.layerConfig.offset; + pos.left += rect.left; + pos.left += renderer.$gutterLayer.gutterWidth; + + var el = this.popup.container; + if (pos.top > window.innerHeight / 2 + lineHeight) { + el.style.top = "" + el.style.bottom = window.innerHeight - pos.top + "px"; + } else { + pos.top += lineHeight; + el.style.top = pos.top + "px"; + el.style.bottom = "" + } + + el.style.left = pos.left + "px"; + el.style.display = ""; + }; + + this.attachToEditor = function(editor) { + if (this.editor) + this.detach(); + this.editor = editor; + if (editor.autocompleter != this) { + if (editor.autocompleter) + editor.autocompleter.detach(); + editor.autocompleter = this; + } + editor.keyBinding.addKeyboardHandler(this.keyboardHandler) + editor.on("changeSelection", this.$changeListener) + editor.on("blur", this.$blurListener) + }; + this.detach = function() { + this.editor.keyBinding.removeKeyboardHandler(this.keyboardHandler); + this.editor.removeEventListener("changeSelection", this.changeListener); + this.editor.removeEventListener("blur", this.changeListener); + this.popup.container.style.display = "none"; + }; + + this.changeListener = function(e) { + console.log(e) }; - // Sets the text the suggest should be based on. - // afterText indicates the position where the suggest box should start. - this.suggest = function(text) { - var options = ["FUNK", "frunk", "blunk", "frunk", "blunk", "frunk", "blunk", "frunk", "blunk", "frunk", "blunk", "frunk", "blunk"]; - if (options.length == 0) { - return deactivate(); - } - - for (var n = 0; n < options.length; n++) { - var opt = this.selection.appendChild(document.createElement("option")); - opt.appendChild(document.createTextNode(options[n])); - } - - this.selection.firstChild.selected = true; - this.selection.size = Math.min(5, options.length); - - document.body.appendChild(this.autocompleteContainer); - this.ensureFocus(); + this.blurListener = function() { + if (document.activeElement != this.editor.textInput.getElement()) + this.detach(); }; - this.isActive = function() { - return this.active; + this.goTo = function(where) { + var row = this.popup.getRow(); + var max = this.popup.session.getLength() - 1 + switch(where) { + case "up": row = row < 0 ? max : row-1; break; + case "down": row = row >= max ? -1 : row+1; break; + case "start": row = 0; break; + case "end": row = max; break + } + this.popup.setRow(row) + }; + + this.insertMatch = function(row) { + if (row == undefined) + row = this.popup.getRow(); + var text = this.completions.filtered[row]; + if (text.value) + text = text.value; + this.editor.insert(text); + this.detach(); + }; + + this.commands = { + "up": function(editor) { editor.autocompleter.goTo("up"); }, + "down": function(editor) { editor.autocompleter.goTo("down"); }, + "ctrl-up": function(editor) { editor.autocompleter.goTo("start"); }, + "ctrl-down": function(editor) { editor.autocompleter.goTo("end"); }, + + "esc": function(editor) { editor.autocompleter.detach(); }, + "Return": function(editor) { editor.autocompleter.insertMatch(); }, + "Shift-Return": function(editor) { editor.autocompleter.insertMatch(true); }, + "Tab": function(editor) {}, + "Shift-Tab": function(editor) {}, + }; + + this.complete = function(editor) { + this.attachToEditor(editor); + var data = this.gatherCompletions(editor); + this.completions = new FilteredList(data); + this.completions.setFilter("a") + if (data) { + if (data.length == 1) + this.insertMatch(0); + else + this.openPopup(editor); + } }; -}).call(Autocomplete.prototype); + this.gatherCompletions = function() { + return ["asdaf", "foo", "bar", "baz"] + } + + +}).call(Autocompleter.prototype); + +Autocompleter.startCommand = { + name: "startAutocomplete", + exec: function(editor) { + if (!editor.autocompleter) + editor.autocompleter = new Autocompleter(); + editor.autocompleter.complete(editor); + }, + bindKey: "Ctrl-Space|Shift-Space|Alt-Space" +} +Autocompleter.addTo = function(editor) { + editor.commands.addCommand(Autocompleter.startCommand); +} + +var FilteredList = function(array, mutateData) { + this.all = array; + this.filtered = array.concat(); + this.filterText = ""; +}; +(function(){ + this.setFilter = function(str) { + + }; + +}).call(FilteredList.prototype); + +exports.Autocompleter = Autocompleter; +exports.FilteredList = FilteredList; -exports.Autocomplete = Autocomplete; }); +