From 257d80f3791cef35b7e929c0324484252050ccfd Mon Sep 17 00:00:00 2001 From: Fabian Jakobs Date: Fri, 16 Apr 2010 12:08:03 +0200 Subject: [PATCH] Add support for doc doc comments: - special highlighting - highlight jsdoc tags - special auto indent mode --- css/editor.css | 8 +++++++ src/KeyBinding.js | 2 +- src/Tokenizer.js | 31 +++++++++++++++++++++------- src/mode/JavaScript.js | 19 ++++++++++------- src/mode/JavaScriptHighlightRules.js | 28 ++++++++++++++++++++----- test/mode/JavaScriptTest.js | 31 ++++++++++------------------ test/mode/JavaScriptTokenizerTest.js | 27 +++++++++++++++++++++--- 7 files changed, 103 insertions(+), 43 deletions(-) diff --git a/css/editor.css b/css/editor.css index 32cd9d98..d30aac52 100644 --- a/css/editor.css +++ b/css/editor.css @@ -80,9 +80,17 @@ .line .comment { font-style: italic; + color: rgb(76, 136, 107); +} + +.line .doc-comment { color: rgb(0, 102, 255); } +.line .doc-comment-tag { + color: rgb(128, 159, 191); +} + .line .number { color: rgb(0, 0, 205); } diff --git a/src/KeyBinding.js b/src/KeyBinding.js index e2ef51fd..cbc1af6b 100644 --- a/src/KeyBinding.js +++ b/src/KeyBinding.js @@ -188,7 +188,7 @@ ace.KeyBinding = function(element, editor) { case keys.TAB: if (e.shiftKey) { editor.blockOutdent(); - } else if (selection.isMultiLineSelection()) { + } else if (selection.isMultiLine()) { editor.blockIndent(); } else { editor.onTextInput("\t"); diff --git a/src/Tokenizer.js b/src/Tokenizer.js index fb50fc74..3881f8ff 100644 --- a/src/Tokenizer.js +++ b/src/Tokenizer.js @@ -27,11 +27,14 @@ ace.Tokenizer.prototype.getLineTokens = function(line, startState) { var lastIndex = 0; + var token = { + type: null, + value: "" + }; + while (match = re.exec(line)) { - var token = { - type : "text", - value : match[0] - }; + var type = "text"; + var value = match[0]; if (re.lastIndex == lastIndex) { throw new Error("tokenizer error"); } lastIndex = re.lastIndex; @@ -41,10 +44,10 @@ ace.Tokenizer.prototype.getLineTokens = function(line, startState) { for ( var i = 0; i < state.length; i++) { if (match[i + 1]) { if (typeof state[i].token == "function") { - token.type = state[i].token(match[0]); + type = state[i].token(match[0]); } else { - token.type = state[i].token; + type = state[i].token; } if (state[i].next && state[i].next !== currentState) { @@ -59,9 +62,23 @@ ace.Tokenizer.prototype.getLineTokens = function(line, startState) { } }; - tokens.push(token); + if (token.type !== type) { + if (token.type) { + tokens.push(token); + } + token = { + type: type, + value: value + }; + } else { + token.value += value; + } }; + if (token.type) { + tokens.push(token); + } + // window.LOG && console.log(tokens, currentState); return { diff --git a/src/mode/JavaScript.js b/src/mode/JavaScript.js index c4f25fd6..1c428866 100644 --- a/src/mode/JavaScript.js +++ b/src/mode/JavaScript.js @@ -13,18 +13,23 @@ ace.mode.JavaScript.prototype.toggleCommentLines = function(doc, range) { return addedRows; }; -ace.mode.JavaScript.prototype.increaseIndentPatterns = { - "start" : /^(\s*).*[\{\(\[]\s*$/ -}; - ace.mode.JavaScript.prototype.getNextLineIndent = function(line, state, tab) { - var re = this.increaseIndentPatterns[state]; - - if (re) { + if (state == "start") { + var re = /^(\s*).*[\{\(\[]\s*$/; var match = line.match(re); if (match) { return (match[1] || "") + tab; } + } else if (state == "doc-comment") { + var re = /^(\s*)(\/?)\*.*$/; + var match = line.match(re); + if (match) { + var indent = match[1]; + if (match[2]) { + indent += " "; + } + return indent + "* "; + } } var match = line.match(/^(\s+).*$/); diff --git a/src/mode/JavaScriptHighlightRules.js b/src/mode/JavaScriptHighlightRules.js index 767a436c..661dd168 100644 --- a/src/mode/JavaScriptHighlightRules.js +++ b/src/mode/JavaScriptHighlightRules.js @@ -32,15 +32,16 @@ ace.mode.JavaScriptHighlightRules = function() { // regexps are ordered -> the first match is used this._rules = { - start : [ { + "start" : [ { token : "comment", regex : "\\/\\/.*$" }, { - token : "comment", // multi line comment in one line - regex : "\\/\\*.*?\\*\\/" + token : "doc-comment", // doc comment + regex : "\\/\\*\\*", + next : "doc-comment" }, { - token : "comment", // multi line comment start - regex : "\\/\\*.*$", + token : "comment", // multi line comment + regex : "\\/\\*", next : "comment" }, { token : "string", // single line @@ -82,6 +83,23 @@ ace.mode.JavaScriptHighlightRules = function() { token : "text", regex : "\\s+" } ], + "doc-comment" : [ { + token : "doc-comment", // closing comment + regex : "\\*\\/", + next : "start" + }, { + token : "doc-comment-tag", + regex : "@[\\w\\d_]+" + }, { + token : "doc-comment", + regex : "\s+" + }, { + token : "doc-comment", + regex : "[^@\\*]+" + }, { + token : "doc-comment", + regex : "." + }], "comment" : [ { token : "comment", // closing comment regex : ".*?\\*\\/", diff --git a/test/mode/JavaScriptTest.js b/test/mode/JavaScriptTest.js index 3ce04472..77309b0f 100644 --- a/test/mode/JavaScriptTest.js +++ b/test/mode/JavaScriptTest.js @@ -50,32 +50,23 @@ var JavaScriptTest = new TestCase("mode.JavaScriptTest", { }, "test: auto indent after opening brace" : function() { - var doc = new ace.TextDocument(["if () {"].join("\n")); - var editor = new ace.Editor(new MockRenderer(), doc, this.mode); - - editor.navigateLineEnd(); - editor.onTextInput("\n"); - - assertEquals(["if () {", doc.getTabString()].join("\n"), doc.toString()); + assertEquals(" ", this.mode.getNextLineIndent("if () {", "start", " ")); }, "test: no auto indent after opening brace in multi line comment" : function() { - var doc = new ace.TextDocument(["/*if () {"].join("\n")); - var editor = new ace.Editor(new MockRenderer(), doc, this.mode); - - editor.navigateLineEnd(); - editor.onTextInput("\n"); - - assertEquals(["/*if () {", ""].join("\n"), doc.toString()); + assertEquals("", this.mode.getNextLineIndent("/*if () {", " ")); }, "test: no auto indent should add to existing indent" : function() { - var doc = new ace.TextDocument([" if () {"].join("\n")); - var editor = new ace.Editor(new MockRenderer(), doc, this.mode); + assertEquals(" ", this.mode.getNextLineIndent(" if () {", "start", " ")); + assertEquals(" ", this.mode.getNextLineIndent(" cde", "start", " ")); + }, - editor.navigateLineEnd(); - editor.onTextInput("\n"); - - assertEquals([" if () {", " " + doc.getTabString()].join("\n"), doc.toString()); + "test: special indent in doc comments" : function() { + assertEquals(" * ", this.mode.getNextLineIndent("/**", "doc-comment", " ")); + assertEquals(" * ", this.mode.getNextLineIndent(" /**", "doc-comment", " ")); + assertEquals(" * ", this.mode.getNextLineIndent(" *", "doc-comment", " ")); + assertEquals(" * ", this.mode.getNextLineIndent(" *", "doc-comment", " ")); + assertEquals(" ", this.mode.getNextLineIndent(" abc", "doc-comment", " ")); } }); \ No newline at end of file diff --git a/test/mode/JavaScriptTokenizerTest.js b/test/mode/JavaScriptTokenizerTest.js index 0e5e019e..9c0cd8b3 100644 --- a/test/mode/JavaScriptTokenizerTest.js +++ b/test/mode/JavaScriptTokenizerTest.js @@ -9,12 +9,33 @@ var JavaScriptTokenizerTest = new TestCase("mode.JavaScriptTokenizerTest", { var tokens = this.tokenizer.getLineTokens(line, "start").tokens; + assertEquals(3, tokens.length); + assertEquals("identifier", tokens[0].type); + assertEquals("text", tokens[1].type); + assertEquals("keyword", tokens[2].type); + }, + + "test: tokenize doc comment" : function() { + var line = "abc /** de */ fg"; + + var tokens = this.tokenizer.getLineTokens(line, "start").tokens; + assertEquals(5, tokens.length); assertEquals("identifier", tokens[0].type); assertEquals("text", tokens[1].type); - assertEquals("text", tokens[2].type); + assertEquals("doc-comment", tokens[2].type); assertEquals("text", tokens[3].type); - assertEquals("keyword", tokens[4].type); - } + assertEquals("identifier", tokens[4].type); + }, + "test: tokenize doc comment with tag" : function() { + var line = "/** @param {} */"; + + var tokens = this.tokenizer.getLineTokens(line, "start").tokens; + + assertEquals(3, tokens.length); + assertEquals("doc-comment", tokens[0].type); + assertEquals("doc-comment-tag", tokens[1].type); + assertEquals("doc-comment", tokens[2].type); + } }); \ No newline at end of file