From ca1c27aa651eac75ec28de6fd77f9ecf47f8e76b Mon Sep 17 00:00:00 2001 From: nightwing Date: Fri, 26 Jul 2013 14:55:08 +0400 Subject: [PATCH 1/6] fix #1533 syntax highlighting of markdown links --- lib/ace/mode/_test/text_markdown.txt | 2 +- lib/ace/mode/_test/tokens_markdown.json | 21 ++++++++++++++++++++- lib/ace/mode/markdown_highlight_rules.js | 19 ++++++++++++------- 3 files changed, 33 insertions(+), 9 deletions(-) diff --git a/lib/ace/mode/_test/text_markdown.txt b/lib/ace/mode/_test/text_markdown.txt index ecaa01b9..0450c820 100644 --- a/lib/ace/mode/_test/text_markdown.txt +++ b/lib/ace/mode/_test/text_markdown.txt @@ -14,7 +14,7 @@ test: only space between #s is not a valid header # # # test links [Cloud9 IDE](http://www.c9.io/) # -* [demo](http://ajaxorg.github.com/ace/) +* [demo](http://ajaxorg.github.com/ace/) [+](escape(\) ) [+](a "title") [+](a "space" ) * usually *work* fine (_em_) in lists diff --git a/lib/ace/mode/_test/tokens_markdown.json b/lib/ace/mode/_test/tokens_markdown.json index 37edf183..3c0db62f 100644 --- a/lib/ace/mode/_test/tokens_markdown.json +++ b/lib/ace/mode/_test/tokens_markdown.json @@ -64,7 +64,26 @@ ["text","]("], ["markup.underline","http://ajaxorg.github.com/ace/"], ["text",")"], - ["list"," "] + ["list"," "], + ["text","["], + ["string","+"], + ["text","]("], + ["markup.underline","escape(\\) "], + ["text",")"], + ["list"," "], + ["text","["], + ["string","+"], + ["text","]("], + ["markup.underline","a"], + ["string"," \"title\""], + ["text",")"], + ["list"," "], + ["text","["], + ["string","+"], + ["text","]("], + ["markup.underline","a"], + ["string"," \"space\" "], + ["text",")"] ],[ "listblock", ["markup.list","* "], diff --git a/lib/ace/mode/markdown_highlight_rules.js b/lib/ace/mode/markdown_highlight_rules.js index cf1457c1..4e07edff 100644 --- a/lib/ace/mode/markdown_highlight_rules.js +++ b/lib/ace/mode/markdown_highlight_rules.js @@ -32,12 +32,17 @@ define(function(require, exports, module) { "use strict"; var oop = require("../lib/oop"); +var lang = require("../lib/lang"); var TextHighlightRules = require("./text_highlight_rules").TextHighlightRules; var JavaScriptHighlightRules = require("./javascript_highlight_rules").JavaScriptHighlightRules; var XmlHighlightRules = require("./xml_highlight_rules").XmlHighlightRules; var HtmlHighlightRules = require("./html_highlight_rules").HtmlHighlightRules; var CssHighlightRules = require("./css_highlight_rules").CssHighlightRules; +var escaped = function(ch) { + return "(?:[^" + lang.escapeRegExp(ch) + "\\\\]|\\\\.)*"; +} + function github_embed(tag, prefix) { return { // Github style block token : "support.function", @@ -104,15 +109,15 @@ var MarkdownHighlightRules = function() { regex : "^([ ]{0,3}\\[)([^\\]]+)(\\]:\\s*)([^ ]+)(\\s*(?:[\"][^\"]+[\"])?(\\s*))$" }, { // link by reference token : ["text", "string", "text", "constant", "text"], - regex : "(\\[)((?:[[^\\]]*\\]|[^\\[\\]])*)(\\][ ]?(?:\\n[ ]*)?\\[)(.*?)(\\])" + regex : "(\\[)(" + escaped("]") + ")(\\]\s*\\[)("+ escaped("]") + ")(\\])" }, { // link by url token : ["text", "string", "text", "markup.underline", "string", "text"], - regex : "(\\[)"+ - "(\\[[^\\]]*\\]|[^\\[\\]]*)"+ - "(\\]\\([ \\t]*)"+ - "(?)"+ - "((?:[ \t]*\"(?:.*?)\"[ \\t]*)?)"+ - "(\\))" + regex : "(\\[)(" + // [ + escaped("]") + // link text + ")(\\]\\()"+ // ]( + '((?:[^\\)\\s\\\\]|\\\\.|\\s(?=[^"]))*)' + // href + '(\\s*"' + escaped('"') + '"\\s*)?' + // "title" + "(\\))" // ) }, { // strong ** __ token : "string", regex : "([*]{2}|[_]{2}(?=\\S))(.*?\\S[*_]*)(\\1)" From 4d9118e6fe009d480e0984d6b72f1cb88d4ce654 Mon Sep 17 00:00:00 2001 From: nightwing Date: Fri, 26 Jul 2013 15:10:59 +0400 Subject: [PATCH 2/6] add emmet demo --- demo/autocompletion.html | 44 +++++++++++++++++++++ demo/emmet.html | 43 ++++++++++++++++++++ demo/keyboard_shortcuts.html | 2 +- demo/modelist.html | 2 +- demo/settings_menu.html | 2 +- demo/{demo_helper.js => show_own_source.js} | 0 demo/statusbar.html | 2 +- 7 files changed, 91 insertions(+), 4 deletions(-) create mode 100644 demo/autocompletion.html create mode 100644 demo/emmet.html rename demo/{demo_helper.js => show_own_source.js} (100%) diff --git a/demo/autocompletion.html b/demo/autocompletion.html new file mode 100644 index 00000000..9ebb1678 --- /dev/null +++ b/demo/autocompletion.html @@ -0,0 +1,44 @@ + + + + + ACE Autocompletion demo + + + + +

+
+
+
+
+
+
+
+
+
+
diff --git a/demo/emmet.html b/demo/emmet.html
new file mode 100644
index 00000000..65b59bf7
--- /dev/null
+++ b/demo/emmet.html
@@ -0,0 +1,43 @@
+
+
+
+  
+  ACE Emmet demo
+  
+
+
+
+

+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/demo/keyboard_shortcuts.html b/demo/keyboard_shortcuts.html
index 4a579d37..988e1918 100644
--- a/demo/keyboard_shortcuts.html
+++ b/demo/keyboard_shortcuts.html
@@ -41,6 +41,6 @@
     })
 
 
-
+
 
 
diff --git a/demo/modelist.html b/demo/modelist.html
index 659e3805..d7f525fa 100644
--- a/demo/modelist.html
+++ b/demo/modelist.html
@@ -43,6 +43,6 @@
     
 
 
-
+
 
 
diff --git a/demo/settings_menu.html b/demo/settings_menu.html
index cc453a53..7eaf0e42 100644
--- a/demo/settings_menu.html
+++ b/demo/settings_menu.html
@@ -40,6 +40,6 @@
 	}]);
 
 
-
+
 
 
diff --git a/demo/demo_helper.js b/demo/show_own_source.js
similarity index 100%
rename from demo/demo_helper.js
rename to demo/show_own_source.js
diff --git a/demo/statusbar.html b/demo/statusbar.html
index 9fc6583c..47341d3b 100644
--- a/demo/statusbar.html
+++ b/demo/statusbar.html
@@ -52,6 +52,6 @@
     editor.session.setMode("ace/mode/html");
 
 
-
+
 
 

From 524930a2c78e959e5c155fc297d7dc104613f657 Mon Sep 17 00:00:00 2001
From: nightwing 
Date: Fri, 26 Jul 2013 23:17:12 +0400
Subject: [PATCH 3/6] force measure sizes if container is visible but width is
 0

---
 lib/ace/virtual_renderer.js | 2 ++
 1 file changed, 2 insertions(+)

diff --git a/lib/ace/virtual_renderer.js b/lib/ace/virtual_renderer.js
index 9c1efb3b..1ba49cb3 100644
--- a/lib/ace/virtual_renderer.js
+++ b/lib/ace/virtual_renderer.js
@@ -738,6 +738,8 @@ var VirtualRenderer = function(container, theme) {
             changes |= this.$changes;
             this.$changes = 0;
         }
+        if (!this.$size.width)
+            return this.onResize(true);
             
         // this.$logChanges(changes);
         

From d4dcdb7736d5c8d1d98e68be7e8e91e2193d6c3d Mon Sep 17 00:00:00 2001
From: nightwing 
Date: Sat, 27 Jul 2013 00:45:08 +0400
Subject: [PATCH 4/6] use snipetManager for expanding emmet snippets

---
 Makefile.dryice.js   |  30 ++++++-------
 demo/emmet.html      |   1 -
 lib/ace/ext/emmet.js | 104 +++++++++++++++++++++++++++----------------
 lib/ace/snippets.js  |  19 +++++---
 4 files changed, 93 insertions(+), 61 deletions(-)

diff --git a/Makefile.dryice.js b/Makefile.dryice.js
index 09e4eca9..14ffea23 100755
--- a/Makefile.dryice.js
+++ b/Makefile.dryice.js
@@ -346,6 +346,21 @@ var buildAce = function(options) {
         dest:   targetDir + '/' + name + ".js"
     });
 
+    console.log('# ace extensions ---------');
+
+    project.assumeAllFilesLoaded();
+    options.extensions.forEach(function(ext) {
+        console.log("extensions " + ext);
+        copy({
+            source: [{
+                project: cloneProject(project),
+                require: [ 'ace/ext/' + ext ]
+            }],
+            filter: getWriteFilters(options, "ext"),
+            dest:   targetDir + "/ext-" + ext + ".js"
+        });
+    });
+
     console.log('# ace modes ---------');
 
     project.assumeAllFilesLoaded();
@@ -380,21 +395,6 @@ var buildAce = function(options) {
         });
     });
 
-    console.log('# ace extensions ---------');
-
-    project.assumeAllFilesLoaded();
-    options.extensions.forEach(function(ext) {
-        console.log("extensions " + ext);
-        copy({
-            source: [{
-                project: cloneProject(project),
-                require: [ 'ace/ext/' + ext ]
-            }],
-            filter: getWriteFilters(options, "ext"),
-            dest:   targetDir + "/ext-" + ext + ".js"
-        });
-    });
-
     console.log('# ace key bindings ---------');
 
     // copy key bindings
diff --git a/demo/emmet.html b/demo/emmet.html
index 65b59bf7..57f13f67 100644
--- a/demo/emmet.html
+++ b/demo/emmet.html
@@ -32,7 +32,6 @@
     // trigger extension
     ace.require("ace/ext/emmet");
     var editor = ace.edit("editor");
-    editor.setTheme("ace/theme/tomorrow");
     editor.session.setMode("ace/mode/html");
     // enable emmet on the current editor
     editor.setOption("enableEmmet", true);    
diff --git a/lib/ace/ext/emmet.js b/lib/ace/ext/emmet.js
index d6c141bf..5c80ee47 100644
--- a/lib/ace/ext/emmet.js
+++ b/lib/ace/ext/emmet.js
@@ -32,6 +32,8 @@ define(function(require, exports, module) {
 "use strict";
 var HashHandler = require("ace/keyboard/hash_handler").HashHandler;
 var Editor = require("ace/editor").Editor;
+var snippetManager = require("ace/snippets").snippetManager;
+var Range = require("ace/range").Range;
 var emmet;
 
 Editor.prototype.indexToPosition = function(index) {
@@ -165,43 +167,17 @@ AceEmmetEditor.prototype = {
         if (end == null)
             end = start == null ? this.getContent().length : start;
         if (start == null)
-            start = 0;
-        var utils = emmet.require("utils");
-
-        // indent new value
-        if (!noIndent) {
-            value = utils.padString(value, utils.getLinePaddingFromPosition(this.getContent(), start));
-        }
-
-        // find new caret position
-        var tabstopData = emmet.require("tabStops").extract(value, {
-            escape: function(ch) {
-                return ch;
-            }
-        });
-
-        value = tabstopData.text;
-        var firstTabStop = tabstopData.tabstops[0];
-
-        if (firstTabStop) {
-            firstTabStop.start += start;
-            firstTabStop.end += start;
-        } else {
-            firstTabStop = {
-                start: value.length + start,
-                end: value.length + start
-            };
-        }
-
-        var range = this.ace.getSelectionRange();
-        range.start = this.ace.indexToPosition(start);
-        range.end = this.ace.indexToPosition(end);
-
-        this.ace.session.replace(range, value);
-
-        range.start = this.ace.indexToPosition(firstTabStop.start);
-        range.end = this.ace.indexToPosition(firstTabStop.end);
-        this.ace.selection.setRange(range);
+            start = 0;        
+        
+        var editor = this.ace;
+        var range = Range.fromPoints(editor.indexToPosition(start), editor.indexToPosition(end));
+        editor.session.remove(range);
+        
+        range.end = range.start;
+        //editor.selection.setRange(range);
+        
+        value = this.$updateTabstops(value);
+        snippetManager.insertSnippet(editor, value)
     },
 
     /**
@@ -282,6 +258,58 @@ AceEmmetEditor.prototype = {
      */
     getFilePath: function() {
         return "";
+    },
+    
+    // update tabstops: make sure all caret placeholders are unique
+    // by default, abbreviation parser generates all unlinked (un-mirrored)
+    // tabstops as ${0}, so we have upgrade all caret tabstops with unique
+    // positions but make sure that all other tabstops are not linked accidentally
+    $updateTabstops: function(value) {
+        var base = 1000;
+        var zeroBase = 0;
+        var lastZero = null;
+        var range = emmet.require('range');
+        var ts = emmet.require('tabStops');
+
+        var tabstopOptions = {
+            tabstop: function(data) {
+                var group = parseInt(data.group, 10);
+                var isZero = group === 0;
+                if (isZero)
+                    group = ++zeroBase;
+                else
+                    group += base;
+
+                var placeholder = data.placeholder;
+                if (placeholder) {
+                    // recursively update nested tabstops
+                    placeholder = ts.processText(placeholder, tabstopOptions);
+                }
+
+                var result = '${' + group + (placeholder ? ':' + placeholder : '') + '}';
+
+                if (isZero) {
+                    lastZero = range.create(data.start, result);
+                }
+
+                return result
+            },
+            escape: function(ch) {
+                if (ch == '$') return '\\$';
+                if (ch == '\\') return '\\\\';
+                return ch;
+            }
+        };
+
+        value = ts.processText(value, tabstopOptions);
+
+        if (/*sublimeGetOption('insert_final_tabstop', false)*/ false && !/\$\{0\}$/.test(value)) {
+            value += '${0}';
+        } else if (lastZero) {
+            value = emmet.require('utils').replaceSubstring(value, '${0}', lastZero);
+        }
+        
+        return value;
     }
 };
 
@@ -359,7 +387,7 @@ var onChangeMode = function(e, target) {
     if (!editor)
         return;
     var modeId = editor.session.$modeId;
-    var enabled = modeId && /css|less|sass|html|php/.test(modeId);
+    var enabled = modeId && /css|less|scss|sass|stylus|html|php/.test(modeId);
     if (e.enableEmmet === false)
         enabled = false;
     if (enabled)
diff --git a/lib/ace/snippets.js b/lib/ace/snippets.js
index cf5ac307..6e11a932 100644
--- a/lib/ace/snippets.js
+++ b/lib/ace/snippets.js
@@ -675,15 +675,20 @@ var TabstopManager = function(editor) {
         ts = this.tabstops[this.index];
         if (!ts || !ts.length)
             return;
-
+        
         this.selectedTabstop = ts;
-        var sel = this.editor.multiSelect;
-        sel.toSingleRange(ts.firstNonLinked.clone());
-        for (var i = ts.length; i--;) {
-            if (ts.hasLinkedRanges && ts[i].linked)
-                continue;
-            sel.addRange(ts[i].clone(), true);
+        if (!this.editor.inVirtualSelectionMode) {        
+            var sel = this.editor.multiSelect;
+            sel.toSingleRange(ts.firstNonLinked.clone());
+            for (var i = ts.length; i--;) {
+                if (ts.hasLinkedRanges && ts[i].linked)
+                    continue;
+                sel.addRange(ts[i].clone(), true);
+            }
+        } else {
+            this.editor.selection.setRange(ts.firstNonLinked);
         }
+        
         this.editor.keyBinding.addKeyboardHandler(this.keyboardHandler);
     };
     this.addTabstops = function(tabstops, start, end) {

From d3cd46adff5fea5f51bf2515dd2aff41b4811f7e Mon Sep 17 00:00:00 2001
From: nightwing 
Date: Tue, 30 Jul 2013 22:10:59 +0400
Subject: [PATCH 5/6] fix $# and %# highlighting in perl mode

---
 lib/ace/mode/perl_highlight_rules.js | 8 ++++----
 1 file changed, 4 insertions(+), 4 deletions(-)

diff --git a/lib/ace/mode/perl_highlight_rules.js b/lib/ace/mode/perl_highlight_rules.js
index 046ecb7b..7c183fe3 100644
--- a/lib/ace/mode/perl_highlight_rules.js
+++ b/lib/ace/mode/perl_highlight_rules.js
@@ -80,9 +80,6 @@ var PerlHighlightRules = function() {
     this.$rules = {
         "start" : [
             {
-                token : "comment",
-                regex : "#.*$"
-            }, {
                 token : "comment.doc",
                 regex : "^=(?:begin|item)\\b",
                 next : "block_comment"
@@ -114,7 +111,10 @@ var PerlHighlightRules = function() {
                 regex : "[a-zA-Z_$][a-zA-Z0-9_$]*\\b"
             }, {
                 token : "keyword.operator",
-                regex : "\\.\\.\\.|\\|\\|=|>>=|<<=|<=>|&&=|=>|!~|\\^=|&=|\\|=|\\.=|x=|%=|\\/=|\\*=|\\-=|\\+=|=~|\\*\\*|\\-\\-|\\.\\.|\\|\\||&&|\\+\\+|\\->|!=|==|>=|<=|>>|<<|,|=|\\?\\:|\\^|\\||x|%|\\/|\\*|<|&|\\\\|~|!|>|\\.|\\-|\\+|\\-C|\\-b|\\-S|\\-u|\\-t|\\-p|\\-l|\\-d|\\-f|\\-g|\\-s|\\-z|\\-k|\\-e|\\-O|\\-T|\\-B|\\-M|\\-A|\\-X|\\-W|\\-c|\\-R|\\-o|\\-x|\\-w|\\-r|\\b(?:and|cmp|eq|ge|gt|le|lt|ne|not|or|xor)"
+                regex : "%#|\\$#|\\.\\.\\.|\\|\\|=|>>=|<<=|<=>|&&=|=>|!~|\\^=|&=|\\|=|\\.=|x=|%=|\\/=|\\*=|\\-=|\\+=|=~|\\*\\*|\\-\\-|\\.\\.|\\|\\||&&|\\+\\+|\\->|!=|==|>=|<=|>>|<<|,|=|\\?\\:|\\^|\\||x|%|\\/|\\*|<|&|\\\\|~|!|>|\\.|\\-|\\+|\\-C|\\-b|\\-S|\\-u|\\-t|\\-p|\\-l|\\-d|\\-f|\\-g|\\-s|\\-z|\\-k|\\-e|\\-O|\\-T|\\-B|\\-M|\\-A|\\-X|\\-W|\\-c|\\-R|\\-o|\\-x|\\-w|\\-r|\\b(?:and|cmp|eq|ge|gt|le|lt|ne|not|or|xor)"
+            }, {
+                token : "comment",
+                regex : "#.*$"
             }, {
                 token : "lparen",
                 regex : "[[({]"

From c80ae070f77cc05534b6008a0ed165f0f4cd6ef7 Mon Sep 17 00:00:00 2001
From: nightwing 
Date: Wed, 31 Jul 2013 17:54:44 +0400
Subject: [PATCH 6/6] small cleanup

---
 lib/ace/ext/emmet.js | 5 +++--
 1 file changed, 3 insertions(+), 2 deletions(-)

diff --git a/lib/ace/ext/emmet.js b/lib/ace/ext/emmet.js
index 5c80ee47..6647da40 100644
--- a/lib/ace/ext/emmet.js
+++ b/lib/ace/ext/emmet.js
@@ -264,13 +264,14 @@ AceEmmetEditor.prototype = {
     // by default, abbreviation parser generates all unlinked (un-mirrored)
     // tabstops as ${0}, so we have upgrade all caret tabstops with unique
     // positions but make sure that all other tabstops are not linked accidentally
+    // based on https://github.com/sergeche/emmet-sublime/blob/master/editor.js#L119-L171
     $updateTabstops: function(value) {
         var base = 1000;
         var zeroBase = 0;
         var lastZero = null;
         var range = emmet.require('range');
         var ts = emmet.require('tabStops');
-
+        var settings = emmet.require('resources').getVocabulary("user");
         var tabstopOptions = {
             tabstop: function(data) {
                 var group = parseInt(data.group, 10);
@@ -303,7 +304,7 @@ AceEmmetEditor.prototype = {
 
         value = ts.processText(value, tabstopOptions);
 
-        if (/*sublimeGetOption('insert_final_tabstop', false)*/ false && !/\$\{0\}$/.test(value)) {
+        if (settings.variables['insert_final_tabstop'] && !/\$\{0\}$/.test(value)) {
             value += '${0}';
         } else if (lastZero) {
             value = emmet.require('utils').replaceSubstring(value, '${0}', lastZero);