From 3f4930c96b8d6b6df59ce062a1cada888001c9fd Mon Sep 17 00:00:00 2001 From: Jeff Booher Date: Wed, 21 Aug 2013 14:38:17 -0700 Subject: [PATCH 1/9] Add Extract CSSUtils.extractAllNamedFlows() to retrieve an array of named css region flows --- src/language/CSSUtils.js | 54 +++++++++++++++- test/spec/CSSUtils-test-files/regions.css | 77 +++++++++++++++++++++++ test/spec/CSSUtils-test.js | 19 +++++- 3 files changed, 148 insertions(+), 2 deletions(-) create mode 100644 test/spec/CSSUtils-test-files/regions.css diff --git a/src/language/CSSUtils.js b/src/language/CSSUtils.js index a331e798008..e94349c5036 100644 --- a/src/language/CSSUtils.js +++ b/src/language/CSSUtils.js @@ -22,7 +22,7 @@ */ -/*jslint vars: true, plusplus: true, devel: true, nomen: true, indent: 4, maxerr: 50 */ +/*jslint vars: true, plusplus: true, devel: true, nomen: true, indent: 4, maxerr: 50, regexp: true */ /*global define, $, CodeMirror, _parseRuleList: true */ // JSLint Note: _parseRuleList() is cyclical dependency, not a global function. @@ -1115,10 +1115,62 @@ define(function (require, exports, module) { return _stripAtRules(selector); } + // removes css comments from the content + function _removeComments(_content) { + return _content.replace(/\/\*(?:(?!\*\/)[\s\S])*\*\//g, ''); + } + + // removes strings from the content + function _removeStrings(_content) { + return _content.replace(/[^\\]\"(.*)[^\\]\"|[^\\]\'(.*)[^\\]\'+/g, ''); + } + + /** + * Reduces the style sheet by removing comments and strings + * so that the content can be parsed using a regular expression + * @param {!String} content to reduce + * @return {String} reduced content + */ + function reduceStyleSheetForRegExParsing(content) { + return _removeStrings(_removeComments(content)); + } + + /** + * Extracts all named flow instances + * @param {!String} text to extract from + * @return {?Array.} array of unique flow names + */ + function extractAllNamedFlows(text) { + var namedFlowRegEx = /(?:flow\-(into|from)\:[ \t\n\r]*)([a-z0-9_\-]+)(?:[ \t\n\r]*;)/gi, + result = [], + names = {}, + thisMatch; + + // Reduce the content so that matches + // inside strings and comments are ignored + text = reduceStyleSheetForRegExParsing(text); + + // Find the first match + thisMatch = namedFlowRegEx.exec(text); + + // Iterate over the matches and add them to result + while (thisMatch) { + var thisName = thisMatch[2]; + if (!names.hasOwnProperty(thisName)) { + names[thisName] = result.push(thisName); + } + thisMatch = namedFlowRegEx.exec(text); + } + + return result; + } + exports._findAllMatchingSelectorsInText = _findAllMatchingSelectorsInText; // For testing only exports.findMatchingRules = findMatchingRules; exports.extractAllSelectors = extractAllSelectors; + exports.extractAllNamedFlows = extractAllNamedFlows; exports.findSelectorAtDocumentPos = findSelectorAtDocumentPos; + exports.reduceStyleSheetForRegExParsing = reduceStyleSheetForRegExParsing; exports.SELECTOR = SELECTOR; exports.PROP_NAME = PROP_NAME; diff --git a/test/spec/CSSUtils-test-files/regions.css b/test/spec/CSSUtils-test-files/regions.css new file mode 100644 index 00000000000..7c07a9fd64e --- /dev/null +++ b/test/spec/CSSUtils-test-files/regions.css @@ -0,0 +1,77 @@ +/* basic tests */ +article.content { + flow-into: main; +} + +section.layout > div { + flow-from: main; +} + + +#jeff.content { + flow-into: jeff; +} + +#jeff.layout > div { + flow-from: jeff; +} + +/* exclude matches inside comments tests */ + +/* +p.content { + flow-into: carter; +} + +p.layout > div { + flow-from: carter; +} +*/ + +/* exclude matches inside strings tests */ + +div { + content: "/* p.content { flow-into: carter; } p.layout > div { flow-from: carter; } */"; +} + + +div { + content: "html.content { flow-into: dexter; } html.layout > div { flow-from: dexter; }"; +} + + +/* +div.content { + content: "flow-into: martin;"; +} + +div.layout > div { + content: "flow-from: martin;"; +} +*/ + +/* multi-line property tests */ + +#randy.content { + flow-into: + randy + ; +} + +#randy.layout > div { + flow-from: randy; +} + +/* test to exclude duplicates */ +#yin.content { + flow-into: jeff; +} + +#yin.layout > div { + flow-from: jeff; +} + +/* flow-from only tests */ +#raymond.layout > div { + flow-from: lim; +} diff --git a/test/spec/CSSUtils-test.js b/test/spec/CSSUtils-test.js index f8df31facfc..d88859ff995 100644 --- a/test/spec/CSSUtils-test.js +++ b/test/spec/CSSUtils-test.js @@ -41,7 +41,8 @@ define(function (require, exports, module) { offsetsCssFileEntry = new NativeFileSystem.FileEntry(testPath + "/offsets.css"), bootstrapCssFileEntry = new NativeFileSystem.FileEntry(testPath + "/bootstrap.css"), escapesCssFileEntry = new NativeFileSystem.FileEntry(testPath + "/escaped-identifiers.css"), - embeddedHtmlFileEntry = new NativeFileSystem.FileEntry(testPath + "/embedded.html"); + embeddedHtmlFileEntry = new NativeFileSystem.FileEntry(testPath + "/embedded.html"), + cssRegionsFileEntry = new NativeFileSystem.FileEntry(testPath + "/regions.css"); var contextTestCss = require("text!spec/CSSUtils-test-files/contexts.css"), selectorPositionsTestCss = require("text!spec/CSSUtils-test-files/selector-positions.css"); @@ -1922,4 +1923,20 @@ define(function (require, exports, module) { }); }); }); + + describe("CSS Regions", function () { + beforeEach(function () { + init(this, cssRegionsFileEntry); + }); + + it("should find named flows", function () { + var namedFlows = CSSUtils.extractAllNamedFlows(this.fileContent); + expect(namedFlows.length).toBe(4); + expect(namedFlows[0]).toBe("main"); + expect(namedFlows[1]).toBe("jeff"); + expect(namedFlows[2]).toBe("randy"); + expect(namedFlows[3]).toBe("lim"); + }); + + }); }); From 439cf3647fc1817032f14173ceb0253a7cb9d6e2 Mon Sep 17 00:00:00 2001 From: Jeff Booher Date: Thu, 22 Aug 2013 20:42:39 -0700 Subject: [PATCH 2/9] fixed jsdoc and regex @RaymondLim --- src/language/CSSUtils.js | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/src/language/CSSUtils.js b/src/language/CSSUtils.js index e94349c5036..d0fd5c142a3 100644 --- a/src/language/CSSUtils.js +++ b/src/language/CSSUtils.js @@ -501,7 +501,7 @@ define(function (require, exports, module) { declListStartChar: column in line where the declaration list for the rule starts declListEndLine: line where the declaration list for the rule ends declListEndChar: column in the line where the declaration list for the rule ends - * @param text {!String} CSS text to extract from + * @param text {!string} CSS text to extract from * @return {Array.} Array with objects specifying selectors. */ function extractAllSelectors(text) { @@ -851,8 +851,8 @@ define(function (require, exports, module) { * jquery and ask what matches. If the node that the user's cursor is in comes back from jquery, then * we know the selector applies. * - * @param text {!String} CSS text to search - * @param selector {!String} selector to search for + * @param text {!string} CSS text to search + * @param selector {!string} selector to search for * @return {Array.<{selectorGroupStartLine:number, declListEndLine:number, selector:string}>} * Array of objects containing the start and end line numbers (0-based, inclusive range) for each * matched selector. @@ -984,7 +984,7 @@ define(function (require, exports, module) { * div .foo .bar {} * .foo.bar {} * - * @param {!String} selector The selector to match. This can be a tag selector, class selector or id selector + * @param {!string} selector The selector to match. This can be a tag selector, class selector or id selector * @param {?Document} htmlDocument An HTML file for context (so we can search