From cd19233b3182e5f74afa83d456d870471d9b5a4c Mon Sep 17 00:00:00 2001 From: Garen Torikian Date: Thu, 3 Jan 2013 23:44:54 -0800 Subject: [PATCH] Improve UX, drop dead code --- lib/ace/autocomplete.js | 41 ++++- lib/ace/autocomplete/autocomplete_worker.js | 10 +- lib/ace/autocomplete/complete_util.js | 22 --- lib/ace/autocomplete/syntax_detector.js | 176 -------------------- lib/ace/autocomplete/text_completer.js | 23 +-- lib/ace/mode/javascript_highlight_rules.js | 2 +- lib/ace/mode/text.js | 41 +++-- lib/ace/multi_select.js | 2 +- tool/mode.tmpl.js | 1 + 9 files changed, 72 insertions(+), 246 deletions(-) delete mode 100644 lib/ace/autocomplete/syntax_detector.js diff --git a/lib/ace/autocomplete.js b/lib/ace/autocomplete.js index e0aa510e..c91cb7c1 100644 --- a/lib/ace/autocomplete.js +++ b/lib/ace/autocomplete.js @@ -55,6 +55,7 @@ var Autocomplete = function() { this.$blurListener = this.blurListener.bind(this); this.$changeListener = this.changeListener.bind(this); + this.$mousedownListener = this.mousedownListener.bind(this); }; @@ -127,8 +128,10 @@ var Autocomplete = function() { }; popup.setHighlight = function(re) { - ace.session.highlight(re) - ace.session._emit("changeFrontMarker") + ace.session.highlight(re); + ace.session._emit("changeFrontMarker"); + // select first item + dom.addCssClass(_self.popup.container.getElementsByClassName("ace_autocomplete")[0].childNodes[0], "autocomplete_selected"); }; this.popup = popup; @@ -166,6 +169,7 @@ var Autocomplete = function() { this.editor.keyBinding.removeKeyboardHandler(this.keyboardHandler); this.editor.removeEventListener("changeSelection", this.changeListener); this.editor.removeEventListener("blur", this.changeListener); + this.editor.removeEventListener("mousedown", this.changeListener); if (this.popup) this.popup.container.style.display = "none"; @@ -179,6 +183,16 @@ var Autocomplete = function() { if (document.activeElement != this.editor.textInput.getElement()) this.detach(); }; + + this.mousedownListener = function(e) { + var mouseX = e.clientX, mouseY = e.clientY; + var newRow = this.editor.renderer.pixelToScreenCoordinates(mouseX, mouseY).row; + var currentRow = e.editor.getCursorPosition().row; + + if (newRow !== currentRow) { + this.detach(); + } + }; this.goTo = function(where) { var row = this.popup.getRow(); @@ -189,8 +203,8 @@ var Autocomplete = function() { dom.removeCssClass(choices[row], "autocomplete_selected"); switch(where) { - case "up": row = row < 0 ? max : row-1; break; - case "down": row = row >= max ? -1 : row+1; break; + case "up": row = row <= 0 ? max : row - 1; break; + case "down": row = row >= max ? 0 : row + 1; break; case "start": row = 0; break; case "end": row = max; break } @@ -224,7 +238,15 @@ var Autocomplete = function() { "Return": function(editor) { editor.Autocomplete.insertMatch(); }, "Shift-Return": function(editor) { editor.Autocomplete.insertMatch(true); }, "Tab": function(editor) { editor.Autocomplete.insertMatch(); }, - "Shift-Tab": function(editor) {}, + "backspace": function(editor) { + var doc = editor.session.getDocument(), + cursor = editor.getCursorPosition(); + + editor.Autocomplete.detach(); + // delete one char, and reevaluate + editor.remove("left"); + editor.Autocomplete.complete(editor); + } }; this.complete = function(editor) { @@ -242,16 +264,17 @@ var Autocomplete = function() { editor.keyBinding.addKeyboardHandler(this.keyboardHandler); editor.on("changeSelection", this.$changeListener); editor.on("blur", this.$blurListener); + editor.on("mousedown", this.$mousedownListener); - worker.attachToDocument(editor.session.getDocument(), {cursor: editor.getCursorPosition(), keywords: editor.session.getMode().$keywordList}, true); + worker.attachToDocument(editor.session.getDocument(), {cursor: editor.getCursorPosition(), keywords: editor.session.getMode().getKeywords(true)}, true); worker.on("complete", function(data) { var matches = data.data.matches; _self.completions = new FilteredList(matches); _self.completions.setFilter("a"); - if (matches.length) - _self.openPopup(editor); + if (matches.length) { + _self.openPopup(editor); } }); }; @@ -328,7 +351,7 @@ var Autocomplete = function() { }; renderer.isScrollableBy=function(){return false}; - renderer.setStyle("ace_one-line"); + renderer.setStyle("ace_autocomplete"); var Editor = require("ace/editor").Editor; var editor = new Editor(renderer); diff --git a/lib/ace/autocomplete/autocomplete_worker.js b/lib/ace/autocomplete/autocomplete_worker.js index 6c5ec0d4..cbcbbc05 100644 --- a/lib/ace/autocomplete/autocomplete_worker.js +++ b/lib/ace/autocomplete/autocomplete_worker.js @@ -34,7 +34,6 @@ define(function(require, exports, module) { var oop = require("../lib/oop"); var Mirror = require("../worker/mirror").Mirror; -var SyntaxDetector = require("./syntax_detector"); var completer = require("./text_completer"); var AutocompleteWorker = exports.AutocompleteWorker = function(sender) { @@ -72,16 +71,13 @@ oop.inherits(AutocompleteWorker, Mirror); this.onUpdate = function() { var _self = this; - var doc = this.doc.getValue(); + var doc = this.doc.getValue(); var pos = this.data.cursor; - var part = SyntaxDetector.getContextSyntaxPart(this.doc, this.data.cursor); - var language = part.language; var currentPos = { line: pos.row, col: pos.column }; - var currentNode = null; - var matches = [], ast = null; + var matches = []; - completer.complete(_self.doc, ast, this.data.cursor, this.data.keywords, currentNode, function(identifier, completions) { + completer.complete(_self.doc, this.data.cursor, this.data.keywords, function(identifier, completions) { if (completions) matches = matches.concat(completions); removeDuplicateMatches(matches); diff --git a/lib/ace/autocomplete/complete_util.js b/lib/ace/autocomplete/complete_util.js index 4b7e6a16..b094fb74 100644 --- a/lib/ace/autocomplete/complete_util.js +++ b/lib/ace/autocomplete/complete_util.js @@ -56,30 +56,8 @@ function findCompletions(prefix, allIdentifiers) { return matches; } -function fetchText(staticPrefix, path) { - var xhr = new XMLHttpRequest(); - xhr.open('GET', staticPrefix + "/" + path, false); - try { - xhr.send(); - } - // Likely we got a cross-script error (equivalent with a 404 in our cloud setup) - catch(e) { - return false; - } - if (xhr.status === 200) - return xhr.responseText; - else - return false; -} - -/** @deprecated Use retrievePrecedingIdentifier */ -exports.retrievePreceedingIdentifier = function() { - console.error("Deprecated: 'retrievePreceedingIdentifier' - use 'retrievePrecedingIdentifier' instead"); - return retrievePrecedingIdentifier.apply(null, arguments); -}; exports.retrievePrecedingIdentifier = retrievePrecedingIdentifier; exports.retrieveFollowingIdentifier = retrieveFollowingIdentifier; exports.findCompletions = findCompletions; -exports.fetchText = fetchText; }); diff --git a/lib/ace/autocomplete/syntax_detector.js b/lib/ace/autocomplete/syntax_detector.js deleted file mode 100644 index 40cfd3f1..00000000 --- a/lib/ace/autocomplete/syntax_detector.js +++ /dev/null @@ -1,176 +0,0 @@ -/** - * Cloud9 Language Foundation - * - * @copyright 2011, Ajax.org B.V. - * @license GPLv3 - */ -define(function(require, exports, module) { - -var mixedLanguages = { - php: { - "default": "html", - "php-start": /<\?(?:php|\=)?/, - "php-end": /\?>/, - "css-start": /]*>/, - "css-end": /<\/style>/, - "javascript-start": /\/])*>/, - "javascript-end": /<\/script>/ - }, - html: { - "css-start": /]*>/, - "css-end": /<\/style>/, - "javascript-start": /\/])*>/, - "javascript-end": /<\/script>/ - } -}; - -/* Now: - * - One level syntax nesting supported - * Future: (if worth it) - * - Have a stack to repesent it - * - Maintain a syntax tree for an opened file - */ -function getSyntaxRegions(doc, originalSyntax) { - if (! mixedLanguages[originalSyntax]) - return [{ - syntax: originalSyntax, - sl: 0, - sc: 0, - el: doc.getLength()-1, - ec: doc.getLine(doc.getLength()-1).length - }]; - - var lines = doc.getAllLines(); - var type = mixedLanguages[originalSyntax]; - var defaultSyntax = type["default"] || originalSyntax; - var starters = Object.keys(type).filter(function (m) { - return m.indexOf("-start") === m.length - 6; - }); - var syntax = defaultSyntax; - var regions = [{syntax: syntax, sl: 0, sc: 0}]; - var starter, endLang; - var tempS, tempM; - var i, m, cut, inLine = 0; - - for (var row = 0; row < lines.length; row++) { - var line = lines[row]; - m = null; - if (endLang) { - m = endLang.exec(line); - if (m) { - endLang = null; - syntax = defaultSyntax; - regions[regions.length-1].el = row; - regions[regions.length-1].ec = m.index + inLine; - regions.push({ - syntax: syntax, - sl: row, - sc: m.index + inLine - }); - cut = m.index + m[0].length; - lines[row] = line.substring(cut); - inLine += cut; - row--; // continue processing of the line - } - else { - inLine = 0; - } - } - else { - for (i = 0; i < starters.length; i++) { - tempS = starters[i]; - tempM = type[tempS].exec(line); - if (tempM && (!m || m.index > tempM.index)) { - m = tempM; - starter = tempS; - } - } - if (m) { - syntax = starter.replace("-start", ""); - endLang = type[syntax+"-end"]; - regions[regions.length-1].el = row; - regions[regions.length-1].ec = inLine + m.index + m[0].length; - regions.push({ - syntax: syntax, - sl: row, - sc: inLine + m.index + m[0].length - }); - cut = m.index + m[0].length; - lines[row] = line.substring(m.index + m[0].length); - row--; // continue processing of the line - inLine += cut; - } - else { - inLine = 0; - } - } - } - regions[regions.length-1].el = lines.length; - regions[regions.length-1].ec = lines[lines.length-1].length; - return regions; -} - -function getContextSyntaxPart(doc, pos, originalSyntax) { - if (! mixedLanguages[originalSyntax]) - return { - language: originalSyntax, - value: doc.getValue(), - region: getSyntaxRegions(doc, originalSyntax)[0], - index: 0 - }; - var regions = getSyntaxRegions(doc, originalSyntax); - for (var i = 0; i < regions.length; i++) { - var region = regions[i]; - if ((pos.row > region.sl && pos.row < region.el) || - (pos.row === region.sl && pos.column >= region.sc) || - (pos.row === region.el && pos.column <= region.ec)) - return regionToCodePart(doc, region, i); - } - return null; // should never happen -} - -function getContextSyntax(doc, pos, originalSyntax) { - var part = getContextSyntaxPart(doc, pos, originalSyntax); - return part && part.language; // should never happen -} - -function regionToCodePart (doc, region, index) { - var lines = doc.getLines(region.sl, region.el); - return { - value: region.sl === region.el ? lines[0].substring(region.sc, region.ec) : - [lines[0].substring(region.sc)].concat(lines.slice(1, lines.length-1)).concat([lines[lines.length-1].substring(0, region.ec)]).join(doc.getNewLineCharacter()), - language: region.syntax, - region: region, - index: index - }; -} - -function getCodeParts (doc, originalSyntax) { - var regions = getSyntaxRegions(doc, originalSyntax); - return regions.map(function (region, i) { - return regionToCodePart(doc, region, i); - }); -} - -function posToRegion (region, pos) { - return { - row: pos.row - region.sl, - column: pos.column - }; -} - -function regionToPos (region, pos) { - return { - row: pos.row + region.sl, - column: pos.column - }; -} - -exports.getContextSyntax = getContextSyntax; -exports.getContextSyntaxPart = getContextSyntaxPart; -exports.getSyntaxRegions = getSyntaxRegions; -exports.getCodeParts = getCodeParts; -exports.posToRegion = posToRegion; -exports.regionToPos = regionToPos; - -}); diff --git a/lib/ace/autocomplete/text_completer.js b/lib/ace/autocomplete/text_completer.js index 0cef7aed..44a72c9b 100644 --- a/lib/ace/autocomplete/text_completer.js +++ b/lib/ace/autocomplete/text_completer.js @@ -4,10 +4,6 @@ define(function(require, exports, module) { var MAX_SCORE = 1000000; var completer = module.exports; - - this.handlesLanguage = function(language) { - return true; - }; // For the current document, gives scores to identifiers not on frequency, but on distance from the current prefix function wordDistanceAnalyzer(doc, pos, prefix, keywords) { @@ -42,6 +38,7 @@ define(function(require, exports, module) { for (var k = 0, l = keywords.length; k < l; k++) { identDict[keywords[k]] = MAX_SCORE; } + return identDict; } @@ -53,30 +50,24 @@ define(function(require, exports, module) { return analysisCache; } - completer.complete = function(doc, fullAst, pos, keywords, currentNode, callback) { - var identDict = analyze(doc, pos, keywords); + completer.complete = function(doc, pos, keywords, callback) { var line = doc.getLine(pos.row); var identifier = completeUtil.retrievePrecedingIdentifier(line, pos.column); + // there's nothing to autocomplete if (identifier === "") return callback(null); + var identDict = analyze(doc, pos, keywords); + var allIdentifiers = []; for (var ident in identDict) { allIdentifiers.push(ident); } + + // find matches based on text in doc var matches = completeUtil.findCompletions(identifier, allIdentifiers); callback(identifier, matches); - /*callback(matches.map(function(m) { - return { - name : m, - replaceText : m, - icon : null, - score : identDict[m], - meta : "", - priority : 1 - }; - }));*/ }; }); \ No newline at end of file diff --git a/lib/ace/mode/javascript_highlight_rules.js b/lib/ace/mode/javascript_highlight_rules.js index 638ef6b3..3984f2c3 100644 --- a/lib/ace/mode/javascript_highlight_rules.js +++ b/lib/ace/mode/javascript_highlight_rules.js @@ -168,7 +168,7 @@ var JavaScriptHighlightRules = function() { next : "start" }, { token : ["punctuation.operator", "support.function"], - regex : /(\.)(s(?:h(?:ift|ow(?:Mod(?:elessDialog|alDialog)|Help))|croll(?:X|By(?:Pages|Lines)?|Y|To)?|t(?:opzzzz|rike)|i(?:n|zeToContent|debar|gnText)|ort|u(?:p|b(?:str(?:ing)?)?)|pli(?:ce|t)|e(?:nd|t(?:Re(?:sizable|questHeader)|M(?:i(?:nutes|lliseconds)|onth)|Seconds|Ho(?:tKeys|urs)|Year|Cursor|Time(?:out)?|Interval|ZOptions|Date|UTC(?:M(?:i(?:nutes|lliseconds)|onth)|Seconds|Hours|Date|FullYear)|FullYear|Active)|arch)|qrt|lice|avePreferences|mall)|h(?:ome|andleEvent)|navigate|c(?:har(?:CodeAt|At)|o(?:s|n(?:cat|textual|firm)|mpile)|eil|lear(?:Timeout|Interval)?|a(?:ptureEvents|ll)|reate(?:StyleSheet|Popup|EventObject))|t(?:o(?:GMTString|S(?:tring|ource)|U(?:TCString|pperCase)|Lo(?:caleString|werCase))|est|a(?:n|int(?:Enabled)?))|i(?:s(?:NaN|Finite)|ndexOf|talics)|d(?:isableExternalCapture|ump|etachEvent)|u(?:n(?:shift|taint|escape|watch)|pdateCommands)|j(?:oin|avaEnabled)|p(?:o(?:p|w)|ush|lugins.refresh|a(?:ddings|rse(?:Int|Float)?)|r(?:int|ompt|eference))|e(?:scape|nableExternalCapture|val|lementFromPoint|x(?:p|ec(?:Script|Command)?))|valueOf|UTC|queryCommand(?:State|Indeterm|Enabled|Value)|f(?:i(?:nd|le(?:ModifiedDate|Size|CreatedDate|UpdatedDate)|xed)|o(?:nt(?:size|color)|rward)|loor|romCharCode)|watch|l(?:ink|o(?:ad|g)|astIndexOf)|a(?:sin|nchor|cos|t(?:tachEvent|ob|an(?:2)?)|pply|lert|b(?:s|ort))|r(?:ou(?:nd|teEvents)|e(?:size(?:By|To)|calc|turnValue|place|verse|l(?:oad|ease(?:Capture|Events)))|andom)|g(?:o|et(?:ResponseHeader|M(?:i(?:nutes|lliseconds)|onth)|Se(?:conds|lection)|Hours|Year|Time(?:zoneOffset)?|Da(?:y|te)|UTC(?:M(?:i(?:nutes|lliseconds)|onth)|Seconds|Hours|Da(?:y|te)|FullYear)|FullYear|A(?:ttention|llResponseHeaders)))|m(?:in|ove(?:B(?:y|elow)|To(?:Absolute)?|Above)|ergeAttributes|a(?:tch|rgins|x))|b(?:toa|ig|o(?:ld|rderWidths)|link|ack))\b(?=\()/ + regex : /(\.)(s(?:h(?:ift|ow(?:Mod(?:elessDialog|alDialog)|Help))|croll(?:X|By(?:Pages|Lines)?|Y|To)?|t(?:op|rike)|i(?:n|zeToContent|debar|gnText)|ort|u(?:p|b(?:str(?:ing)?)?)|pli(?:ce|t)|e(?:nd|t(?:Re(?:sizable|questHeader)|M(?:i(?:nutes|lliseconds)|onth)|Seconds|Ho(?:tKeys|urs)|Year|Cursor|Time(?:out)?|Interval|ZOptions|Date|UTC(?:M(?:i(?:nutes|lliseconds)|onth)|Seconds|Hours|Date|FullYear)|FullYear|Active)|arch)|qrt|lice|avePreferences|mall)|h(?:ome|andleEvent)|navigate|c(?:har(?:CodeAt|At)|o(?:s|n(?:cat|textual|firm)|mpile)|eil|lear(?:Timeout|Interval)?|a(?:ptureEvents|ll)|reate(?:StyleSheet|Popup|EventObject))|t(?:o(?:GMTString|S(?:tring|ource)|U(?:TCString|pperCase)|Lo(?:caleString|werCase))|est|a(?:n|int(?:Enabled)?))|i(?:s(?:NaN|Finite)|ndexOf|talics)|d(?:isableExternalCapture|ump|etachEvent)|u(?:n(?:shift|taint|escape|watch)|pdateCommands)|j(?:oin|avaEnabled)|p(?:o(?:p|w)|ush|lugins.refresh|a(?:ddings|rse(?:Int|Float)?)|r(?:int|ompt|eference))|e(?:scape|nableExternalCapture|val|lementFromPoint|x(?:p|ec(?:Script|Command)?))|valueOf|UTC|queryCommand(?:State|Indeterm|Enabled|Value)|f(?:i(?:nd|le(?:ModifiedDate|Size|CreatedDate|UpdatedDate)|xed)|o(?:nt(?:size|color)|rward)|loor|romCharCode)|watch|l(?:ink|o(?:ad|g)|astIndexOf)|a(?:sin|nchor|cos|t(?:tachEvent|ob|an(?:2)?)|pply|lert|b(?:s|ort))|r(?:ou(?:nd|teEvents)|e(?:size(?:By|To)|calc|turnValue|place|verse|l(?:oad|ease(?:Capture|Events)))|andom)|g(?:o|et(?:ResponseHeader|M(?:i(?:nutes|lliseconds)|onth)|Se(?:conds|lection)|Hours|Year|Time(?:zoneOffset)?|Da(?:y|te)|UTC(?:M(?:i(?:nutes|lliseconds)|onth)|Seconds|Hours|Da(?:y|te)|FullYear)|FullYear|A(?:ttention|llResponseHeaders)))|m(?:in|ove(?:B(?:y|elow)|To(?:Absolute)?|Above)|ergeAttributes|a(?:tch|rgins|x))|b(?:toa|ig|o(?:ld|rderWidths)|link|ack))\b(?=\()/ }, { token : ["punctuation.operator", "support.function.dom"], regex : /(\.)(s(?:ub(?:stringData|mit)|plitText|e(?:t(?:NamedItem|Attribute(?:Node)?)|lect))|has(?:ChildNodes|Feature)|namedItem|c(?:l(?:ick|o(?:se|neNode))|reate(?:C(?:omment|DATASection|aption)|T(?:Head|extNode|Foot)|DocumentFragment|ProcessingInstruction|E(?:ntityReference|lement)|Attribute))|tabIndex|i(?:nsert(?:Row|Before|Cell|Data)|tem)|open|delete(?:Row|C(?:ell|aption)|T(?:Head|Foot)|Data)|focus|write(?:ln)?|a(?:dd|ppend(?:Child|Data))|re(?:set|place(?:Child|Data)|move(?:NamedItem|Child|Attribute(?:Node)?)?)|get(?:NamedItem|Element(?:sBy(?:Name|TagName)|ById)|Attribute(?:Node)?)|blur)\b(?=\()/ diff --git a/lib/ace/mode/text.js b/lib/ace/mode/text.js index 6763c602..6bf36018 100644 --- a/lib/ace/mode/text.js +++ b/lib/ace/mode/text.js @@ -326,22 +326,35 @@ var Mode = function() { } }; - this.getKeywords = function() { - var rules = this.$tokenizer.rules; - var keywords = []; - for (var rule in rules) { - var ruleItr = rules[rule]; - for (var r = 0, l = ruleItr.length; r < l; r++) { - if (typeof ruleItr[r].token === "string") { - if (/keyword/.test(ruleItr[r].token)) - keywords.push(ruleItr[r].regex); - else if (/support/.test(ruleItr[r].token)) - keywords.push(ruleItr[r].regex); - } + this.getKeywords = function(append) { + // this is for autocompletion to pick up regexp'ed keywords + if (!this.completionKeywords) { + var rules = this.$tokenizer.rules; + var completionKeywords = []; + for (var rule in rules) { + var ruleItr = rules[rule]; + for (var r = 0, l = ruleItr.length; r < l; r++) { + if (typeof ruleItr[r].token === "string") { + if (/keyword|support|storage/.test(ruleItr[r].token)) + completionKeywords.push(ruleItr[r].regex); + } + else if (typeof ruleItr[r].token === "object") { + for (var a = 0, aLength = ruleItr[r].token.length; a < aLength; a++) { + if (/keyword|support|storage/.test(ruleItr[r].token[a])) { + // drop surrounding parens + var rule = ruleItr[r].regex.match(/\(.+?\)/g)[a]; + completionKeywords.push(rule.substr(1, rule.length - 2)); + } + } + } + } } + this.completionKeywords = completionKeywords; } - - return keywords.concat(this.$keywordList || []); + // this is for highlighting embed rules, like HAML/Ruby or Obj-C/C + if (!append) + return this.$keywordList; + return completionKeywords.concat(this.$keywordList || []); }; }).call(Mode.prototype); diff --git a/lib/ace/multi_select.js b/lib/ace/multi_select.js index 2de7fc1d..41ca9b89 100644 --- a/lib/ace/multi_select.js +++ b/lib/ace/multi_select.js @@ -374,7 +374,7 @@ var Editor = require("./editor").Editor; /** * Removes the selection marker. - * @param {Range} The selection range added with [[Editor.addSelectionMarker `addSelectionMarker()`]]. + * @param {Range} range The selection range added with [[Editor.addSelectionMarker `addSelectionMarker()`]]. * @method Editor.removeSelectionMarker **/ this.removeSelectionMarker = function(range) { diff --git a/tool/mode.tmpl.js b/tool/mode.tmpl.js index 054cb7e6..bdf8065e 100644 --- a/tool/mode.tmpl.js +++ b/tool/mode.tmpl.js @@ -51,6 +51,7 @@ var Mode = function() { var highlighter = new %language%HighlightRules(); this.foldingRules = new FoldMode(); this.$tokenizer = new Tokenizer(highlighter.getRules()); + this.$keywordList = highlighter.$keywordList; }; oop.inherits(Mode, TextMode);