├── app ├── styles.less ├── bootstrap.config.less ├── TimedCacheFileSystem.js ├── content.jade ├── LatencyFileSystem.js ├── bootstrap.config.js ├── web_modules │ └── jquery-caret.js ├── TestContents.js └── app.js ├── README.md ├── .gitignore └── package.json /app/styles.less: -------------------------------------------------------------------------------- 1 | .completion-table td { 2 | cursor: pointer; 3 | } -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # enhanced-resolve-completion-demo 2 | 3 | See link in description -------------------------------------------------------------------------------- /app/bootstrap.config.less: -------------------------------------------------------------------------------- 1 | // Modify bootstrap variables here. 2 | 3 | // a example: 4 | // @bodyBackground: @grayLighter; -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # will be installed by "wpt init" 2 | /publish.bat 3 | /publish.sh 4 | /dev-server.bat 5 | /dev-server.sh 6 | /server.bat 7 | /server.sh 8 | /_template 9 | 10 | # will be installed by "npm install" 11 | /node_modules 12 | 13 | # will be created by publishing or "wpt init" 14 | # you may remove these lines if you want to publish 15 | # to a git repo. (i. e. in the gh-pages branch) 16 | /publishedStats.json 17 | /public 18 | /index.html 19 | /cache.manifest 20 | /server.js -------------------------------------------------------------------------------- /app/TimedCacheFileSystem.js: -------------------------------------------------------------------------------- 1 | var createThrottledFunction = require("enhanced-resolve/lib/createThrottledFunction"); 2 | 3 | function TimedCacheFileSystem(fs, timeout) { 4 | this.fs = fs; 5 | this.stat = createThrottledFunction(this.fs.stat.bind(this.fs), timeout, {}); 6 | this.readdir = createThrottledFunction(this.fs.readdir.bind(this.fs), timeout, {}); 7 | this.readFile = createThrottledFunction(this.fs.readFile.bind(this.fs), timeout, {}); 8 | } 9 | module.exports = TimedCacheFileSystem; 10 | -------------------------------------------------------------------------------- /app/content.jade: -------------------------------------------------------------------------------- 1 | - require("bootstrap"); 2 | - require("./styles.less"); 3 | .container-fluid 4 | .row-fluid 5 | .span6 6 | form.form 7 | fieldset 8 | legend Code completion test 9 | p Current directory: /home/user/app 10 | label Type here: 11 | input(type="text").span12.request-input 12 | table.table.table-condensed.table-hover.completion-table 13 | legend Resolve 14 | lable Resolves to: 15 | pre.result loader/index.js!moon/shine/is/cool.js 16 | .span6 17 | form.form 18 | fieldset 19 | legend Test filesystem 20 | label Choose one: 21 | select.filesystem-chooser 22 | option(value="simple") Simple 23 | option(value="depth") Depth 24 | option(value="modules") Modules 25 | label Latency: (in ms, per fs access) 26 | input(type="number", value="20").latency-input 27 | label Cache timeout: (in ms) 28 | input(type="number", value="4000").timeout-input 29 | h4 Preview 30 | pre.filesystem 31 | -------------------------------------------------------------------------------- /app/LatencyFileSystem.js: -------------------------------------------------------------------------------- 1 | function LatencyFileSystem(fs, latency) { 2 | this.fs = fs; 3 | this.latency = latency; 4 | } 5 | module.exports = LatencyFileSystem; 6 | 7 | function asAsync(fn, callback) { 8 | try { 9 | var result = fn(); 10 | setTimeout(function() { 11 | callback(null, result); 12 | }, this.latency); 13 | } catch(e) { 14 | setTimeout(function() { 15 | callback(e); 16 | }, this.latency); 17 | } 18 | } 19 | 20 | LatencyFileSystem.prototype.readdir = function(path, callback) { 21 | asAsync.call(this, function() { 22 | return this.fs.readdirSync(path); 23 | }.bind(this), callback); 24 | } 25 | 26 | LatencyFileSystem.prototype.readFile = function(path, encoding, callback) { 27 | asAsync.call(this, function() { 28 | return this.fs.readFileSync(path, encoding); 29 | }.bind(this), callback); 30 | } 31 | 32 | LatencyFileSystem.prototype.stat = function(path, callback) { 33 | asAsync.call(this, function() { 34 | return this.fs.statSync(path); 35 | }.bind(this), callback); 36 | } -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "enhanced-resolve-completion-demo", 3 | "version": "0.1.0", 4 | "author": "Tobias Koppers @sokra", 5 | "dependencies": { 6 | "enhanced-resolve": "0.4.x" 7 | }, 8 | "devDependencies": { 9 | "wpt": "0.5.x", 10 | "webpack": "0.8.x", 11 | "webpack-dev-server": "0.8.x", 12 | "enhanced-require": "0.4.x", 13 | "coffee-loader": "0.2.x", 14 | "jade-loader": "0.2.x", 15 | "json-loader": "0.2.x", 16 | "raw-loader": "0.2.x", 17 | "express": "3.0.x" 18 | }, 19 | "licenses": [ 20 | { 21 | "type": "MIT", 22 | "url": "http://www.opensource.org/licenses/mit-license.php" 23 | } 24 | ], 25 | "webpackTemplate": { 26 | "version": "0.5.5", 27 | "modules": { 28 | "jquery": { 29 | "templates": "https://raw.github.com/webpack/jquery-wpt-module/master", 30 | "template": "https://raw.github.com/webpack/jquery-wpt-module/v1.8.1", 31 | "version": "1.8.1" 32 | }, 33 | "bootstrap": { 34 | "templates": "https://raw.github.com/webpack/bootstrap-wpt-module/master", 35 | "template": "https://raw.github.com/webpack/bootstrap-wpt-module/v2.2.1", 36 | "version": "2.2.1" 37 | } 38 | }, 39 | "options": { 40 | "appName": "enhanced-resolve-completion-demo", 41 | "author": "Tobias Koppers @sokra", 42 | "nodeServer": false, 43 | "cacheManifest": false 44 | } 45 | } 46 | } -------------------------------------------------------------------------------- /app/bootstrap.config.js: -------------------------------------------------------------------------------- 1 | // executed while compiling by the bootstrap wpt-module 2 | // to get the (by-default) required bootstrap scripts and styles 3 | // you can ever require("bootstrap/xxx"), i.e. require("bootstrap/affix") 4 | // to include more scripts, that are not included by default 5 | 6 | // modify to your wishes 7 | module.exports = { 8 | scripts: { 9 | affix: true, 10 | alert: true, 11 | button: true, 12 | carousel: true, 13 | collapse: true, 14 | dropdown: true, 15 | modal: true, 16 | popover: true, 17 | scrollspy: true, 18 | tab: true, 19 | tooltip: true, 20 | transition: true, 21 | typeahead: true 22 | }, 23 | styles: { 24 | accordion: true, 25 | alerts: true, 26 | breadcrumbs: true, 27 | "button-groups": true, 28 | buttons: true, 29 | carousel: true, 30 | close: true, 31 | code: true, 32 | "component-animations": true, 33 | dropdowns: true, 34 | forms: true, 35 | grid: true, 36 | "hero-unit": true, 37 | "labels-badges": true, 38 | layouts: true, 39 | mixins: true, 40 | media: true, 41 | modals: true, 42 | navbar: true, 43 | navs: true, 44 | pager: true, 45 | pagination: true, 46 | popovers: true, 47 | "progress-bars": true, 48 | "responsive-1200px-min": true, 49 | "responsive-767px-max": true, 50 | "responsive-768px-979px": true, 51 | "responsive-navbar": true, 52 | "responsive-utilities": true, 53 | scaffolding: true, 54 | sprites: true, 55 | tables: true, 56 | thumbnails: true, 57 | tooltip: true, 58 | type: true, 59 | utilities: true, 60 | wells: true 61 | } 62 | } -------------------------------------------------------------------------------- /app/web_modules/jquery-caret.js: -------------------------------------------------------------------------------- 1 | /* 2 | * 3 | * Copyright (c) 2010 C. F., Wong (Cloudgen Examplet Store) 4 | * Licensed under the MIT License: 5 | * http://www.opensource.org/licenses/mit-license.php 6 | * 7 | */ 8 | (function($,len,createRange,duplicate){ 9 | $.fn.caret=function(options,opt2){ 10 | var start,end,t=this[0],browser=$.browser.msie; 11 | if(typeof options==="object" && typeof options.start==="number" && typeof options.end==="number") { 12 | start=options.start; 13 | end=options.end; 14 | } else if(typeof options==="number" && typeof opt2==="number"){ 15 | start=options; 16 | end=opt2; 17 | } else if(typeof options==="string"){ 18 | if((start=t.value.indexOf(options))>-1) end=start+options[len]; 19 | else start=null; 20 | } else if(Object.prototype.toString.call(options)==="[object RegExp]"){ 21 | var re=options.exec(t.value); 22 | if(re != null) { 23 | start=re.index; 24 | end=start+re[0][len]; 25 | } 26 | } 27 | if(typeof start!="undefined"){ 28 | if(browser){ 29 | var selRange = this[0].createTextRange(); 30 | selRange.collapse(true); 31 | selRange.moveStart('character', start); 32 | selRange.moveEnd('character', end-start); 33 | selRange.select(); 34 | } else { 35 | this[0].selectionStart=start; 36 | this[0].selectionEnd=end; 37 | } 38 | this[0].focus(); 39 | return this 40 | } else { 41 | if(browser){ 42 | var selection=document.selection; 43 | if (this[0].tagName.toLowerCase() != "textarea") { 44 | var val = this.val(), 45 | range = selection[createRange]()[duplicate](); 46 | range.moveEnd("character", val[len]); 47 | var s = (range.text == "" ? val[len]:val.lastIndexOf(range.text)); 48 | range = selection[createRange]()[duplicate](); 49 | range.moveStart("character", -val[len]); 50 | var e = range.text[len]; 51 | } else { 52 | var range = selection[createRange](), 53 | stored_range = range[duplicate](); 54 | stored_range.moveToElementText(this[0]); 55 | stored_range.setEndPoint('EndToEnd', range); 56 | var s = stored_range.text[len] - range.text[len], 57 | e = s + range.text[len] 58 | } 59 | } else { 60 | var s=t.selectionStart, 61 | e=t.selectionEnd; 62 | } 63 | var te=t.value.substring(s,e); 64 | return {start:s,end:e,text:te,replace:function(st){ 65 | return t.value.substring(0,s)+st+t.value.substring(e,t.value[len]) 66 | }} 67 | } 68 | } 69 | })(jQuery,"length","createRange","duplicate"); -------------------------------------------------------------------------------- /app/TestContents.js: -------------------------------------------------------------------------------- 1 | exports.simple = { 2 | home: { 3 | user: { 4 | node_modules: { 5 | usermodule1: { 6 | "index.js": "" 7 | }, 8 | usermodule2: { 9 | "package.json": JSON.stringify({ 10 | main: "main" 11 | }), 12 | "main.js": "" 13 | }, 14 | "usermodule3.js": "" 15 | }, 16 | app: { 17 | lib: { 18 | "file.js": "" 19 | } 20 | } 21 | } 22 | } 23 | }; 24 | 25 | exports.depth = { 26 | home: { 27 | user: { 28 | node_modules: { 29 | usermodule1: { 30 | "index.js": "" 31 | }, 32 | usermodule2: { 33 | "package.json": JSON.stringify({ 34 | main: "main" 35 | }), 36 | "main.js": "" 37 | }, 38 | "usermodule3.js": "" 39 | }, 40 | app: { 41 | node_modules: { 42 | 43 | }, 44 | lib: { 45 | node_modules: { 46 | }, 47 | web_modules: { 48 | }, 49 | directory: { 50 | node_modules: { 51 | }, 52 | "file.js": "" 53 | } 54 | } 55 | } 56 | } 57 | } 58 | }; 59 | 60 | exports.modules = { 61 | home: { 62 | user: { 63 | node_modules: { 64 | usermodule1: { 65 | "index.js": "" 66 | }, 67 | usermodule2: { 68 | "package.json": JSON.stringify({ 69 | main: "main" 70 | }), 71 | "main.js": "" 72 | }, 73 | "usermodule3.js": "", 74 | "raw-loader": { 75 | "package.json": JSON.stringify({ 76 | loader: "loader", 77 | main: "main" 78 | }), 79 | "index.loader.js": "", 80 | "index.js": "", 81 | "main.js": "", 82 | "loader.js": "" 83 | } 84 | }, 85 | app: { 86 | node_modules: { 87 | "enhanced-resolve": { 88 | "package.json": JSON.stringify({ 89 | main: "lib/cachedFsResolve.js" 90 | }), 91 | lib: { 92 | "cachedFsResolve.js": "", 93 | "createThrottledFunction.js": "", 94 | "matchRegExpObject.js": "", 95 | "parse.js": "", 96 | "resolve.js": "", 97 | "stringify.js": "" 98 | }, 99 | test: { 100 | "complete.js": "", 101 | "errors.js": "", 102 | fixtures: { 103 | "a.js": "", 104 | "abc.txt": "", 105 | "b.js": "", 106 | "c.js": "", 107 | "complex.js": "", 108 | "dirOrFile.js": "", 109 | "file.load1": "", 110 | "file.load2": "", 111 | "main1.js": "", 112 | "main2.js": "", 113 | "main3.js": "", 114 | "shortcutdir.js": "" 115 | }, 116 | lib: { 117 | "AsyncFileSystem.js": "", 118 | "benchmark.js": "", 119 | "CachedFileSystem.js": "", 120 | "ConstFileSystem.js": "", 121 | "customResolveFactory.js": "", 122 | "DebugFileSystem.js": "", 123 | "LoggedFileSystem.js": "", 124 | "TestContents.js": "", 125 | }, 126 | "parse.js": "", 127 | "resolve.js": "", 128 | "simple.js": "", 129 | "stringify.js": "" 130 | } 131 | }, 132 | }, 133 | lib: { 134 | node_modules: { 135 | }, 136 | web_modules: { 137 | }, 138 | directory: { 139 | node_modules: { 140 | }, 141 | "file.js": "" 142 | } 143 | } 144 | } 145 | } 146 | } 147 | }; -------------------------------------------------------------------------------- /app/app.js: -------------------------------------------------------------------------------- 1 | function prefix(str, prefix) { 2 | return prefix + str.split("\n").join(prefix); 3 | } 4 | 5 | var customResolveFactory = require("enhanced-resolve/test/lib/customResolveFactory"); 6 | var ConstFileSystem = require("enhanced-resolve/test/lib/ConstFileSystem"); 7 | var LatencyFileSystem = require("./LatencyFileSystem"); 8 | var TimedCacheFileSystem = require("./TimedCacheFileSystem"); 9 | var TestContents = require("./TestContents"); 10 | 11 | function syncFsToString(fs, path) { 12 | var stat = fs.statSync(path); 13 | if(stat.isFile()) { 14 | return path.split("/").pop(); 15 | } else { 16 | var dir = fs.readdirSync(path); 17 | if(dir.length == 0) { 18 | return path.split("/").pop() + " (empty)"; 19 | } else { 20 | dir = dir.map(function(d) { 21 | return syncFsToString(fs, path + "/" + d); 22 | }); 23 | var last = dir.pop(); 24 | dir = dir.map(function(d) { 25 | return "+--" + d.split("\n").join("\n| "); 26 | }); 27 | dir.push("+--" + last.split("\n").join("\n ")); 28 | return path.split("/").pop() + "\n" + dir.join("\n"); 29 | } 30 | } 31 | } 32 | 33 | var resolve = null; 34 | function initFilesystem(name, latency, timeout) { 35 | var syncFs = new ConstFileSystem(TestContents[name]); 36 | resolve = customResolveFactory(new TimedCacheFileSystem(new LatencyFileSystem(syncFs, latency), timeout)); 37 | $(".filesystem").text(syncFsToString(syncFs, "/home")); 38 | } 39 | 40 | var jquery = require("jquery"); 41 | require("jquery-caret"); 42 | jquery(function($) { 43 | $("body").html(require("./content.jade")()); 44 | 45 | function updateFilesystem() { 46 | initFilesystem($(".filesystem-chooser").val(), parseInt($(".latency-input").val(), 10), parseInt($(".timeout-input").val(), 10)); 47 | oldText = ""; 48 | checkUpdate(); 49 | } 50 | updateFilesystem(); 51 | 52 | $(".filesystem-chooser, .latency-input, .timeout-input").change(function() { 53 | updateFilesystem(); 54 | }); 55 | 56 | $(".request-input").bind("focus keyup click mouseup mousedown", function() { 57 | checkUpdate(); 58 | }); 59 | 60 | var currentCompletions = null; 61 | var currentCompletionPos = 0; 62 | $(".request-input").bind("keydown keypress", function(event) { 63 | if(event.which == 13) { 64 | if(currentCompletions) { 65 | applyCompletion(currentCompletions[currentCompletionPos], $(".request-input").caret().start); 66 | } 67 | event.preventDefault(); 68 | return false; 69 | } 70 | if(event.which == 38) { 71 | if(currentCompletions && currentCompletionPos > 0) { 72 | currentCompletionPos--; 73 | $($(".completion-table tr").removeClass("success")[currentCompletionPos]).addClass("success"); 74 | } 75 | event.preventDefault(); 76 | return false; 77 | } 78 | if(event.which == 40) { 79 | if(currentCompletions && currentCompletionPos < currentCompletions.length-1) { 80 | currentCompletionPos++; 81 | $($(".completion-table tr").removeClass("success")[currentCompletionPos]).addClass("success"); 82 | } 83 | event.preventDefault(); 84 | return false; 85 | } 86 | 87 | checkUpdate(); 88 | }); 89 | 90 | var oldText = ""; 91 | function getCompletionText() { 92 | var input = $(".request-input"); 93 | var caret = input.caret(); 94 | var text = input.val(); 95 | var rawText = text; 96 | return text.substr(0, caret.start) + "*" + text.substr(caret.end); 97 | } 98 | function checkUpdate() { 99 | var input = $(".request-input"); 100 | var caret = input.caret(); 101 | var rawText = input.val(); 102 | var text = getCompletionText();; 103 | if(oldText == text) return; 104 | oldText = text; 105 | resolve("/home/user/app", rawText, function(err, result) { 106 | if(err) return $(".result").text(err + ""); 107 | $(".result").text(result.replace(/!/g, "!\n ")); 108 | }); 109 | var table = $(".completion-table"); 110 | resolve.complete("/home/user/app", text, function(err, completions) { 111 | table.html(""); 112 | if(err) return; 113 | currentCompletions = completions; 114 | currentCompletionPos = 0; 115 | var body = $(""); 116 | completions.forEach(function(completion, idx) { 117 | var row = $(""); 118 | // $("").append($("").text(completion.insert)).appendTo(row); 119 | $("").append($("").text(completion.seqment)).appendTo(row); 120 | $("").append($("").text(completion.result)).appendTo(row); 121 | row.click(function() { 122 | applyCompletion(completion, caret.start); 123 | }); 124 | if(idx == 0) 125 | row.addClass("success"); 126 | row.appendTo(body); 127 | }); 128 | body.appendTo(table); 129 | }); 130 | } 131 | function applyCompletion(completion, caret) { 132 | var input = $(".request-input"); 133 | input.val(completion.result); 134 | input.focus(); 135 | var pos = caret + completion.insert.length; 136 | input.caret({start: pos, end: pos}); 137 | } 138 | }); 139 | 140 | 141 | --------------------------------------------------------------------------------