swagr 0.0.8 → 0.0.10

Sign up to get free protection for your applications and to get access to all the features.
Files changed (93) hide show
  1. data/Rakefile +20 -0
  2. data/bin/swagr +37 -9
  3. data/examples/examples01/app.rb +5 -1
  4. data/examples/examples01/views/edit.slim +31 -0
  5. data/examples/examples01/views/footer.slim +1 -1
  6. data/examples/examples01/views/layout.slim +23 -5
  7. data/examples/examples01/views/navbar.slim +17 -16
  8. data/lib/swagr/version.rb +3 -1
  9. data/templates/app.rb +5 -1
  10. data/templates/static/js/codemirror/keymap/vim.js +279 -66
  11. data/templates/static/js/codemirror/lib/codemirror.css +10 -9
  12. data/templates/static/js/codemirror/lib/codemirror.js +385 -152
  13. data/templates/static/js/codemirror/mode/apl/apl.js +160 -0
  14. data/templates/static/js/codemirror/mode/apl/index.html +61 -0
  15. data/templates/static/js/codemirror/mode/asterisk/asterisk.js +183 -0
  16. data/templates/static/js/codemirror/mode/asterisk/index.html +142 -0
  17. data/templates/static/js/codemirror/mode/clike/clike.js +2 -0
  18. data/templates/static/js/codemirror/mode/clike/index.html +1 -1
  19. data/templates/static/js/codemirror/mode/clike/scala.html +1 -1
  20. data/templates/static/js/codemirror/mode/clojure/clojure.js +3 -3
  21. data/templates/static/js/codemirror/mode/css/css.js +2 -2
  22. data/templates/static/js/codemirror/mode/css/test.js +76 -471
  23. data/templates/static/js/codemirror/mode/d/d.js +205 -0
  24. data/templates/static/js/codemirror/mode/d/index.html +262 -0
  25. data/templates/static/js/codemirror/mode/erlang/index.html +1 -1
  26. data/templates/static/js/codemirror/mode/gfm/gfm.js +2 -1
  27. data/templates/static/js/codemirror/mode/gfm/index.html +1 -2
  28. data/templates/static/js/codemirror/mode/gfm/test.js +84 -225
  29. data/templates/static/js/codemirror/mode/go/index.html +1 -1
  30. data/templates/static/js/codemirror/mode/groovy/index.html +1 -1
  31. data/templates/static/js/codemirror/mode/haskell/index.html +1 -1
  32. data/templates/static/js/codemirror/mode/javascript/index.html +2 -2
  33. data/templates/static/js/codemirror/mode/javascript/javascript.js +13 -2
  34. data/templates/static/js/codemirror/mode/less/index.html +1 -1
  35. data/templates/static/js/codemirror/mode/lua/index.html +1 -1
  36. data/templates/static/js/codemirror/mode/markdown/index.html +1 -1
  37. data/templates/static/js/codemirror/mode/markdown/markdown.js +8 -7
  38. data/templates/static/js/codemirror/mode/markdown/test.js +579 -1266
  39. data/templates/static/js/codemirror/mode/meta.js +71 -0
  40. data/templates/static/js/codemirror/mode/mysql/index.html +2 -0
  41. data/templates/static/js/codemirror/mode/ocaml/index.html +1 -1
  42. data/templates/static/js/codemirror/mode/php/index.html +1 -1
  43. data/templates/static/js/codemirror/mode/plsql/index.html +2 -0
  44. data/templates/static/js/codemirror/mode/python/index.html +1 -1
  45. data/templates/static/js/codemirror/mode/ruby/index.html +1 -1
  46. data/templates/static/js/codemirror/mode/sass/index.html +54 -0
  47. data/templates/static/js/codemirror/mode/sass/sass.js +349 -0
  48. data/templates/static/js/codemirror/mode/shell/index.html +1 -1
  49. data/templates/static/js/codemirror/mode/sieve/LICENSE +0 -4
  50. data/templates/static/js/codemirror/mode/sieve/sieve.js +37 -10
  51. data/templates/static/js/codemirror/mode/smalltalk/index.html +1 -1
  52. data/templates/static/js/codemirror/mode/sparql/index.html +1 -1
  53. data/templates/static/js/codemirror/mode/sql/index.html +68 -0
  54. data/templates/static/js/codemirror/mode/sql/sql.js +268 -0
  55. data/templates/static/js/codemirror/mode/stex/test.js +104 -343
  56. data/templates/static/js/codemirror/mode/tiddlywiki/index.html +1 -1
  57. data/templates/static/js/codemirror/mode/vb/index.html +1 -1
  58. data/templates/static/js/codemirror/mode/xquery/test.js +54 -67
  59. data/templates/static/js/codemirror/mode/xquery/xquery.js +8 -8
  60. data/templates/static/js/crossfilter.min.js +1 -0
  61. data/templates/views/edit.slim +31 -0
  62. data/templates/views/footer.slim +1 -1
  63. data/templates/views/layout.slim +23 -5
  64. data/templates/views/navbar.slim +17 -16
  65. metadata +16 -30
  66. data/examples/examples01/static/css/codemirror.css +0 -1247
  67. data/examples/examples01/static/js/codemirror-compressed-3-0.js +0 -5
  68. data/examples/examples01/views/about.slim +0 -15
  69. data/examples/examples01/views/application.slim +0 -0
  70. data/examples/examples01/views/layout_cm.slim +0 -17
  71. data/templates/static/js/codemirror/lib/util/closetag.js +0 -85
  72. data/templates/static/js/codemirror/lib/util/colorize.js +0 -29
  73. data/templates/static/js/codemirror/lib/util/continuecomment.js +0 -36
  74. data/templates/static/js/codemirror/lib/util/continuelist.js +0 -28
  75. data/templates/static/js/codemirror/lib/util/dialog.css +0 -32
  76. data/templates/static/js/codemirror/lib/util/dialog.js +0 -75
  77. data/templates/static/js/codemirror/lib/util/foldcode.js +0 -182
  78. data/templates/static/js/codemirror/lib/util/formatting.js +0 -108
  79. data/templates/static/js/codemirror/lib/util/javascript-hint.js +0 -137
  80. data/templates/static/js/codemirror/lib/util/loadmode.js +0 -51
  81. data/templates/static/js/codemirror/lib/util/match-highlighter.js +0 -46
  82. data/templates/static/js/codemirror/lib/util/matchbrackets.js +0 -63
  83. data/templates/static/js/codemirror/lib/util/multiplex.js +0 -95
  84. data/templates/static/js/codemirror/lib/util/overlay.js +0 -59
  85. data/templates/static/js/codemirror/lib/util/pig-hint.js +0 -117
  86. data/templates/static/js/codemirror/lib/util/runmode-standalone.js +0 -90
  87. data/templates/static/js/codemirror/lib/util/runmode.js +0 -52
  88. data/templates/static/js/codemirror/lib/util/search.js +0 -119
  89. data/templates/static/js/codemirror/lib/util/searchcursor.js +0 -119
  90. data/templates/static/js/codemirror/lib/util/simple-hint.css +0 -16
  91. data/templates/static/js/codemirror/lib/util/simple-hint.js +0 -102
  92. data/templates/static/js/codemirror/lib/util/xml-hint.js +0 -131
  93. data/templates/views/about.slim +0 -15
data/Rakefile CHANGED
@@ -1 +1,21 @@
1
1
  require "bundler/gem_tasks"
2
+
3
+ desc "Update the build date for the gem"
4
+ task :update_build_date do
5
+ version_file = File.join(".", "lib", "swagr", "version.rb")
6
+ lines = File.readlines(version_file)
7
+ lines = lines.map do |line|
8
+ if line =~ /(\s*)GemBuildDate = "(.+)"/
9
+ timestamp = Time.new.strftime "%Y-%m-%d %H:%M.%S"
10
+ "#{$1}GemBuildDate = #{timestamp.inspect}\n"
11
+ else
12
+ line
13
+ end
14
+ end
15
+ File.open(version_file, "w") {|fh| fh.write lines.join()}
16
+ end
17
+
18
+ desc "Update build date then build and install gem"
19
+ task :uinstall => [:update_build_date, :install] do
20
+ # Nothing to do! Work is done by the two other tasks...
21
+ end
data/bin/swagr CHANGED
@@ -14,6 +14,15 @@ SkeletonTarball = File.join(SkeletonDir, DefaultSkeletonTarball)
14
14
  StaticDir = PublicDir = "static"
15
15
  DefaultSubdirs = ["coffee", "views", StaticDir]
16
16
 
17
+ def output_lines_from_running_command(command)
18
+ `#{command}`.split("\n")
19
+ end
20
+
21
+ def first_output_line_matching(command, regexp)
22
+ lines = output_lines_from_running_command command
23
+ lines.each {|line| return line if line =~ regexp}
24
+ end
25
+
17
26
  class SwagrCommand < Thor
18
27
 
19
28
  include Thor::Actions
@@ -22,25 +31,44 @@ class SwagrCommand < Thor
22
31
  SwagrSourceRoot
23
32
  end
24
33
 
34
+ desc "version", "Print the current version of swagr"
35
+ def version
36
+ say "Swagr version #{Swagr::VERSION} built #{Swagr::GemBuildDate}"
37
+ end
38
+
25
39
  desc "upgrade", "Upgrade used libs by re-downloading the latest versions of needed libs. Might brake some of your code so use with caution."
26
40
  def upgrade
27
41
  inside File.join(SwagrSourceRoot, "templates", PublicDir, "js") do
28
42
  get "http://d3js.org/d3.v3.min.js"
29
43
  get "http://coffeescript.org/extras/coffee-script.js"
30
44
  get "http://code.jquery.com/jquery-latest.js"
45
+ get "https://raw.github.com/square/crossfilter/master/crossfilter.min.js"
31
46
 
32
- # Now get the codemirror tarball, unpack it then copy just the files we want
47
+ # Now get the codemirror tarball and unpack it
33
48
  get "http://codemirror.net/codemirror.zip"
34
- run "unzip -o codemirror.zip"
35
- FileUtils.rm_rf "codemirror" # Delete old dir so we get a fresh start
36
- empty_directory "codemirror"
37
- directory "codemirror-3.0/lib", "codemirror/lib"
38
- directory "codemirror-3.0/keymap", "codemirror/keymap"
39
- directory "codemirror-3.0/mode", "codemirror/mode"
40
- directory "codemirror-3.0/theme", "codemirror/theme"
49
+ # Find the name of the dir were files will be unpacked by "unzip -l" and parsing the lines until matches regexp
50
+ re = /\s*0.+(codemirror-\d+.\d+.?\d*)\//
51
+ line = first_output_line_matching "unzip -l codemirror.zip", re
52
+ if line
53
+ line =~ re
54
+ codemirror_dir = $1
55
+ say "Found codemirror unzipped to #{codemirror_dir}"
56
+
57
+ run "unzip codemirror.zip"
58
+
59
+ # Now copy from unpacked codemirror dir to our template dir
60
+ FileUtils.rm_rf "codemirror" # Delete old dest dir so we get a fresh start
61
+ empty_directory "codemirror"
62
+ directory File.join(codemirror_dir, "lib"), "codemirror/lib"
63
+ directory File.join(codemirror_dir, "keymap"), "codemirror/keymap"
64
+ directory File.join(codemirror_dir, "mode"), "codemirror/mode"
65
+ directory File.join(codemirror_dir, "theme"), "codemirror/theme"
66
+ end
67
+
68
+ # Cleanup after codemirror download and unpack
41
69
  FileUtils.rm_rf "codemirror.zip"
42
70
  say "Removed tarball"
43
- FileUtils.rm_rf "codemirror-3.0"
71
+ FileUtils.rm_rf codemirror_dir
44
72
  say "Removed unpacked codemirror dir"
45
73
  end
46
74
 
@@ -74,8 +74,12 @@ class SwagrGuiServerExample < Sinatra::Base
74
74
  slim :index
75
75
  end
76
76
 
77
+ get '/edit' do
78
+ slim :edit
79
+ end
80
+
77
81
  get '/about' do
78
- slim :about, :layout => :layout_cm
82
+ slim :about
79
83
  end
80
84
 
81
85
  def copyright_holders
@@ -0,0 +1,31 @@
1
+ h1 Let's try in-browser code editing
2
+
3
+ /! script src="js/coffee-script.js" type="text/javascript"
4
+ div class="container" id="code"
5
+ textarea id="editor" cols="50" rows="10" style="width:30%"
6
+ | # Hello
7
+ # This looks ok. How very nice!
8
+ a = 1 + 2
9
+
10
+ /! --------------------------------------------------------------
11
+ /! -- Include files needed by CodeMirror (CM)
12
+ /! --------------------------------------------------------------
13
+ link rel="stylesheet" href="js/codemirror/lib/codemirror.css"
14
+ script src="js/codemirror/lib/codemirror.js"
15
+
16
+ /! CM utils that you use:
17
+ script src="js/codemirror/lib/util/loadmode.js"
18
+
19
+ /! CM modes that you use:
20
+ script src="js/codemirror/mode/ruby/ruby.js"
21
+
22
+ /! CM themes that you use:
23
+ link rel="stylesheet" href="js/codemirror/theme/lesser-dark.css"
24
+
25
+
26
+ script type="text/javascript"
27
+ | var editor = CodeMirror.fromTextArea(document.getElementById("editor"), {
28
+ lineNumbers: true,
29
+ mode: "ruby",
30
+ theme: "lesser-dark"
31
+ });
@@ -1,2 +1,2 @@
1
- h3 Footer...
1
+ /! h3 Footer...
2
2
  | Copyright © #{year} #{copyright_holders}
@@ -2,20 +2,31 @@ doctype html
2
2
  html
3
3
  head
4
4
  meta charset="utf-8"
5
- title Swagr web app template
5
+
6
6
  meta name="viewport" content="width=device-width, initial-scale=1.0"
7
7
  meta name="description" content=""
8
8
  meta name="author" content="FIXME: YOUR NAME"
9
- link href="css/bootstrap.css" rel="stylesheet" /! media="screen"
10
- link rel="stylesheet" href="css/codemirror.css"
9
+
10
+ title Swagr web app template
11
+
12
+ /! --------------------------------------------------------------
13
+ /! -- Files needed by Bootstrap
14
+ /! --------------------------------------------------------------
15
+ link href="css/bootstrap.css" rel="stylesheet"
16
+
17
+
18
+ /! --------------------------------------------------------------
19
+ /! -- Our own style file + custom changes inline
20
+ /! --------------------------------------------------------------
11
21
  link href="css/style.css" rel="stylesheet" /! media="screen"
12
22
  style
13
23
  | body {
14
24
  padding-top: 60px; /* 60px to make the container go all the way to the bottom of the topbar */
15
25
  }
26
+
16
27
  body
17
28
 
18
- div class="container"
29
+ div class="container" id="main_body_content"
19
30
  == slim :navbar
20
31
  == yield
21
32
 
@@ -24,4 +35,11 @@ html
24
35
  == slim :footer
25
36
 
26
37
  script src="js/jquery-latest.js"
27
- script src="js/bootstrap.min.js"
38
+ script src="js/bootstrap.min.js"
39
+
40
+ /! Ensure the active class on the main navbar is updated if one of the nav links is clicked
41
+ script type="text/javascript"
42
+ | $('#top-navbar-ul li a').on('click', function() {
43
+ $(this).parent().parent().find('.active').removeClass('active');
44
+ $(this).parent().addClass('active').css('font-weight', 'bold');
45
+ });
@@ -1,16 +1,17 @@
1
- div class="navbar navbar-inverse navbar-fixed-top"
2
- div class="navbar-inner"
3
- div class="container"
4
- a class="btn btn-navbar" data-toggle="collapse" data-target=".nav-collapse"
5
- span class="icon-bar"
6
- span class="icon-bar"
7
- span class="icon-bar"
8
- a class="brand" href="/" SwagrApp
9
- div class="nav-collapse collapse"
10
- ul class="nav"
11
- li class="active"
12
- a href="/" Home
13
- li
14
- a href="/about" About
15
- li
16
- a href="/contact" Contact
1
+ div id="#top-navbar"
2
+ div class="navbar navbar-inverse navbar-fixed-top"
3
+ div class="navbar-inner"
4
+ div class="container"
5
+ a class="btn btn-navbar" data-toggle="collapse" data-target=".nav-collapse"
6
+ span class="icon-bar"
7
+ span class="icon-bar"
8
+ span class="icon-bar"
9
+ a class="brand" href="/" SwagrApp
10
+ div class="nav-collapse collapse"
11
+ ul class="nav" id="top-navbar-"
12
+ li class="active"
13
+ a href="/" Home
14
+ li
15
+ a href="/edit" Edit
16
+ li
17
+ a href="/about" About
@@ -1,3 +1,5 @@
1
1
  module Swagr
2
- VERSION = "0.0.8"
2
+ VERSION = "0.0.10"
3
+ # Following line is automatically updated. DO NOT EDIT MANUALLY!
4
+ GemBuildDate = "2013-01-28 09:57.15"
3
5
  end
@@ -74,8 +74,12 @@ class SwagrGuiServerExample < Sinatra::Base
74
74
  slim :index
75
75
  end
76
76
 
77
+ get '/edit' do
78
+ slim :edit
79
+ end
80
+
77
81
  get '/about' do
78
- slim :about, :layout => :layout_cm
82
+ slim :about
79
83
  end
80
84
 
81
85
  def copyright_holders
@@ -115,6 +115,10 @@
115
115
  motion: 'moveByWords',
116
116
  motionArgs: { forward: false, wordEnd: true, bigWord: true,
117
117
  inclusive: true }},
118
+ { keys: ['{'], type: 'motion', motion: 'moveByParagraph',
119
+ motionArgs: { forward: false }},
120
+ { keys: ['}'], type: 'motion', motion: 'moveByParagraph',
121
+ motionArgs: { forward: true }},
118
122
  { keys: ['Ctrl-f'], type: 'motion',
119
123
  motion: 'moveByPage', motionArgs: { forward: true }},
120
124
  { keys: ['Ctrl-b'], type: 'motion',
@@ -247,7 +251,7 @@
247
251
  var specialKeys = ['Left', 'Right', 'Up', 'Down', 'Space', 'Backspace',
248
252
  'Esc', 'Home', 'End', 'PageUp', 'PageDown'];
249
253
  var validMarks = upperCaseAlphabet.concat(lowerCaseAlphabet).concat(
250
- numbers);
254
+ numbers).concat(['<', '>']);
251
255
  var validRegisters = upperCaseAlphabet.concat(lowerCaseAlphabet).concat(
252
256
  numbers).concat('-\"'.split(''));
253
257
 
@@ -720,7 +724,11 @@
720
724
  // Handle user defined Ex to Ex mappings
721
725
  exCommandDispatcher.processCommand(cm, command.exArgs.input);
722
726
  } else {
723
- showPrompt(cm, onPromptClose, ':');
727
+ if (vim.visualMode) {
728
+ showPrompt(cm, onPromptClose, ':', undefined, '\'<,\'>');
729
+ } else {
730
+ showPrompt(cm, onPromptClose, ':');
731
+ }
724
732
  }
725
733
  },
726
734
  evalInput: function(cm, vim) {
@@ -806,6 +814,12 @@
806
814
  // CodeMirror can't figure out that we changed directions...
807
815
  cm.setCursor(selectionStart);
808
816
  cm.setSelection(selectionStart, selectionEnd);
817
+ updateMark(cm, vim, '<',
818
+ cursorIsBefore(selectionStart, selectionEnd) ? selectionStart
819
+ : selectionEnd);
820
+ updateMark(cm, vim, '>',
821
+ cursorIsBefore(selectionStart, selectionEnd) ? selectionEnd
822
+ : selectionStart);
809
823
  } else if (!operator) {
810
824
  curEnd = clipCursorToContent(cm, curEnd);
811
825
  cm.setCursor(curEnd.line, curEnd.ch);
@@ -923,6 +937,22 @@
923
937
  cm.setCursor(curStart);
924
938
  return curEnd;
925
939
  },
940
+ moveByParagraph: function(cm, motionArgs) {
941
+ var line = cm.getCursor().line;
942
+ var repeat = motionArgs.repeat;
943
+ var inc = motionArgs.forward ? 1 : -1;
944
+ for (var i = 0; i < repeat; i++) {
945
+ if ((!motionArgs.forward && line === 0) ||
946
+ (motionArgs.forward && line == cm.lineCount() - 1)) {
947
+ break;
948
+ }
949
+ line += inc;
950
+ while (line !== 0 && line != cm.lineCount - 1 && cm.getLine(line)) {
951
+ line += inc;
952
+ }
953
+ }
954
+ return { line: line, ch: 0 };
955
+ },
926
956
  moveByWords: function(cm, motionArgs) {
927
957
  return moveToWord(cm, motionArgs.repeat, !!motionArgs.forward,
928
958
  !!motionArgs.wordEnd, !!motionArgs.bigWord);
@@ -1119,21 +1149,29 @@
1119
1149
  cm.setSelection(curStart, curEnd);
1120
1150
  }
1121
1151
  } else {
1152
+ curStart = cm.getCursor('anchor');
1153
+ curEnd = cm.getCursor('head');
1122
1154
  if (!vim.visualLine && actionArgs.linewise) {
1123
1155
  // Shift-V pressed in characterwise visual mode. Switch to linewise
1124
1156
  // visual mode instead of exiting visual mode.
1125
1157
  vim.visualLine = true;
1126
- curStart = cm.getCursor('anchor');
1127
- curEnd = cm.getCursor('head');
1128
1158
  curStart.ch = cursorIsBefore(curStart, curEnd) ? 0 :
1129
1159
  lineLength(cm, curStart.line);
1130
1160
  curEnd.ch = cursorIsBefore(curStart, curEnd) ?
1131
1161
  lineLength(cm, curEnd.line) : 0;
1132
1162
  cm.setSelection(curStart, curEnd);
1163
+ } else if (vim.visualLine && !actionArgs.linewise) {
1164
+ // v pressed in linewise visual mode. Switch to characterwise visual
1165
+ // mode instead of exiting visual mode.
1166
+ vim.visualLine = false;
1133
1167
  } else {
1134
1168
  exitVisualMode(cm, vim);
1135
1169
  }
1136
1170
  }
1171
+ updateMark(cm, vim, '<', cursorIsBefore(curStart, curEnd) ? curStart
1172
+ : curEnd);
1173
+ updateMark(cm, vim, '>', cursorIsBefore(curStart, curEnd) ? curEnd
1174
+ : curStart);
1137
1175
  },
1138
1176
  joinLines: function(cm, actionArgs, vim) {
1139
1177
  var curStart, curEnd;
@@ -1232,13 +1270,7 @@
1232
1270
  },
1233
1271
  setMark: function(cm, actionArgs, vim) {
1234
1272
  var markName = actionArgs.selectedCharacter;
1235
- if (!inArray(markName, validMarks)) {
1236
- return;
1237
- }
1238
- if (vim.marks[markName]) {
1239
- vim.marks[markName].clear();
1240
- }
1241
- vim.marks[markName] = cm.setBookmark(cm.getCursor());
1273
+ updateMark(cm, vim, markName, cm.getCursor());
1242
1274
  },
1243
1275
  replace: function(cm, actionArgs) {
1244
1276
  var replaceWith = actionArgs.selectedCharacter;
@@ -1636,6 +1668,16 @@
1636
1668
  return clipCursorToContent(cm, { line: line, ch: repeat - 1 });
1637
1669
  }
1638
1670
 
1671
+ function updateMark(cm, vim, markName, pos) {
1672
+ if (!inArray(markName, validMarks)) {
1673
+ return;
1674
+ }
1675
+ if (vim.marks[markName]) {
1676
+ vim.marks[markName].clear();
1677
+ }
1678
+ vim.marks[markName] = cm.setBookmark(pos);
1679
+ }
1680
+
1639
1681
  function charIdxInLine(start, line, character, forward, includeChar) {
1640
1682
  // Search for char in line.
1641
1683
  // motion_options: {forward, includeChar}
@@ -1804,6 +1846,12 @@
1804
1846
  setMarked: function(marked) {
1805
1847
  this.marked = marked;
1806
1848
  },
1849
+ getOverlay: function() {
1850
+ return this.searchOverlay;
1851
+ },
1852
+ setOverlay: function(overlay) {
1853
+ this.searchOverlay = overlay;
1854
+ },
1807
1855
  isReversed: function() {
1808
1856
  return getVimGlobalState().isReversed;
1809
1857
  },
@@ -1815,9 +1863,9 @@
1815
1863
  var vim = getVimState(cm);
1816
1864
  return vim.searchState_ || (vim.searchState_ = new SearchState());
1817
1865
  }
1818
- function dialog(cm, text, shortText, callback) {
1866
+ function dialog(cm, text, shortText, callback, initialValue) {
1819
1867
  if (cm.openDialog) {
1820
- cm.openDialog(text, callback, {bottom: true});
1868
+ cm.openDialog(text, callback, { bottom: true, value: initialValue });
1821
1869
  }
1822
1870
  else {
1823
1871
  callback(prompt(shortText, ""));
@@ -1899,9 +1947,10 @@
1899
1947
  return raw;
1900
1948
  }
1901
1949
  var searchPromptDesc = '(Javascript regexp)';
1902
- function showPrompt(cm, onPromptClose, prefix, desc) {
1950
+ function showPrompt(cm, onPromptClose, prefix, desc, initialValue) {
1903
1951
  var shortText = (prefix || '') + ' ' + (desc || '');
1904
- dialog(cm, makePrompt(prefix, desc), shortText, onPromptClose);
1952
+ dialog(cm, makePrompt(prefix, desc), shortText, onPromptClose,
1953
+ initialValue);
1905
1954
  }
1906
1955
  function regexEqual(r1, r2) {
1907
1956
  if (r1 instanceof RegExp && r2 instanceof RegExp) {
@@ -1934,17 +1983,53 @@
1934
1983
  state.setQuery(query);
1935
1984
  });
1936
1985
  }
1986
+ function searchOverlay(query) {
1987
+ return {
1988
+ token: function(stream) {
1989
+ var match = stream.match(query, false);
1990
+ if (match) {
1991
+ if (!stream.sol()) {
1992
+ // Backtrack 1 to match \b
1993
+ stream.backUp(1);
1994
+ if (!query.exec(stream.next() + match[0])) {
1995
+ stream.next();
1996
+ return null;
1997
+ }
1998
+ }
1999
+ stream.match(query);
2000
+ return "searching";
2001
+ }
2002
+ while (!stream.eol()) {
2003
+ stream.next();
2004
+ if (stream.match(query, false)) break;
2005
+ }
2006
+ },
2007
+ query: query
2008
+ };
2009
+ }
1937
2010
  function highlightSearchMatches(cm, query) {
1938
- // TODO: Highlight only text inside the viewport. Highlighting everything
1939
- // is inefficient and expensive.
1940
- if (cm.lineCount() < 2000) { // This is too expensive on big documents.
1941
- var marked = [];
1942
- for (var cursor = cm.getSearchCursor(query);
1943
- cursor.findNext();) {
1944
- marked.push(cm.markText(cursor.from(), cursor.to(),
1945
- { className: 'CodeMirror-searching' }));
2011
+ if (cm.addOverlay) {
2012
+ var overlay = getSearchState(cm).getOverlay();
2013
+ if (!overlay || query != overlay.query) {
2014
+ if (overlay) {
2015
+ cm.removeOverlay(overlay);
2016
+ }
2017
+ overlay = searchOverlay(query);
2018
+ cm.addOverlay(overlay);
2019
+ getSearchState(cm).setOverlay(overlay);
2020
+ }
2021
+ } else {
2022
+ // TODO: Highlight only text inside the viewport. Highlighting everything
2023
+ // is inefficient and expensive.
2024
+ if (cm.lineCount() < 2000) { // This is too expensive on big documents.
2025
+ var marked = [];
2026
+ for (var cursor = cm.getSearchCursor(query);
2027
+ cursor.findNext();) {
2028
+ marked.push(cm.markText(cursor.from(), cursor.to(),
2029
+ { className: 'cm-searching' }));
2030
+ }
2031
+ getSearchState(cm).setMarked(marked);
1946
2032
  }
1947
- getSearchState(cm).setMarked(marked);
1948
2033
  }
1949
2034
  }
1950
2035
  function findNext(cm, prev, repeat) {
@@ -1978,20 +2063,52 @@
1978
2063
  return cursor.from();
1979
2064
  });}
1980
2065
  function clearSearchHighlight(cm) {
1981
- cm.operation(function() {
1982
- var state = getSearchState(cm);
1983
- if (!state.getQuery()) {
1984
- return;
1985
- }
1986
- var marked = state.getMarked();
1987
- if (!marked) {
1988
- return;
1989
- }
1990
- for (var i = 0; i < marked.length; ++i) {
1991
- marked[i].clear();
2066
+ if (cm.addOverlay) {
2067
+ cm.removeOverlay(getSearchState(cm).getOverlay());
2068
+ getSearchState(cm).setOverlay(null);
2069
+ } else {
2070
+ cm.operation(function() {
2071
+ var state = getSearchState(cm);
2072
+ if (!state.getQuery()) {
2073
+ return;
2074
+ }
2075
+ var marked = state.getMarked();
2076
+ if (!marked) {
2077
+ return;
2078
+ }
2079
+ for (var i = 0; i < marked.length; ++i) {
2080
+ marked[i].clear();
2081
+ }
2082
+ state.setMarked(null);
2083
+ });
2084
+ }
2085
+ }
2086
+ /**
2087
+ * Check if pos is in the specified range, INCLUSIVE.
2088
+ * Range can be specified with 1 or 2 arguments.
2089
+ * If the first range argument is an array, treat it as an array of line
2090
+ * numbers. Match pos against any of the lines.
2091
+ * If the first range argument is a number,
2092
+ * if there is only 1 range argument, check if pos has the same line
2093
+ * number
2094
+ * if there are 2 range arguments, then check if pos is in between the two
2095
+ * range arguments.
2096
+ */
2097
+ function isInRange(pos, start, end) {
2098
+ if (typeof pos != 'number') {
2099
+ // Assume it is a cursor position. Get the line number.
2100
+ pos = pos.line;
2101
+ }
2102
+ if (start instanceof Array) {
2103
+ return inArray(pos, start);
2104
+ } else {
2105
+ if (end) {
2106
+ return (pos >= start && pos <= end);
2107
+ } else {
2108
+ return pos == start;
1992
2109
  }
1993
- state.setMarked(null);
1994
- });}
2110
+ }
2111
+ }
1995
2112
 
1996
2113
  // Ex command handling
1997
2114
  // Care must be taken when adding to the default Ex command map. For any
@@ -2001,14 +2118,23 @@
2001
2118
  { name: 'map', type: 'builtIn' },
2002
2119
  { name: 'write', shortName: 'w', type: 'builtIn' },
2003
2120
  { name: 'undo', shortName: 'u', type: 'builtIn' },
2004
- { name: 'redo', shortName: 'red', type: 'builtIn' }
2121
+ { name: 'redo', shortName: 'red', type: 'builtIn' },
2122
+ { name: 'substitute', shortName: 's', type: 'builtIn'}
2005
2123
  ];
2006
- var ExCommandDispatcher = function() {
2124
+ Vim.ExCommandDispatcher = function() {
2007
2125
  this.buildCommandMap_();
2008
2126
  };
2009
- ExCommandDispatcher.prototype = {
2127
+ Vim.ExCommandDispatcher.prototype = {
2010
2128
  processCommand: function(cm, input) {
2011
- var params = this.parseInput_(input);
2129
+ var inputStream = new CodeMirror.StringStream(input);
2130
+ var params = {};
2131
+ params.input = input;
2132
+ try {
2133
+ this.parseInput_(cm, inputStream, params);
2134
+ } catch(e) {
2135
+ showConfirm(cm, e);
2136
+ return;
2137
+ }
2012
2138
  var commandName;
2013
2139
  if (!params.commandName) {
2014
2140
  // If only a line range is defined, move to the line.
@@ -2019,6 +2145,7 @@
2019
2145
  var command = this.matchCommand_(params.commandName);
2020
2146
  if (command) {
2021
2147
  commandName = command.name;
2148
+ this.parseCommandArgs_(inputStream, params, command);
2022
2149
  if (command.type == 'exToKey') {
2023
2150
  // Handle Ex to Key mapping.
2024
2151
  for (var i = 0; i < command.toKeys.length; i++) {
@@ -2038,37 +2165,63 @@
2038
2165
  }
2039
2166
  exCommands[commandName](cm, params);
2040
2167
  },
2041
- parseInput_: function(input) {
2042
- var result = {};
2043
- result.input = input;
2044
- var idx = 0;
2045
- // Trim preceding ':'.
2046
- var colons = (/^:+/).exec(input);
2047
- if (colons) {
2048
- idx += colons[0].length;
2049
- }
2050
-
2168
+ parseInput_: function(cm, inputStream, result) {
2169
+ inputStream.eatWhile(':');
2051
2170
  // Parse range.
2052
- var numberMatch = (/^(\d+)/).exec(input.substring(idx));
2053
- if (numberMatch) {
2054
- result.line = parseInt(numberMatch[1], 10);
2055
- idx += numberMatch[0].length;
2171
+ if (inputStream.eat('%')) {
2172
+ result.line = 0;
2173
+ result.lineEnd = cm.lineCount() - 1;
2174
+ } else {
2175
+ result.line = this.parseLineSpec_(cm, inputStream);
2176
+ if (result.line !== undefined && inputStream.eat(',')) {
2177
+ result.lineEnd = this.parseLineSpec_(cm, inputStream);
2178
+ }
2056
2179
  }
2057
2180
 
2058
2181
  // Parse command name.
2059
- var commandMatch = (/^(\w+)/).exec(input.substring(idx));
2182
+ var commandMatch = inputStream.match(/^(\w+)/);
2060
2183
  if (commandMatch) {
2061
2184
  result.commandName = commandMatch[1];
2062
- idx += commandMatch[1].length;
2185
+ } else {
2186
+ result.commandName = inputStream.match(/.*/)[0];
2063
2187
  }
2064
2188
 
2189
+ return result;
2190
+ },
2191
+ parseLineSpec_: function(cm, inputStream) {
2192
+ var numberMatch = inputStream.match(/^(\d+)/);
2193
+ if (numberMatch) {
2194
+ return parseInt(numberMatch[1], 10) - 1;
2195
+ }
2196
+ switch (inputStream.next()) {
2197
+ case '.':
2198
+ return cm.getCursor().line;
2199
+ case '$':
2200
+ return cm.lineCount() - 1;
2201
+ case '\'':
2202
+ var mark = getVimState(cm).marks[inputStream.next()];
2203
+ if (mark && mark.find()) {
2204
+ return mark.find().line;
2205
+ } else {
2206
+ throw "Mark not set";
2207
+ }
2208
+ break;
2209
+ default:
2210
+ inputStream.backUp(1);
2211
+ return cm.getCursor().line;
2212
+ }
2213
+ },
2214
+ parseCommandArgs_: function(inputStream, params, command) {
2215
+ if (inputStream.eol()) {
2216
+ return;
2217
+ }
2218
+ params.argString = inputStream.match(/.*/)[0];
2065
2219
  // Parse command-line arguments
2066
- var args = trim(input.substring(idx)).split(/\s+/);
2220
+ var delim = command.argDelimiter || /\s+/;
2221
+ var args = trim(params.argString).split(delim);
2067
2222
  if (args.length && args[0]) {
2068
- result.commandArgs = args;
2223
+ params.args = args;
2069
2224
  }
2070
-
2071
- return result;
2072
2225
  },
2073
2226
  matchCommand_: function(commandName) {
2074
2227
  // Return the command in the command map that matches the shortest
@@ -2095,9 +2248,9 @@
2095
2248
  }
2096
2249
  },
2097
2250
  map: function(lhs, rhs) {
2098
- if (lhs.charAt(0) == ':') {
2251
+ if (lhs != ':' && lhs.charAt(0) == ':') {
2099
2252
  var commandName = lhs.substring(1);
2100
- if (rhs.charAt(0) == ':') {
2253
+ if (rhs != ':' && rhs.charAt(0) == ':') {
2101
2254
  // Ex to Ex mapping
2102
2255
  this.commandMap_[commandName] = {
2103
2256
  name: commandName,
@@ -2113,7 +2266,7 @@
2113
2266
  };
2114
2267
  }
2115
2268
  } else {
2116
- if (rhs.charAt(0) == ':') {
2269
+ if (rhs != ':' && rhs.charAt(0) == ':') {
2117
2270
  // Key to Ex mapping.
2118
2271
  defaultKeymap.unshift({
2119
2272
  keys: parseKeyString(lhs),
@@ -2172,7 +2325,7 @@
2172
2325
 
2173
2326
  var exCommands = {
2174
2327
  map: function(cm, params) {
2175
- var mapArgs = params.commandArgs;
2328
+ var mapArgs = params.args;
2176
2329
  if (!mapArgs || mapArgs.length < 2) {
2177
2330
  if (cm) {
2178
2331
  showConfirm(cm, 'Invalid mapping: ' + params.input);
@@ -2187,6 +2340,66 @@
2187
2340
  motionArgs: { forward: false, explicitRepeat: true,
2188
2341
  linewise: true, repeat: params.line }});
2189
2342
  },
2343
+ substitute: function(cm, params) {
2344
+ var argString = params.argString;
2345
+ var slashes = findUnescapedSlashes(argString);
2346
+ if (slashes[0] !== 0) {
2347
+ showConfirm(cm, 'Substitutions should be of the form ' +
2348
+ ':s/pattern/replace/');
2349
+ return;
2350
+ }
2351
+ var regexPart = argString.substring(slashes[0] + 1, slashes[1]);
2352
+ var replacePart = '';
2353
+ var flagsPart;
2354
+ var count;
2355
+ if (slashes[1]) {
2356
+ replacePart = argString.substring(slashes[1] + 1, slashes[2]);
2357
+ }
2358
+ if (slashes[2]) {
2359
+ // After the 3rd slash, we can have flags followed by a space followed
2360
+ // by count.
2361
+ var trailing = argString.substring(slashes[2] + 1).split(' ');
2362
+ flagsPart = trailing[0];
2363
+ count = parseInt(trailing[1]);
2364
+ }
2365
+ if (flagsPart) {
2366
+ regexPart = regexPart + '/' + flagsPart;
2367
+ }
2368
+ if (regexPart) {
2369
+ // If regex part is empty, then use the previous query. Otherwise use
2370
+ // the regex part as the new query.
2371
+ updateSearchQuery(cm, regexPart, true /** ignoreCase */,
2372
+ true /** smartCase */);
2373
+ }
2374
+ var state = getSearchState(cm);
2375
+ var query = state.getQuery();
2376
+ var lineStart = params.line || 0;
2377
+ var lineEnd = params.lineEnd || lineStart;
2378
+ if (count) {
2379
+ lineStart = lineEnd;
2380
+ lineEnd = lineStart + count - 1;
2381
+ }
2382
+ var startPos = clipCursorToContent(cm, { line: lineStart, ch: 0 });
2383
+ function doReplace() {
2384
+ for (var cursor = cm.getSearchCursor(query, startPos);
2385
+ cursor.findNext() &&
2386
+ isInRange(cursor.from(), lineStart, lineEnd);) {
2387
+ var text = cm.getRange(cursor.from(), cursor.to());
2388
+ var newText = text.replace(query, replacePart);
2389
+ cursor.replace(newText);
2390
+ }
2391
+ var vim = getVimState(cm);
2392
+ if (vim.visualMode) {
2393
+ exitVisualMode(cm, vim);
2394
+ }
2395
+ }
2396
+ if (cm.compoundChange) {
2397
+ // Only exists in v2
2398
+ cm.compoundChange(doReplace);
2399
+ } else {
2400
+ cm.operation(doReplace);
2401
+ }
2402
+ },
2190
2403
  redo: CodeMirror.commands.redo,
2191
2404
  undo: CodeMirror.commands.undo,
2192
2405
  write: function(cm) {
@@ -2200,7 +2413,7 @@
2200
2413
  }
2201
2414
  };
2202
2415
 
2203
- var exCommandDispatcher = new ExCommandDispatcher();
2416
+ var exCommandDispatcher = new Vim.ExCommandDispatcher();
2204
2417
 
2205
2418
  // Register Vim with CodeMirror
2206
2419
  function buildVimKeyMap() {