Active Line Demo
+ + + + +Styling the current cursor line.
+ + + +diff --git a/Gemfile b/Gemfile
index 8033bf210..57e9d17f7 100644
--- a/Gemfile
+++ b/Gemfile
@@ -75,7 +75,7 @@ group :development, :test do
gem 'pry-byebug'
gem "test-unit", "~>3.0"
end
- gem 'rspec-rails', '~> 3.0'
+ # gem 'rspec-rails', '~> 3.0'
gem 'factory_girl_rails'
end
diff --git a/app/controllers/attachments_controller.rb b/app/controllers/attachments_controller.rb
index 35abc1a5e..6f43678a9 100644
--- a/app/controllers/attachments_controller.rb
+++ b/app/controllers/attachments_controller.rb
@@ -268,6 +268,8 @@ class AttachmentsController < ApplicationController
def upload
+
+
# Make sure that API users get used to set this content type
# as it won't trigger Rails' automatic parsing of the request body for parameters
unless request.content_type == 'application/octet-stream'
@@ -295,6 +297,26 @@ class AttachmentsController < ApplicationController
end
end
+ def upload_with_markdown
+ logger.debug "upload_with_markdown"
+
+
+ @attachment = Attachment.new(:file => params["editormd-image-file"])
+ @attachment.author = User.current
+ if !params[:project].nil?
+ @attachment.container_type = 'Project'
+ @attachment.container_id = params[:project].split("?")[0]
+ end
+ @attachment.filename = params[:filename].presence || Redmine::Utils.random_hex(16)
+ saved = @attachment.save
+
+ render json: {
+ success: saved ? 1 : 0, # | 1, // 0 表示上传失败,1 表示上传成功
+ message: "上传失败:#{ @attachment.errors.full_messages.join(',') }",
+ url: download_attachment_path(@attachment.id) # 上传成功时才返回
+ }
+ end
+
def upload_attachment_version
@flag = false
Attachment.transaction do
diff --git a/app/controllers/challenges_controller.rb b/app/controllers/challenges_controller.rb
index 5c29976e4..9095dea3d 100644
--- a/app/controllers/challenges_controller.rb
+++ b/app/controllers/challenges_controller.rb
@@ -24,34 +24,49 @@ class ChallengesController < ApplicationController
@challenge.position = challenge_count + 1
@challenge.shixun = @shixun
@challenge.user = User.current
- if @challenge.save
- if params[:sample][:input].length > 0
- params[:sample][:input].each_with_index do |value, index|
- unless (value == params[:sample][:output][index] && value.blank?)
- ChallengeSample.create(:challenge_id => @challenge.id, :input => value, :output => params[:sample][:output][index])
+ ActiveRecord::Base.transaction do
+ begin
+ @challenge.save!
+ if params[:test_set][:hidden].length > 0
+ params[:test_set][:input].each_with_index do |input, index|
+ params[:test_set][:hidden][index].to_i == 0 ? open = 1 : open = 0
+ unless (input[index] == params[:test_set][:output][index] && input[index].nil?)
+ TestSet.create(:challenge_id => @challenge.id, :input => input, :output => params[:test_set][:output][index], :is_public => open, :position => (index + 1))
+ end
end
end
- end
- if params[:program][:output].length > 0
- params[:program][:output].each do |output|
- TestSet.create(:challenge_id => @challenge.id, :input => nil, :output => output)
+ unless params[:knowledge].blank?
+ params[:knowledge][:input].each do |input|
+ ChallengeTag.create(:name => input, :challenge_id => @challenge.id)
+ end
end
- end
- unless params[:knowledge].blank?
- params[:knowledge][:input].each do |input|
- ChallengeTag.create(:name => input, :challenge_id => @challenge.id)
+ # 向jenkins端发送请求,构建pipeline片段,实时返回结果
+ jenkins_shixuns = Redmine::Configuration['jenkins_shixuns']
+ params = {:challengeStage => "#{@challenge.position}", :challengeType => "#{@challenge.evaluation_way.to_i}", :challengeProgramName => "#{@challenge.path}", :trainingID => "#{@shixun.id}"}
+ uri = URI("#{jenkins_shixuns}/jenkins-exec/game/generatePipelineScriptForChallenge")
+ res = uri_exec uri, {challengeInfo: params.to_json}
+ if res['code'] == 0
+ @challenge.update_attribute(:pipeline_script, res['msg'])
+ respond_to do |format|
+ format.html {redirect_to shixun_challenge_path(@challenge, :shixun_id => @shixun)}
+ end
+ else
+ logger.error("res['code'] is #{res['code']}, res['msg'] is #{res['code']}")
+ raise("脚本构建失败")
end
- end
- respond_to do |format|
- format.html {redirect_to shixun_challenge_path(@challenge, :shixun_id => @shixun), notice: '创建成功!'}
- end
- else
- respond_to do |format|
- format.html { render :action => 'new' }
+ rescue Exception => e
+ flash[:error] = "#{e.message}"
+ redirect_to new_shixun_challenge_path(:shixun_id => @shixun.id)
+ raise ActiveRecord::Rollback
end
end
end
+ # 处理新建challenge返回的pipeline片段
+ def fragment_from_jenkins
+
+ end
+
def index
# 顶部导航
@challenges = @shixun.challenges.order("position desc")
@@ -85,21 +100,16 @@ class ChallengesController < ApplicationController
respond_to do |format|
if @challenge.update_attributes(params[:challenge])
ActiveRecord::Base.transaction do
- @challenge_samples.delete_all unless @challenge_samples.blank?
@test_sets.delete_all unless @test_sets.blank?
- @challenge_tags.delete_all unless @challenge_tags.blank?
- if params[:sample][:input].length > 0
- params[:sample][:input].each_with_index do |value, index|
- unless (value == params[:sample][:output][index] && value.blank?)
- ChallengeSample.create(:challenge_id => @challenge.id, :input => value, :output => params[:sample][:output][index])
+ @challenge_tags.delete_all unless @challenge_tags.blank?
+ if params[:test_set][:hidden].length > 0
+ params[:test_set][:input].each_with_index do |input, index|
+ unless (input[index] == params[:test_set][:output][index] && input[index].nil?)
+ params[:test_set][:hidden][index].to_i == 0 ? open = 1 : open = 0
+ TestSet.create(:challenge_id => @challenge.id, :input => input, :output => params[:test_set][:output][index], :is_public => open, :position => (index + 1))
end
end
end
- if params[:program][:output].length > 0
- params[:program][:output].each do |output|
- TestSet.create(:challenge_id => @challenge.id, :input => nil, :output => output)
- end
- end
unless params[:knowledge].blank?
params[:knowledge][:input].each do |input|
ChallengeTag.create(:name => input, :challenge_id => @challenge.id)
@@ -181,7 +191,6 @@ class ChallengesController < ApplicationController
end
def query_challeges
- @challenge_samples = @challenge.challenge_samples
@test_sets = @challenge.test_sets
@challenge_tags = @challenge.challenge_tags
end
diff --git a/app/controllers/games_controller.rb b/app/controllers/games_controller.rb
index d919ac59a..c992a9bc1 100644
--- a/app/controllers/games_controller.rb
+++ b/app/controllers/games_controller.rb
@@ -22,8 +22,12 @@ class GamesController < ApplicationController
# mushixun的版本库必须创建时就创建
# 首次进入版本库自动打开文件
# path:"" && path: @game.path
+ # @praise 判断是否点赞
+ # @praise 判断是否踩
def show
@game_challenge = @game.challenge
+ @praise = PraiseTread.where("praise_tread_object_id=? and praise_tread_object_type=? and user_id=?", @game_challenge.id, @game_challenge.class.to_s, User.current.id).first
+ @tread = PraiseTread.where("praise_tread_object_id=? and praise_tread_object_type=? and user_id=?", @game_challenge.id, "ChallengeTread", User.current.id).first
logger.info("test ................#{@game_challenge.id}")
game_path = (@game_challenge.path.blank? ? @path : @game_challenge.path.strip)
logger.info("test game_path ................#{game_path}")
@@ -46,19 +50,14 @@ class GamesController < ApplicationController
logger.info("7777************#{@entries} ")
end
logger.info("88888************#{@entries} ")
- @latest_output = @game.latest_output.try(:out_put).to_s
- outputs = @game.outputs
- (@myshixun.games.count == @game_challenge.position && @game.status ==2) ? @had_done = 1 : @had_done = 0
- if outputs.count == 0
- @results = []
- else
- @results = outputs.map{|result| [result.code, result.id]}
+ @test_sets = @game_challenge.test_sets
+ @test_sets.each_with_index do |test_set, index|
+ @test_set_error_position = (index + 1) unless test_set.result
end
- logger.info("##################languae is #{@language}")
- logger.info("##################latest_output is #{@latest_output}")
- logger.info("##################had_done is #{@had_done}")
- logger.info("##################status is #{@game.status}")
- logger.info("##################result is #{@results}")
+ @test_sets_count = @test_sets.count
+ (@myshixun.games.count == @game_challenge.position && @game.status ==2) ? @had_done = 1 : @had_done = 0
+ @test_sets_hidden_count = @test_sets.where(:is_public => false).count
+
respond_to do |format|
format.html
format.js
@@ -110,18 +109,10 @@ class GamesController < ApplicationController
jobName = "myshixun_#{@myshixun.id}"
ActiveRecord::Base.transaction do
@game.update_attributes!(:status => 1)
- testCode = {}
- test_sets = game_challenge.test_sets
- unless test_sets.blank?
- test_sets.each_with_index do |test_set, index|
- testCode.store("testCode_#{index}",test_set.try(:output))
- end
- end
- testCode = testCode.to_json
jenkins_shixuns = Redmine::Configuration['jenkins_shixuns']
step = game_challenge.try(:position)
- params = {:jobName => "#{jobName}", :taskId => "#{taskId}", :step => "#{step}", :gitUrl => "#{gitUrl}", :testCode => "#{testCode}"}
- uri = URI("#{jenkins_shixuns}/jenkins-exec/api/buildJob")
+ params = {:jobNameForInstance => "#{jobName}", :buildID => "#{taskId}", :step => "#{step}", :instanceGitURL => "#{gitUrl}", :instanceChallenge => "#{step}"}
+ uri = URI("#{jenkins_shixuns}/jenkins-exec/api/buildJobForInstance")
res = uri_exec uri, params
if (res && res['code'] != 0)
raise("Build job failed")
@@ -167,7 +158,7 @@ class GamesController < ApplicationController
def next_step
render_403 if @game.status != 2
next_game = @game.next_game
- next_game.update_attribute(:status, 0) if next_game.status == 3
+ next_game.update_attributes(:status => 0, :open_time => Time.now) if next_game.status == 3
respond_to do |format|
format.js{redirect_to myshixun_game_path(next_game, :myshixun_id => @myshixun.id)}
diff --git a/app/controllers/myshixuns_controller.rb b/app/controllers/myshixuns_controller.rb
index b8df444b3..58098e62a 100644
--- a/app/controllers/myshixuns_controller.rb
+++ b/app/controllers/myshixuns_controller.rb
@@ -5,28 +5,33 @@ class MyshixunsController < ApplicationController
before_filter :find_myshixun, :only => [:show]
# taskId 即返回的game id
- # params [:stauts] 0 表示成功,其它则失败
+ # 返回结果:params [:stauts] 0 表示成功,其它则失败
# msg 错误信息
# output 为测试用户编译输出结果
# myshixun:status 1为完成实训
+ # @jenkins: caseId对应test_set的position,passed: 1表示成功,0表示失败
def training_task_status
- status = params[:status].to_i
- task_id = params[:taskId]
- outPut = Base64.decode64(params[:outPut]) unless params[:outPut].blank?
- message = Base64.decode64(params[:msg]) unless params[:msg].blank?
- game = Game.find(task_id)
+ jsonTestDetails = JSON.parse(params[:jsonTestDetails])
+ status = jsonTestDetails['status']
+ game_id = jsonTestDetails['buildID']
+ outPut = Base64.decode64(jsonTestDetails['outPut']) unless params[:outPut].blank?
+ jenkins_testsets = jsonTestDetails['msg']
+ # message = Base64.decode64(params[:msg]) unless params[:msg].blank?
+ game = Game.find(game_id)
challenge = game.challenge
- if status == 0
- myshixun = game.myshixun
- games_count = myshixun.games.count
- if challenge.position == games_count
- myshixun.update_attribute(:status, 1)
+ test_sets = challenge.test_sets
+ game_outputs = Output.create(:code => status, :game_id => game_id, :out_put => outPut)
+ unless jenkins_testsets.blank?
+ jenkins_testsets.each do |j_test_set|
+ test_set = TestSet.where(:position => j_test_set['caseId'], :challenge_id => challenge).first
+ test_set.update_attributes(:actual_output => j_test_set['expectedOutput'], :result => j_test_set['passed'].to_i)
end
- game_outputs = Output.create(:code => status, :msg => message, :game_id => task_id, :out_put => outPut)
+ end
+ if status == 0
game.update_attribute(:status, 2)
game.update_attribute(:final_score, challenge.score)
else
- game_outputs = Output.create(:code => status, :msg => message, :game_id => task_id, :out_put => outPut)
+
game.update_attribute(:status, 0)
end
render :json => {:data => "success"}
diff --git a/app/controllers/praise_tread_controller.rb b/app/controllers/praise_tread_controller.rb
index 136915bbd..a661892f8 100644
--- a/app/controllers/praise_tread_controller.rb
+++ b/app/controllers/praise_tread_controller.rb
@@ -17,13 +17,19 @@ class PraiseTreadController < ApplicationController
end
# @is_in_list = nil
@obj = find_object_by_type_and_id(@obj_type,@obj_id)
- pts = PraiseTread.where("praise_tread_object_id=? and praise_tread_object_type=? and user_id=?",@obj_id,@obj_type.to_s,User.current.id)
- unless pts.empty?
+ pts = PraiseTread.where("praise_tread_object_id=? and praise_tread_object_type=? and user_id=?",@obj_id,@obj_type.to_s,User.current.id).first
+ unless pts.blank?
+ if(params[:game_praise] == "true")
+ pts.delete
+ render :json => {praise: false}
+ return
+ end
respond_to do |format|
format.js
end
return
end
+
@horizontal = params[:horizontal].downcase == "false" ? false:true if params[:horizontal]
# if @obj.respond_to?("author_id")
# author_id = @obj.author_id
@@ -33,6 +39,10 @@ class PraiseTreadController < ApplicationController
# unless author_id == User.current.id
praise_tread_plus(@obj_type,@obj_id,1)
# end
+ if params[:game_praise] == "true"
+ render :json => {praise: true}
+ return
+ end
respond_to do |format|
format.js
end
@@ -137,6 +147,8 @@ class PraiseTreadController < ApplicationController
@obj = Syllabus.find_by_id(id)
when 'Work'
@obj = Work.find_by_id(id)
+ when 'Challenge' || 'ChallengeTread'
+ @obj = Challenge.find_by_id(id)
else
@obj = nil
end
diff --git a/app/controllers/shixuns_controller.rb b/app/controllers/shixuns_controller.rb
index bd92c6b9e..46a9d8093 100644
--- a/app/controllers/shixuns_controller.rb
+++ b/app/controllers/shixuns_controller.rb
@@ -5,28 +5,75 @@ class ShixunsController < ApplicationController
before_filter :require_login
before_filter :find_shixun, :except => [ :index, :new, :create]
before_filter :shixun_view_allow, :only => [:show]
- before_filter :require_manager, :only => [ :settings, :add_script]
+ before_filter :require_manager, :only => [ :settings, :add_script, :publish]
include ApplicationHelper
def shixun_monitor
- monitor_filter
- if @had_exec
- @tpm = Myshixun.where(:user_id => User.current, :shixun_id => @shixun).first
- end
+ # monitor_filter
+ # if @had_exec
+ # @tpm = Myshixun.where(:user_id => User.current, :shixun_id => @shixun).first
+ # end
+ #
+ # respond_to do |format|
+ # format.js
+ # end
+ end
- respond_to do |format|
- format.js
+ # jenkins回调脚本
+ # gameID 及实训ID
+ def publish
+ jenkins_shixuns = Redmine::Configuration['jenkins_shixuns']
+ fragments = []
+ testSet = []
+ testCases = []
+ gameInfo = []
+ begin
+ challenges = @shixun.challenges.includes(:test_sets)
+ challenges.each do |challenge|
+ pipeline_script = challenge.try(:pipeline_script)
+ challenge_script = {:challengeStage => "#{challenge.position}", :challengePipeline => "#{pipeline_script}"}
+ fragments << challenge_script
+ challenge.test_sets.each do |test_set|
+ test_cases = {:caseId => test_set.id.to_s, :input => test_set.input, :output => test_set.output}
+ testSet << test_cases
+ end
+ test_sets_list = {:challengeStage => challenge.position.to_s, :challengeTestCases => testSet}
+ testCases << test_sets_list
+ end
+ gameInfo = {:trainingID => @shixun.id.to_s, :operationEnvironment => @shixun.language, :challengePipelines => fragments, :testCases => testCases}
+ fragments = fragments.to_json
+ uri = URI("#{jenkins_shixuns}/jenkins-exec/game/publishGame")
+ params = {gameInfo:gameInfo.to_json}
+ res = uri_exec uri, params
+ if res['code'] == 0
+ @shixun.update_attribute(:status, 1)
+ @shixun.update_attribute(:script, res["msg"])
+ else
+ logger.error("res['code'] is #{res['code']}, res['msg'] is #{res['code']}")
+ @jenkin_error = true
+ raise("服务器异常,请联系系统管理员")
+ end
+ rescue
+ respond_to do |format|
+ format.js
+ end
end
end
# copy_shixun:复制一个新的实训模块包括版本库
# copy_myshixun自动创建系列game,game中只包含状态等信息,公共信息从Challeges中读取
- #
+ # 开启过实训的则直接跳入my实训页面
def shixun_exec
- if has_exec_cur_shixun(@shixun) || User.current.id == @shixun.user_id
+ unless allow_shixun_exec(@shixun)
render_403
end
+ myshixun = Myshixun.where(:user_id => User.current.id, :shixun_id => @shixun.id).first
+ unless myshixun.blank?
+ logger.info("current task id is #{myshixun.current_task}")
+ redirect_to myshixun_game_path(myshixun.current_task, :myshixun_id => myshixun)
+ return
+ end
repository = @shixun.repository
ActiveRecord::Base.transaction do
begin
@@ -42,17 +89,17 @@ class ShixunsController < ApplicationController
# 之所以增加user_id是为了方便统计查询性能
challenges.each_with_index do |challenge, index|
status = (index == 0 ? 0 : 3)
- Game.create!(:challenge_id => challenge.id, :myshixun_id => myshixun.id, :status => status, :user_id => myshixun.user_id)
+ Game.create!(:challenge_id => challenge.id, :myshixun_id => myshixun.id, :status => status, :user_id => myshixun.user_id, :open_time => Time.now)
end
else
raise("网络异常,请稍后重试")
end
- respond_to do |format|
- format.html{myshixun_game_url(myshixun.current_task, :myshixun_id => myshixun)}
- end
+ redirect_to myshixun_game_path(myshixun.current_task, :myshixun_id => myshixun)
rescue Exception => e
- flash[:error] = l(:notice_shixun_failed_exec)+ " : " + e.message
+ flash[:error] = e.message
+ logger.info("failed to exec shixun: current task id is #{e}")
@g.delete_project(gshixun.id) if !gshixun.id.nil?
+ redirect_to shixun_challenges_path(@shixun)
raise ActiveRecord::Rollback
end
end
@@ -92,7 +139,6 @@ class ShixunsController < ApplicationController
end
def edit
-
end
def update
@@ -186,13 +232,14 @@ class ShixunsController < ApplicationController
jenkins_shixuns = Redmine::Configuration['jenkins_shixuns']
if myshixun.save!
MyshixunMember.create!(:myshixun_id => myshixun.id, :user_id => User.current.id, :role => 1)
- uri = URI("#{jenkins_shixuns}/jenkins-exec/api/createJob")
- pipeLine = "#{Base64.encode64(shixun.script)}"
- params = {jobName: "myshixun_#{myshixun.id}", pipeLine: pipeLine}
- res = uri_exec uri, params
rep = Repository.new(:myshixun_id => myshixun.id, :identifier => gshixun.name,:project_id => -1, :shixun_id => -2)
rep.type = "Repository::Gitlab"
rep.save!
+ rep_url = git_repository_url(myshixun, "Myshixun")
+ uri = URI("#{jenkins_shixuns}/jenkins-exec/game/openGameInstance")
+ pipeLine = "#{shixun.script}"
+ params = {jobNameForInstance: "myshixun_#{myshixun.id}", gamePipelineScript: pipeLine, instanceGitURL:rep_url}
+ res = uri_exec uri, params
if (res && res['code'] != 0)
raise("Job 创建失败")
end
@@ -219,7 +266,7 @@ class ShixunsController < ApplicationController
# 实训管理员或者超级管理员
def require_manager
- render_403 unless User.current.manager_of_shixun?(@shixun)
+ render_403 unless (User.current.manager_of_shixun?(@shixun) || User.current.admin?)
end
end
diff --git a/app/controllers/words_controller.rb b/app/controllers/words_controller.rb
index f8d1290f4..b3e8ec91c 100644
--- a/app/controllers/words_controller.rb
+++ b/app/controllers/words_controller.rb
@@ -514,8 +514,19 @@ class WordsController < ApplicationController
if params[:text].size>0 && @user
# @user.add_jour(me, params[:text])
#私信
+ if params[:shixun_name]
+ game_id = params[:game_id]
+ message = params[:text]
+ admin_id = User.where(:admin => 1).first.try(:id)
+ user_ids = [admin_id, params[:shixun_author_id].to_i]
+ user_ids.each do |user_id|
+ JournalsForMessage.create(:jour_id => game_id, :jour_type => "Game", :user_id => user_id, :notes => message, :reply_id => 0, :status => true, :is_readed => false, :private => 1)
+ end
+ else
message = "【未收到激活邮件的用户反馈,用户邮箱:"+me.mail+"】
"+params[:text]
@user.journals_for_messages << JournalsForMessage.new(:user_id => me.id, :notes => message, :reply_id => 0, :status => true, :is_readed => false, :private => 1)
+ end
+
else
status = 0
end
@@ -524,6 +535,7 @@ class WordsController < ApplicationController
render_403
end
end
+
private
diff --git a/app/helpers/application_helper.rb b/app/helpers/application_helper.rb
index 2d73a75e9..c2f0c18d9 100644
--- a/app/helpers/application_helper.rb
+++ b/app/helpers/application_helper.rb
@@ -50,6 +50,10 @@ module ApplicationHelper
end
# 定义实训相关方法
+ def sum_final_score
+ Game.find_by_sql("SELECT sum(final_score) as total_score FROM `games` where user_id=#{User.current.id}").first.try(:total_score)
+ end
+
# myshixun 最高分
def top_score shixun, position
Game.find_by_sql("SELECT max(final_score) as top_score FROM `games` g, `challenges` c where g.challenge_id = c.id and c.position=#{position} and g.myshixun_id in (SELECT id FROM `myshixuns` ms where ms.shixun_id=#{shixun.id})").first.try(:top_score)
@@ -118,7 +122,7 @@ module ApplicationHelper
time = "#{hour}小时 : #{min}分"
end
else
- time = "#{day}天 : #{hour}小时 : #{min}分"
+ time = "#{day}天 #{hour}小时 #{min}分"
end
return time
end
@@ -181,7 +185,23 @@ module ApplicationHelper
end
def uri_exec uri, params
- res = Net::HTTP.post_form(uri, params).body
+ begin
+ logger.error("@parmas is #{params}, url is #{uri}")
+ res = Net::HTTP.post_form(uri, params).body
+ res = JSON.parse(res)
+ rescue => e
+ logger.error("failed to create challenge by 'generatePipelineScriptForChallenge'! #{e}")
+ raise("Jenkins服务器异常")
+ end
+ end
+
+ def uri_json_exec uri, params
+ Net::HTTP.start(uri.hostname, uri.port) {|http|
+ http.post(uri.path, params.to_json)
+ }
+
+ http = Net::HTTP.new(uri.host, uri.port)
+ res = Net::HTTP.post(uri, params.to_json).body
res = JSON.parse(res)
end
@@ -276,7 +296,7 @@ module ApplicationHelper
end
def allow_shixun_exec shixun
- (User.current.logged? && User.current.id != shixun.user_id && shixun.status == 1)
+ (User.current.logged? && shixun.challenges.count > 0 && !shixun.repository.blank?)
end
# 通过系统外部邮箱查找用户,如果用户不存在则用邮箱替换
diff --git a/app/helpers/challenges_helper.rb b/app/helpers/challenges_helper.rb
index 86b0183c9..06d71f9bb 100644
--- a/app/helpers/challenges_helper.rb
+++ b/app/helpers/challenges_helper.rb
@@ -1,2 +1,14 @@
+# encoding: utf-8
module ChallengesHelper
+
+ def difficulty_type difficulty
+ case difficulty
+ when 1
+ "简单"
+ when 2
+ "中等"
+ when 3
+ "复杂"
+ end
+ end
end
diff --git a/app/models/game.rb b/app/models/game.rb
index 3810c91e9..9bff76d7e 100644
--- a/app/models/game.rb
+++ b/app/models/game.rb
@@ -1,7 +1,7 @@
class Game < ActiveRecord::Base
# stauts 0: can exe 1:doing 2:successed 3:locked
default_scope :order => 'created_at desc'
- attr_accessible :myshixun_id, :user_id, :status, :final_score, :challenge_id
+ attr_accessible :myshixun_id, :user_id, :status, :final_score, :challenge_id, :open_time
belongs_to :myshixun,:touch=> true
belongs_to :user
belongs_to :challenge
@@ -9,6 +9,14 @@ class Game < ActiveRecord::Base
has_many :test_sets, :dependent => :destroy
has_many :challenge_samples, :dependent => :destroy
+ def last_game
+ challenge = self.challenge
+ last_challenge_id = challenge.last_challenge
+ game = Game.where(:myshixun_id => self.myshixun_id, :challenge_id => last_challenge_id).first
+ render_404 if game.nil?
+ return game
+ end
+
def next_game
challenge = self.challenge
next_challenge_id = challenge.next_challenge
diff --git a/app/models/journals_for_message.rb b/app/models/journals_for_message.rb
index d1f32f82d..885bdc0b8 100644
--- a/app/models/journals_for_message.rb
+++ b/app/models/journals_for_message.rb
@@ -322,6 +322,9 @@ class JournalsForMessage < ActiveRecord::Base
receivers.each do |r|
self.user_feedback_messages << UserFeedbackMessage.new(:user_id => r.id, :journals_for_message_id => self.id, :journals_for_message_type => "Principal", :viewed => false)
end
+ elsif self.jour_type == 'Game'
+ # game不存在回复
+ self.user_feedback_messages << UserFeedbackMessage.new(:user_id => self.user_id, :journals_for_message_id => self.id, :journals_for_message_type => "Game", :viewed => false)
end
end
diff --git a/app/models/test_set.rb b/app/models/test_set.rb
index b42172970..86ae6887b 100644
--- a/app/models/test_set.rb
+++ b/app/models/test_set.rb
@@ -1,4 +1,5 @@
class TestSet < ActiveRecord::Base
- attr_accessible :challenge_id, :input, :output, :game_id
+ default_scope :order => 'id asc'
+ attr_accessible :challenge_id, :input, :output, :game_id, :actual_output, :is_public, :result, :position
belongs_to :challenge
end
diff --git a/app/views/challenges/_form.html.erb b/app/views/challenges/_form.html.erb
index ababe3b8e..4aaf0b552 100644
--- a/app/views/challenges/_form.html.erb
+++ b/app/views/challenges/_form.html.erb
@@ -4,20 +4,60 @@
<%= stylesheet_link_tag '/editormd/css/editormd','/editormd/css/editormd.min' %>
<%= javascript_include_tag '/editormd/editormd.min.js','/editormd/examples/js/jquery.min.js' %>
-
+
@@ -25,24 +65,16 @@
- 样例<%= index + 1 %> + 组<%= index + 1 %> + <% if test_set.is_public? %> + 锁定 + <% else %> + 锁定 + <% end %> + <% if index == 0 %> - 温馨提示:输入样例供学员参考。 + 温馨提示:选中“锁定”,则对学员隐藏该条测试集,但在“提交评测”时也将被自动检测。 <% end %> <% if index > 0 %> <% end %>
- - + +- 样例<%= index + 1 %> -
-<%= sample.input %>
<%= sample.output %>
测试集<%= index + 1 %> + <% if test.is_public? %> + + <% else %> + + <% end %>
-<%= test.output %>
<%= test.input %>
<%= test.output %>
<%= @test_sets_count %>/<%= @test_sets_count %> 全部通过
+<% else %> +<%= @test_set_error_position %>/<%= @test_sets_count %> 编译错误
+<% end %> \ No newline at end of file diff --git a/app/views/games/_game_results.html.erb b/app/views/games/_game_results.html.erb new file mode 100644 index 000000000..d364133fa --- /dev/null +++ b/app/views/games/_game_results.html.erb @@ -0,0 +1,301 @@ +<%= content_for(:header_tags) do %> + <%= javascript_include_tag 'baiduTemplate', 'jquery.datetimepicker.js' %> +<% end %> + + + + + + + + + + + +<% if false %> +- 已闯关:<%= had_pass(@myshixun.shixun_id, game.challenge.try(:position)) %>人 - 平均耗时:<%= game_spend_time(avg_spend_time(@myshixun.shixun_id, game.challenge.try(:position)), 0) %> -
-- 已闯关:<%= had_pass(@myshixun.shixun_id, game.challenge.try(:position)) %>人 - 平均耗时:<%= game_spend_time(avg_spend_time(@myshixun.shixun_id, game.challenge.try(:position)), 0) %> -
-- 开始时间:<%= format_time(game.created_at) %> - 完成时间:<%= format_time(game.updated_at) %> - 耗时:<%= game_spend_time(game.created_at, game.updated_at) %> - 已闯关:<%= had_pass(@myshixun.shixun_id, game.challenge.try(:position)) %>人 - 得分:<%= game.final_score %>分 -
+- 已闯关:<%= had_pass(@myshixun.shixun_id, game.challenge.try(:position)) %>人 - 平均耗时:<%= game_spend_time(avg_spend_time(@myshixun.shixun_id, game.challenge.try(:position)), 0) %> -
-这个任务有什么问题或错误?
+请尽可能清晰的描述
+ +实训名称不能为空
<%= @shixun.name %>
<% if allow_shixun_exec(@shixun) %> - <%= link_to "开始挑战", shixun_monitor_shixun_path(@shixun), - :class => "fr task-btn task-btn-orange", - :id => "challenge_begin", - :remote => true %> + <% if User.current.id == @shixun.user_id %> + <% if @shixun.status == 1 %> + 已发布 + <% else %> + <%= link_to "发布实训", publish_shixun_path(@shixun), :class => "fr task-btn task-btn-orange", :id => "challenge_begin", :remote => true %> + <% end %> + <% else %> + <% if @shixun.status == 1 %> + <%= link_to "开始挑战", shixun_exec_shixun_path(@shixun), :class => "fr task-btn task-btn-orange", :id => "challenge_begin" %> + <% end %> + <% end %> <% end %>Styling the current cursor line.
+ + + +Press ctrl-space to activate autocompletion. The +completion uses +the anyword-hint.js +module, which simply looks at nearby words in the buffer and completes +to those.
+ + +Demonstration of bi-directional text support. See + the related + blog post for more background.
+ +Demonstration of
+ using linked documents
+ to provide a split view on a document, and
+ using swapDoc
+ to use a single editor to display multiple documents.
On changes to the content of the above editor, a (crude) script +tries to auto-detect the language used, and switches the editor to +either JavaScript or Scheme mode based on that.
+ + +Press ctrl-space to activate autocompletion. Built
+on top of the show-hint
+and javascript-hint
+addons.
The emacs keybindings are enabled by
+including keymap/emacs.js and setting
+the keyMap option to "emacs". Because
+CodeMirror's internal API is quite different from Emacs, they are only
+a loose approximation of actual emacs bindings, though.
Also note that a lot of browsers disallow certain keys from being +captured. For example, Chrome blocks both Ctrl-W and Ctrl-N, with the +result that idiomatic use of Emacs keys will constantly close your tab +or open a new window.
+ + + +Demonstration of + the fullscreen + addon. Press F11 when cursor is in the editor to + toggle full screen editing. Esc can also be used + to exit full screen editing.
+Demonstration of
+the hardwrap addon.
+The above editor has its change event hooked up to
+the wrapParagraphsInRange method, so that the paragraphs
+are reflown as you are typing.
Shows the XML completer + parameterized with information about the tags in HTML. + Press ctrl-space to activate completion.
+ + + + +This page uses a hack on top of the "renderLine"
+ event to make wrapped text line up with the base indentation of
+ the line.
Current mode: text/plain
+ +Filename, mime, or mode name:
+ + +Click the line-number gutter to add or remove 'breakpoints'.
+ + + +Simple addon to easily mark (and style) selected text. Docs.
+ +Search and highlight occurences of the selected text.
+ +Put the cursor on or inside a pair of tags to highlight them. + Press Ctrl-J to jump to the tag that matches the one under the + cursor.
+The merge
+addon provides an interface for displaying and merging diffs,
+either two-way
+or three-way.
+The left (or center) pane is editable, and the differences with the
+other pane(s) are optionally shown live as you edit
+it. In the two-way configuration, there are also options to pad changed
+sections to align them, and to collapse unchanged
+stretches of text.
This addon depends on +the google-diff-match-patch +library to compute the diffs.
+ + +Demonstration of a multiplexing mode, which, at certain
+ boundary strings, switches to one or more inner modes. The out
+ (HTML) mode does not get fed the content of the <<
+ >> blocks. See
+ the manual and
+ the source for more
+ information.
Demonstration of a mode that parses HTML, highlighting
+ the Mustache templating
+ directives inside of it by using the code
+ in overlay.js. View
+ source to see the 15 lines of code needed to accomplish this.
+ The panel
+ addon allows you to display panels above or below an editor.
+
+ Click the links below to add panels at the given position:
+
+ top + after-top + before-bottom + bottom +
++ You can also replace an existing panel: +
+ + + + +The placeholder
+ plug-in adds an option placeholder that can be set to
+ make text appear in the editor when it is empty and not focused.
+ If the source textarea has a placeholder attribute,
+ it will automatically be inherited.
This demo does the same thing as + the HTML5 completion demo, but + loads its dependencies + with Require.js, rather than + explicitly. Press ctrl-space to activate + completion.
+ + + + + + +By setting an editor's height style
+to auto and giving
+the viewportMargin
+a value of Infinity, CodeMirror can be made to
+automatically resize to fit its content.
Demonstration of +the rulers addon, which +displays vertical lines at given column offsets.
+ +Running a CodeMirror mode outside of the editor.
+ The CodeMirror.runMode function, defined
+ in addon/runmode/runmode.js takes the following arguments:
text (string)mode (mode spec)output (function or DOM node)null for unstyled tokens). If it is a DOM node,
+ the tokens will be converted to span elements as in
+ an editor, and inserted into the node
+ (through innerHTML).Demonstration of primitive search/replace functionality. The + keybindings (which can be configured with custom keymaps) are:
+Searching is enabled by + including addon/search/search.js + and addon/search/searchcursor.js. + Jump to line - including addon/search/jump-to-line.js.
+For good-looking input dialogs, you also want to include + addon/dialog/dialog.js + and addon/dialog/dialog.css.
+The mode/simple
+addon allows CodeMirror modes to be specified using a relatively simple
+declarative format. This format is not as powerful as writing code
+directly against the mode
+interface, but is a lot easier to get started with, and
+sufficiently expressive for many simple language modes.
This interface is still in flux. It is unlikely to be scrapped or +overhauled completely, so do start writing code against it, but +details might change as it stabilizes, and you might have to tweak +your code when upgrading.
+ +Simple modes (loosely based on +the Common +JavaScript Syntax Highlighting Specification, which never took +off), are state machines, where each state has a number of rules that +match tokens. A rule describes a type of token that may occur in the +current state, and possibly a transition to another state caused by +that token.
+ +The CodeMirror.defineSimpleMode(name, states) method
+takes a mode name and an object that describes the mode's states. The
+editor below shows an example of such a mode (and is itself
+highlighted by the mode shown in it).
Each state is an array of rules. A rule may have the following properties:
+ +regex: string | RegExpignoreCase flag
+ will be taken into account when matching the token. This regex
+ should only capture groups when the token property is
+ an array.token: string | nullregex for
+ this rule captures groups, it must capture all of the
+ string (since JS provides no way to find out where a group matched),
+ and this property must hold an array of token styles that has one
+ style for each matched group.sol: boolean^ regexp marker doesn't work as you'd expect in
+ this context because of limitations in JavaScript's RegExp
+ API.)next: stringnext property is present, the mode will
+ transfer to the state named by the property when the token is
+ encountered.push: stringnext, but instead replacing the current state
+ by the new state, the current state is kept on a stack, and can be
+ returned to with the pop directive.pop: boolmode: {spec, end, persistent}spec property that describes
+ the embedded mode, and an optional end end property
+ that specifies the regexp that will end the extent of the mode. When
+ a persistent property is set (and true), the nested
+ mode's state will be preserved between occurrences of the mode.indent: booldedent: booldedentIfLineStart: booldedent property set, it will, by
+ default, cause lines where it appears at the start to be dedented.
+ Set this property to false to prevent that behavior.The meta property of the states object is special, and
+will not be interpreted as a state. Instead, properties set on it will
+be set on the mode, which is useful for properties
+like lineComment,
+which sets the comment style for a mode. The simple mode addon also
+recognizes a few such properties:
dontIndentStates: array<string>The simplescrollbars addon defines two
+styles of non-native scrollbars: "simple" and "overlay" (click to try), which can be passed to
+the scrollbarStyle option. These implement
+the scrollbar using DOM elements, allowing more control over
+its appearance.
+
+
+
+This is a hack to automatically derive
+ a spanAffectsWrapping regexp for a browser. See the
+ comments above that variable
+ in lib/codemirror.js
+ for some more details.
The sublime keymap defines many Sublime Text-specific
+bindings for CodeMirror. See the code below for an overview.
Enable the keymap by
+loading keymap/sublime.js
+and setting
+the keyMap
+option to "sublime".
(A lot of the search functionality is still missing.) + + + +
Demonstrates integration of Tern +and CodeMirror. The following keys are bound:
+ +Documentation is sparse for now. See the top of +the script for a rough API +overview.
+ + + +Select a theme: +
+ + +Uses +the trailingspace +addon to highlight trailing whitespace.
+ +Note: The CodeMirror vim bindings do not have an +active maintainer. That means that if you report bugs in it, they are +likely to go unanswered. It also means that if you want to help, you +are very welcome to look +at the +open issues and see which ones you can solve.
+ + +The vim keybindings are enabled by including keymap/vim.js and setting the
+keyMap option to vim.
Features
+ +For the full list of key mappings and Ex commands, refer to the
+defaultKeymap and defaultExCommandMap at the
+top of keymap/vim.js.
+
+
Note that while the vim mode tries to emulate the most useful +features of vim as faithfully as possible, it does not strive to +become a complete vim implementation
+ + + +Tabs inside the editor are spans with the
+class cm-tab, and can be styled.
This demo runs JSHint over the code +in the editor (which is the script used on this page), and +inserts line widgets to +display the warnings that JSHint comes up with.
+Press ctrl-space, or type a '<' character to + activate autocompletion. This demo defines a simple schema that + guides completion. The schema can be customized—see + the manual.
+ +Development of the xml-hint addon was kindly
+ sponsored
+ by www.xperiment.mobi.
+
+
+
+ Topic: JavaScript, code editor implementation
+ Author: Marijn Haverbeke
+ Date: March 2nd 2011 (updated November 13th 2011)
+
Caution: this text was written briefly after +version 2 was initially written. It no longer (even including the +update at the bottom) fully represents the current implementation. I'm +leaving it here as a historic document. For more up-to-date +information, look at the entries +tagged cm-internals +on my blog.
+ +This is a followup to +my Brutal Odyssey to the +Dark Side of the DOM Tree story. That one describes the +mind-bending process of implementing (what would become) CodeMirror 1. +This one describes the internals of CodeMirror 2, a complete rewrite +and rethink of the old code base. I wanted to give this piece another +Hunter Thompson copycat subtitle, but somehow that would be out of +place—the process this time around was one of straightforward +engineering, requiring no serious mind-bending whatsoever.
+ +So, what is wrong with CodeMirror 1? I'd estimate, by mailing list +activity and general search-engine presence, that it has been +integrated into about a thousand systems by now. The most prominent +one, since a few weeks, +being Google +code's project hosting. It works, and it's being used widely.
+ +Still, I did not start replacing it because I was bored. CodeMirror
+1 was heavily reliant on designMode
+or contentEditable (depending on the browser). Neither of
+these are well specified (HTML5 tries
+to specify
+their basics), and, more importantly, they tend to be one of the more
+obscure and buggy areas of browser functionality—CodeMirror, by using
+this functionality in a non-typical way, was constantly running up
+against browser bugs. WebKit wouldn't show an empty line at the end of
+the document, and in some releases would suddenly get unbearably slow.
+Firefox would show the cursor in the wrong place. Internet Explorer
+would insist on linkifying everything that looked like a URL or email
+address, a behaviour that can't be turned off. Some bugs I managed to
+work around (which was often a frustrating, painful process), others,
+such as the Firefox cursor placement, I gave up on, and had to tell
+user after user that they were known problems, but not something I
+could help.
Also, there is the fact that designMode (which seemed
+to be less buggy than contentEditable in Webkit and
+Firefox, and was thus used by CodeMirror 1 in those browsers) requires
+a frame. Frames are another tricky area. It takes some effort to
+prevent getting tripped up by domain restrictions, they don't
+initialize synchronously, behave strangely in response to the back
+button, and, on several browsers, can't be moved around the DOM
+without having them re-initialize. They did provide a very nice way to
+namespace the library, though—CodeMirror 1 could freely pollute the
+namespace inside the frame.
Finally, working with an editable document means working with
+selection in arbitrary DOM structures. Internet Explorer (8 and
+before) has an utterly different (and awkward) selection API than all
+of the other browsers, and even among the different implementations of
+document.selection, details about how exactly a selection
+is represented vary quite a bit. Add to that the fact that Opera's
+selection support tended to be very buggy until recently, and you can
+imagine why CodeMirror 1 contains 700 lines of selection-handling
+code.
And that brings us to the main issue with the CodeMirror 1 +code base: The proportion of browser-bug-workarounds to real +application code was getting dangerously high. By building on top of a +few dodgy features, I put the system in a vulnerable position—any +incompatibility and bugginess in these features, I had to paper over +with my own code. Not only did I have to do some serious stunt-work to +get it to work on older browsers (as detailed in the +previous story), things +also kept breaking in newly released versions, requiring me to come up +with new scary hacks in order to keep up. This was starting +to lose its appeal.
+ +What CodeMirror 2 does is try to sidestep most of the hairy hacks +that came up in version 1. I owe a lot to the +ACE editor for inspiration on how to +approach this.
+ +I absolutely did not want to be completely reliant on key events to +generate my input. Every JavaScript programmer knows that key event +information is horrible and incomplete. Some people (most awesomely +Mihai Bazon with Ymacs) have been able +to build more or less functioning editors by directly reading key +events, but it takes a lot of work (the kind of never-ending, fragile +work I described earlier), and will never be able to properly support +things like multi-keystoke international character +input. [see below for caveat]
+ +So what I do is focus a hidden textarea, and let the browser +believe that the user is typing into that. What we show to the user is +a DOM structure we built to represent his document. If this is updated +quickly enough, and shows some kind of believable cursor, it feels +like a real text-input control.
+ +Another big win is that this DOM representation does not have to
+span the whole document. Some CodeMirror 1 users insisted that they
+needed to put a 30 thousand line XML document into CodeMirror. Putting
+all that into the DOM takes a while, especially since, for some
+reason, an editable DOM tree is slower than a normal one on most
+browsers. If we have full control over what we show, we must only
+ensure that the visible part of the document has been added, and can
+do the rest only when needed. (Fortunately, the onscroll
+event works almost the same on all browsers, and lends itself well to
+displaying things only as they are scrolled into view.)
ACE uses its hidden textarea only as a text input shim, and does +all cursor movement and things like text deletion itself by directly +handling key events. CodeMirror's way is to let the browser do its +thing as much as possible, and not, for example, define its own set of +key bindings. One way to do this would have been to have the whole +document inside the hidden textarea, and after each key event update +the display DOM to reflect what's in that textarea.
+ +That'd be simple, but it is not realistic. For even medium-sized +document the editor would be constantly munging huge strings, and get +terribly slow. What CodeMirror 2 does is put the current selection, +along with an extra line on the top and on the bottom, into the +textarea.
+ +This means that the arrow keys (and their ctrl-variations), home, +end, etcetera, do not have to be handled specially. We just read the +cursor position in the textarea, and update our cursor to match it. +Also, copy and paste work pretty much for free, and people get their +native key bindings, without any special work on my part. For example, +I have emacs key bindings configured for Chrome and Firefox. There is +no way for a script to detect this. [no longer the case]
+ +Of course, since only a small part of the document sits in the +textarea, keys like page up and ctrl-end won't do the right thing. +CodeMirror is catching those events and handling them itself.
+Getting and setting the selection range of a textarea in modern
+browsers is trivial—you just use the selectionStart
+and selectionEnd properties. On IE you have to do some
+insane stuff with temporary ranges and compensating for the fact that
+moving the selection by a 'character' will treat \r\n as a single
+character, but even there it is possible to build functions that
+reliably set and get the selection range.
But consider this typical case: When I'm somewhere in my document, +press shift, and press the up arrow, something gets selected. Then, if +I, still holding shift, press the up arrow again, the top of my +selection is adjusted. The selection remembers where its head +and its anchor are, and moves the head when we shift-move. +This is a generally accepted property of selections, and done right by +every editing component built in the past twenty years.
+ +But not something that the browser selection APIs expose.
+ +Great. So when someone creates an 'upside-down' selection, the next +time CodeMirror has to update the textarea, it'll re-create the +selection as an 'upside-up' selection, with the anchor at the top, and +the next cursor motion will behave in an unexpected way—our second +up-arrow press in the example above will not do anything, since it is +interpreted in exactly the same way as the first.
+ +No problem. We'll just, ehm, detect that the selection is +upside-down (you can tell by the way it was created), and then, when +an upside-down selection is present, and a cursor-moving key is +pressed in combination with shift, we quickly collapse the selection +in the textarea to its start, allow the key to take effect, and then +combine its new head with its old anchor to get the real +selection.
+ +In short, scary hacks could not be avoided entirely in CodeMirror +2.
+ +And, the observant reader might ask, how do you even know that a +key combo is a cursor-moving combo, if you claim you support any +native key bindings? Well, we don't, but we can learn. The editor +keeps a set known cursor-movement combos (initialized to the +predictable defaults), and updates this set when it observes that +pressing a certain key had (only) the effect of moving the cursor. +This, of course, doesn't work if the first time the key is used was +for extending an inverted selection, but it works most of the +time.
+One thing that always comes up when you have a complicated internal +state that's reflected in some user-visible external representation +(in this case, the displayed code and the textarea's content) is +keeping the two in sync. The naive way is to just update the display +every time you change your state, but this is not only error prone +(you'll forget), it also easily leads to duplicate work on big, +composite operations. Then you start passing around flags indicating +whether the display should be updated in an attempt to be efficient +again and, well, at that point you might as well give up completely.
+ +I did go down that road, but then switched to a much simpler model: +simply keep track of all the things that have been changed during an +action, and then, only at the end, use this information to update the +user-visible display.
+ +CodeMirror uses a concept of operations, which start by
+calling a specific set-up function that clears the state and end by
+calling another function that reads this state and does the required
+updating. Most event handlers, and all the user-visible methods that
+change state are wrapped like this. There's a method
+called operation that accepts a function, and returns
+another function that wraps the given function as an operation.
It's trivial to extend this (as CodeMirror does) to detect nesting, +and, when an operation is started inside an operation, simply +increment the nesting count, and only do the updating when this count +reaches zero again.
+ +If we have a set of changed ranges and know the currently shown +range, we can (with some awkward code to deal with the fact that +changes can add and remove lines, so we're dealing with a changing +coordinate system) construct a map of the ranges that were left +intact. We can then compare this map with the part of the document +that's currently visible (based on scroll offset and editor height) to +determine whether something needs to be updated.
+ +CodeMirror uses two update algorithms—a full refresh, where it just +discards the whole part of the DOM that contains the edited text and +rebuilds it, and a patch algorithm, where it uses the information +about changed and intact ranges to update only the out-of-date parts +of the DOM. When more than 30 percent (which is the current heuristic, +might change) of the lines need to be updated, the full refresh is +chosen (since it's faster to do than painstakingly finding and +updating all the changed lines), in the other case it does the +patching (so that, if you scroll a line or select another character, +the whole screen doesn't have to be +re-rendered). [the full-refresh +algorithm was dropped, it wasn't really faster than the patching +one]
+ +All updating uses innerHTML rather than direct DOM
+manipulation, since that still seems to be by far the fastest way to
+build documents. There's a per-line function that combines the
+highlighting, marking, and
+selection info for that line into a snippet of HTML. The patch updater
+uses this to reset individual lines, the refresh updater builds an
+HTML chunk for the whole visible document at once, and then uses a
+single innerHTML update to do the refresh.
When I wrote CodeMirror 1, I +thought interruptable +parsers were a hugely scary and complicated thing, and I used a +bunch of heavyweight abstractions to keep this supposed complexity +under control: parsers +were iterators +that consumed input from another iterator, and used funny +closure-resetting tricks to copy and resume themselves.
+ +This made for a rather nice system, in that parsers formed strictly +separate modules, and could be composed in predictable ways. +Unfortunately, it was quite slow (stacking three or four iterators on +top of each other), and extremely intimidating to people not used to a +functional programming style.
+ +With a few small changes, however, we can keep all those +advantages, but simplify the API and make the whole thing less +indirect and inefficient. CodeMirror +2's mode API uses explicit state +objects, and makes the parser/tokenizer a function that simply takes a +state and a character stream abstraction, advances the stream one +token, and returns the way the token should be styled. This state may +be copied, optionally in a mode-defined way, in order to be able to +continue a parse at a given point. Even someone who's never touched a +lambda in his life can understand this approach. Additionally, far +fewer objects are allocated in the course of parsing now.
+ +The biggest speedup comes from the fact that the parsing no longer +has to touch the DOM though. In CodeMirror 1, on an older browser, you +could see the parser work its way through the document, +managing some twenty lines in each 50-millisecond time slice it got. It +was reading its input from the DOM, and updating the DOM as it went +along, which any experienced JavaScript programmer will immediately +spot as a recipe for slowness. In CodeMirror 2, the parser usually +finishes the whole document in a single 100-millisecond time slice—it +manages some 1500 lines during that time on Chrome. All it has to do +is munge strings, so there is no real reason for it to be slow +anymore.
+Given all this, what can you expect from CodeMirror 2?
+ +iframe nodes aren't
+really known for respecting document flow. Now that an editor instance
+is a plain div element, it is much easier to size it to
+fit the surrounding elements. You don't even have to make it scroll if
+you do not want to.On the downside, a CodeMirror 2 instance is not a native +editable component. Though it does its best to emulate such a +component as much as possible, there is functionality that browsers +just do not allow us to hook into. Doing select-all from the context +menu, for example, is not currently detected by CodeMirror.
+ +[Updates from November 13th 2011] Recently, I've made +some changes to the codebase that cause some of the text above to no +longer be current. I've left the text intact, but added markers at the +passages that are now inaccurate. The new situation is described +below.
+The original implementation of CodeMirror 2 represented the
+document as a flat array of line objects. This worked well—splicing
+arrays will require the part of the array after the splice to be
+moved, but this is basically just a simple memmove of a
+bunch of pointers, so it is cheap even for huge documents.
However, I recently added line wrapping and code folding (line +collapsing, basically). Once lines start taking up a non-constant +amount of vertical space, looking up a line by vertical position +(which is needed when someone clicks the document, and to determine +the visible part of the document during scrolling) can only be done +with a linear scan through the whole array, summing up line heights as +you go. Seeing how I've been going out of my way to make big documents +fast, this is not acceptable.
+ +The new representation is based on a B-tree. The leaves of the tree +contain arrays of line objects, with a fixed minimum and maximum size, +and the non-leaf nodes simply hold arrays of child nodes. Each node +stores both the amount of lines that live below them and the vertical +space taken up by these lines. This allows the tree to be indexed both +by line number and by vertical position, and all access has +logarithmic complexity in relation to the document size.
+ +I gave line objects and tree nodes parent pointers, to the node +above them. When a line has to update its height, it can simply walk +these pointers to the top of the tree, adding or subtracting the +difference in height from each node it encounters. The parent pointers +also make it cheaper (in complexity terms, the difference is probably +tiny in normal-sized documents) to find the current line number when +given a line object. In the old approach, the whole document array had +to be searched. Now, we can just walk up the tree and count the sizes +of the nodes coming before us at each level.
+ +I chose B-trees, not regular binary trees, mostly because they +allow for very fast bulk insertions and deletions. When there is a big +change to a document, it typically involves adding, deleting, or +replacing a chunk of subsequent lines. In a regular balanced tree, all +these inserts or deletes would have to be done separately, which could +be really expensive. In a B-tree, to insert a chunk, you just walk +down the tree once to find where it should go, insert them all in one +shot, and then break up the node if needed. This breaking up might +involve breaking up nodes further up, but only requires a single pass +back up the tree. For deletion, I'm somewhat lax in keeping things +balanced—I just collapse nodes into a leaf when their child count goes +below a given number. This means that there are some weird editing +patterns that may result in a seriously unbalanced tree, but even such +an unbalanced tree will perform well, unless you spend a day making +strangely repeating edits to a really big document.
+Above, I claimed that directly catching key +events for things like cursor movement is impractical because it +requires some browser-specific kludges. I then proceeded to explain +some awful hacks that were needed to make it +possible for the selection changes to be detected through the +textarea. In fact, the second hack is about as bad as the first.
+ +On top of that, in the presence of user-configurable tab sizes and +collapsed and wrapped lines, lining up cursor movement in the textarea +with what's visible on the screen becomes a nightmare. Thus, I've +decided to move to a model where the textarea's selection is no longer +depended on.
+ +So I moved to a model where all cursor movement is handled by my +own code. This adds support for a goal column, proper interaction of +cursor movement with collapsed lines, and makes it possible for +vertical movement to move through wrapped lines properly, instead of +just treating them like non-wrapped lines.
+ +The key event handlers now translate the key event into a string,
+something like Ctrl-Home or Shift-Cmd-R, and
+use that string to look up an action to perform. To make keybinding
+customizable, this lookup goes through
+a table, using a scheme that
+allows such tables to be chained together (for example, the default
+Mac bindings fall through to a table named 'emacsy', which defines
+basic Emacs-style bindings like Ctrl-F, and which is also
+used by the custom Emacs bindings).
A new
+option extraKeys
+allows ad-hoc keybindings to be defined in a much nicer way than what
+was possible with the
+old onKeyEvent
+callback. You simply provide an object mapping key identifiers to
+functions, instead of painstakingly looking at raw key events.
Built-in commands map to strings, rather than functions, for
+example "goLineUp" is the default action bound to the up
+arrow key. This allows new keymaps to refer to them without
+duplicating any code. New commands can be defined by assigning to
+the CodeMirror.commands object, which maps such commands
+to functions.
The hidden textarea now only holds the current selection, with no +extra characters around it. This has a nice advantage: polling for +input becomes much, much faster. If there's a big selection, this text +does not have to be read from the textarea every time—when we poll, +just noticing that something is still selected is enough to tell us +that no new text was typed.
+ +The reason that cheap polling is important is that many browsers do
+not fire useful events on IME (input method engine) input, which is
+the thing where people inputting a language like Japanese or Chinese
+use multiple keystrokes to create a character or sequence of
+characters. Most modern browsers fire input when the
+composing is finished, but many don't fire anything when the character
+is updated during composition. So we poll, whenever the
+editor is focused, to provide immediate updates of the display.
+
+ CodeMirror is a code-editor component that can be embedded in + Web pages. The core library provides only the editor + component, no accompanying buttons, auto-completion, or other IDE + functionality. It does provide a rich API on top of which such + functionality can be straightforwardly implemented. See + the addons included in the distribution, + and the list + of externally hosted addons, for reusable + implementations of extra features.
+ +CodeMirror works with language-specific modes. Modes are
+ JavaScript programs that help color (and optionally indent) text
+ written in a given language. The distribution comes with a number
+ of modes (see the mode/
+ directory), and it isn't hard to write new
+ ones for other languages.
The easiest way to use CodeMirror is to simply load the script
+ and style sheet found under lib/ in the distribution,
+ plus a mode script from one of the mode/ directories.
+ For example:
<script src="lib/codemirror.js"></script> +<link rel="stylesheet" href="lib/codemirror.css"> +<script src="mode/javascript/javascript.js"></script>+ +
(Alternatively, use a module loader. More + about that later.)
+ +Having done this, an editor instance can be created like + this:
+ +var myCodeMirror = CodeMirror(document.body);+ +
The editor will be appended to the document body, will start
+ empty, and will use the mode that we loaded. To have more control
+ over the new editor, a configuration object can be passed
+ to CodeMirror as a second
+ argument:
var myCodeMirror = CodeMirror(document.body, {
+ value: "function myScript(){return 100;}\n",
+ mode: "javascript"
+});
+
+ This will initialize the editor with a piece of code already in + it, and explicitly tell it to use the JavaScript mode (which is + useful when multiple modes are loaded). + See below for a full discussion of the + configuration options that CodeMirror accepts.
+ +In cases where you don't want to append the editor to an
+ element, and need more control over the way it is inserted, the
+ first argument to the CodeMirror function can also
+ be a function that, when given a DOM element, inserts it into the
+ document somewhere. This could be used to, for example, replace a
+ textarea with a real editor:
var myCodeMirror = CodeMirror(function(elt) {
+ myTextArea.parentNode.replaceChild(elt, myTextArea);
+}, {value: myTextArea.value});
+
+ However, for this use case, which is a common way to use + CodeMirror, the library provides a much more powerful + shortcut:
+ +var myCodeMirror = CodeMirror.fromTextArea(myTextArea);+ +
This will, among other things, ensure that the textarea's value + is updated with the editor's contents when the form (if it is part + of a form) is submitted. See the API + reference for a full description of this method.
+ +The files in the CodeMirror distribution contain shims for
+ loading them (and their dependencies) in AMD or CommonJS
+ environments. If the variables exports
+ and module exist and have type object, CommonJS-style
+ require will be used. If not, but there is a
+ function define with an amd property
+ present, AMD-style (RequireJS) will be used.
It is possible to + use Browserify or similar + tools to statically build modules using CodeMirror. Alternatively, + use RequireJS to dynamically + load dependencies at runtime. Both of these approaches have the + advantage that they don't use the global namespace and can, thus, + do things like load multiple versions of CodeMirror alongside each + other.
+ +Here's a simple example of using RequireJS to load CodeMirror:
+ +require([
+ "cm/lib/codemirror", "cm/mode/htmlmixed/htmlmixed"
+], function(CodeMirror) {
+ CodeMirror.fromTextArea(document.getElementById("code"), {
+ lineNumbers: true,
+ mode: "htmlmixed"
+ });
+});
+
+ It will automatically load the modes that the mixed HTML mode
+ depends on (XML, JavaScript, and CSS). Do not use
+ RequireJS' paths option to configure the path to
+ CodeMirror, since it will break loading submodules through
+ relative paths. Use
+ the packages
+ configuration option instead, as in:
require.config({
+ packages: [{
+ name: "codemirror",
+ location: "../path/to/codemirror",
+ main: "lib/codemirror"
+ }]
+});
+
+Both the CodeMirror
+ function and its fromTextArea method take as second
+ (optional) argument an object containing configuration options.
+ Any option not supplied like this will be taken
+ from CodeMirror.defaults, an
+ object containing the default options. You can update this object
+ to change the defaults on your page.
Options are not checked in any way, so setting bogus option + values is bound to lead to odd errors.
+ +These are the supported options:
+ +value: string|CodeMirror.Docmode: string|objectname property that names the mode (for
+ example {name: "javascript", json: true}). The demo
+ pages for each mode contain information about what configuration
+ parameters the mode supports. You can ask CodeMirror which modes
+ and MIME types have been defined by inspecting
+ the CodeMirror.modes
+ and CodeMirror.mimeModes objects. The first maps
+ mode names to their constructors, and the second maps MIME types
+ to mode specs.lineSeparator: string|nullnull), the document will be split on CRLFs
+ as well as lone CRs and LFs, and a single LF will be used as
+ line separator in all output (such
+ as getValue). When a
+ specific string is given, lines will only be split on that
+ string, and output will, by default, use that same
+ separator.theme: string.cm-s-[name]
+ styles is loaded (see
+ the theme directory in the
+ distribution). The default is "default", for which
+ colors are included in codemirror.css. It is
+ possible to use multiple theming classes at once—for
+ example "foo bar" will assign both
+ the cm-s-foo and the cm-s-bar classes
+ to the editor.indentUnit: integersmartIndent: booleantabSize: integerindentWithTabs: booleantabSize
+ spaces should be replaced by N tabs. Default is false.electricChars: booleanspecialChars: RegExp/[\u0000-\u001f\u007f-\u009f\u00ad\u061c\u200b-\u200f\u2028\u2029\ufeff]/.specialCharPlaceholder: function(char) → ElementspecialChars
+ option, produces a DOM node that is used to represent the
+ character. By default, a red dot (•)
+ is shown, with a title tooltip to indicate the character code.rtlMoveVisually: booleanfalse
+ on Windows, and true on other platforms.keyMap: string"default", which is the only key map defined
+ in codemirror.js itself. Extra key maps are found in
+ the key map directory. See
+ the section on key maps for more
+ information.extraKeys: objectkeyMap. Should be
+ either null, or a valid key map value.lineWrapping: booleanfalse (scroll).lineNumbers: booleanfirstLineNumber: integerlineNumberFormatter: function(line: integer) → stringgutters: array<string>width (and optionally a
+ background), and which will be used to draw the background of
+ the gutters. May include
+ the CodeMirror-linenumbers class, in order to
+ explicitly set the position of the line number gutter (it will
+ default to be to the right of all other gutters). These class
+ names are the keys passed
+ to setGutterMarker.fixedGutter: booleanscrollbarStyle: string"native", showing native scrollbars. The core
+ library also provides the "null" style, which
+ completely hides the
+ scrollbars. Addons can
+ implement additional scrollbar models.coverGutterNextToScrollbar: booleanfixedGutter
+ is on, and there is a horizontal scrollbar, by default the
+ gutter will be visible to the left of this scrollbar. If this
+ option is set to true, it will be covered by an element with
+ class CodeMirror-gutter-filler.inputStyle: string"textarea"
+ and "contenteditable" input models. On mobile
+ browsers, the default is "contenteditable". On
+ desktop browsers, the default is "textarea".
+ Support for IME and screen readers is better in
+ the "contenteditable" model. The intention is to
+ make it the default on modern desktop browsers in the
+ future.readOnly: boolean|string"nocursor" is given (instead of
+ simply true), focusing of the editor is also
+ disallowed.showCursorWhenSelecting: booleanlineWiseCopyCut: booleanundoDepth: integerhistoryEventDelay: integertabindex: integerautofocus: booleanfromTextArea is
+ used, and no explicit value is given for this option, it will be
+ set to true when either the source textarea is focused, or it
+ has an autofocus attribute and no other element is
+ focused.Below this a few more specialized, low-level options are + listed. These are only useful in very specific situations, you + might want to skip them the first time you read this manual.
+ +dragDrop: booleanallowDropFileTypes: array<string>null) only files whose
+ type is in the array can be dropped into the editor. The strings
+ should be MIME types, and will be checked against
+ the type
+ of the File object as reported by the browser.cursorBlinkRate: numbercursorScrollMargin: numbercursorHeight: number0.85),
+ which causes the cursor to not reach all the way to the bottom
+ of the line, looks betterresetSelectionOnContextMenu: booleantrue.workTime, workDelay: numberworkTime milliseconds, and then use
+ timeout to sleep for workDelay milliseconds. The
+ defaults are 200 and 300, you can change these options to make
+ the highlighting more or less aggressive.pollInterval: numberflattenSpans: booleanaddModeClass: boolean"cm-m-". For example, tokens from the XML mode
+ will get the cm-m-xml class.maxHighlightLength: numberInfinity to turn off
+ this behavior.viewportMargin: integerInfinity to make sure the whole document is
+ always rendered, and thus the browser's text search works on it.
+ This will have bad effects on performance of big
+ documents.Various CodeMirror-related objects emit events, which allow
+ client code to react to various situations. Handlers for such
+ events can be registered with the on
+ and off methods on the objects
+ that the event fires on. To fire your own events,
+ use CodeMirror.signal(target, name, args...),
+ where target is a non-DOM-node object.
An editor instance fires the following events.
+ The instance argument always refers to the editor
+ itself.
"change" (instance: CodeMirror, changeObj: object)changeObj is a {from, to, text, removed,
+ origin} object containing information about the changes
+ that occurred as second argument. from
+ and to are the positions (in the pre-change
+ coordinate system) where the change started and ended (for
+ example, it might be {ch:0, line:18} if the
+ position is at the beginning of line #19). text is
+ an array of strings representing the text that replaced the
+ changed range (split by line). removed is the text
+ that used to be between from and to,
+ which is overwritten by this change. This event is
+ fired before the end of
+ an operation, before the DOM updates
+ happen."changes" (instance: CodeMirror, changes: array<object>)"change"
+ event, but batched per operation,
+ passing an array containing all the changes that happened in the
+ operation. This event is fired after the operation finished, and
+ display changes it makes will trigger a new operation."beforeChange" (instance: CodeMirror, changeObj: object)changeObj object
+ has from, to, and text
+ properties, as with
+ the "change" event. It
+ also has a cancel() method, which can be called to
+ cancel the change, and, if the change isn't
+ coming from an undo or redo event, an update(from, to,
+ text) method, which may be used to modify the change.
+ Undo or redo changes can't be modified, because they hold some
+ metainformation for restoring old marked ranges that is only
+ valid for that specific change. All three arguments
+ to update are optional, and can be left off to
+ leave the existing value for that field
+ intact. Note: you may not do anything from
+ a "beforeChange" handler that would cause changes
+ to the document or its visualization. Doing so will, since this
+ handler is called directly from the bowels of the CodeMirror
+ implementation, probably cause the editor to become
+ corrupted."cursorActivity" (instance: CodeMirror)"keyHandled" (instance: CodeMirror, name: string, event: Event)name is the name of the handled key (for
+ example "Ctrl-X" or "'q'"),
+ and event is the DOM keydown
+ or keypress event."inputRead" (instance: CodeMirror, changeObj: object)"electricInput" (instance: CodeMirror, line: integer)"beforeSelectionChange" (instance: CodeMirror, obj: {ranges, origin, update}){anchor, head} objects in
+ the ranges property of the obj
+ argument, and optionally change them by calling
+ the update method on this object, passing an array
+ of ranges in the same format. The object also contains
+ an origin property holding the origin string passed
+ to the selection-changing method, if any. Handlers for this
+ event have the same restriction
+ as "beforeChange"
+ handlers — they should not do anything to directly update the
+ state of the editor."viewportChange" (instance: CodeMirror, from: number, to: number)from and to arguments
+ give the new start and end of the viewport."swapDoc" (instance: CodeMirror, oldDoc: Doc)swapDoc
+ method."gutterClick" (instance: CodeMirror, line: integer, gutter: string, clickEvent: Event)mousedown event object as
+ fourth argument."gutterContextMenu" (instance: CodeMirror, line: integer, gutter: string, contextMenu: Event: Event)contextmenu event. Will pass the editor
+ instance as first argument, the (zero-based) number of the line
+ that was clicked as second argument, the CSS class of the
+ gutter that was clicked as third argument, and the raw
+ contextmenu mouse event object as fourth argument.
+ You can preventDefault the event, to signal that
+ CodeMirror should do no further handling."focus" (instance: CodeMirror, event: Event)"blur" (instance: CodeMirror, event: Event)"scroll" (instance: CodeMirror)"refresh" (instance: CodeMirror)"optionChange" (instance: CodeMirror, option: string)setOption."scrollCursorIntoView" (instance: CodeMirror, event: Event)preventDefault method called, CodeMirror will
+ not itself try to scroll the window."update" (instance: CodeMirror)"renderLine" (instance: CodeMirror, line: LineHandle, element: Element)"mousedown",
+ "dblclick", "touchstart", "contextmenu",
+ "keydown", "keypress",
+ "keyup", "cut", "copy", "paste",
+ "dragstart", "dragenter",
+ "dragover", "dragleave",
+ "drop"
+ (instance: CodeMirror, event: Event)preventDefault the event, or give it a
+ truthy codemirrorIgnore property, to signal that
+ CodeMirror should do no further handling.Document objects (instances
+ of CodeMirror.Doc) emit the
+ following events:
"change" (doc: CodeMirror.Doc, changeObj: object)changeObj has a similar type as the
+ object passed to the
+ editor's "change"
+ event."beforeChange" (doc: CodeMirror.Doc, change: object)"cursorActivity" (doc: CodeMirror.Doc)"beforeSelectionChange" (doc: CodeMirror.Doc, selection: {head, anchor})Line handles (as returned by, for
+ example, getLineHandle)
+ support these events:
"delete" ()"change" (line: LineHandle, changeObj: object)change
+ object is similar to the one passed
+ to change event on the editor
+ object.Marked range handles (CodeMirror.TextMarker), as returned
+ by markText
+ and setBookmark, emit the
+ following events:
"beforeCursorEnter" ()"clear" (from: {line, ch}, to: {line, ch})clearOnEnter
+ or through a call to its clear() method. Will only
+ be fired once per handle. Note that deleting the range through
+ text editing does not fire this event, because an undo action
+ might bring the range back into existence. from
+ and to give the part of the document that the range
+ spanned when it was cleared."hide" ()"unhide" ()Line widgets (CodeMirror.LineWidget), returned
+ by addLineWidget, fire
+ these events:
"redraw" ()Key maps are ways to associate keys with functionality. A key map + is an object mapping strings that identify the keys to functions + that implement their functionality.
+ +The CodeMirror distributions comes + with Emacs, Vim, + and Sublime Text-style keymaps.
+ +Keys are identified either by name or by character.
+ The CodeMirror.keyNames object defines names for
+ common keys and associates them with their key codes. Examples of
+ names defined here are Enter, F5,
+ and Q. These can be prefixed
+ with Shift-, Cmd-, Ctrl-,
+ and Alt- to specify a modifier. So for
+ example, Shift-Ctrl-Space would be a valid key
+ identifier.
Common example: map the Tab key to insert spaces instead of a tab + character.
+ +
+editor.setOption("extraKeys", {
+ Tab: function(cm) {
+ var spaces = Array(cm.getOption("indentUnit") + 1).join(" ");
+ cm.replaceSelection(spaces);
+ }
+});
+
+ Alternatively, a character can be specified directly by
+ surrounding it in single quotes, for example '$'
+ or 'q'. Due to limitations in the way browsers fire
+ key events, these may not be prefixed with modifiers.
Multi-stroke key bindings can be specified
+ by separating the key names by spaces in the property name, for
+ example Ctrl-X Ctrl-V. When a map contains
+ multi-stoke bindings or keys with modifiers that are not specified
+ in the default order (Shift-Cmd-Ctrl-Alt), you must
+ call CodeMirror.normalizeKeyMap on it before it can
+ be used. This function takes a keymap and modifies it to normalize
+ modifier order and properly recognize multi-stroke bindings. It
+ will return the keymap itself.
The CodeMirror.keyMap object associates key maps
+ with names. User code and key map definitions can assign extra
+ properties to this object. Anywhere where a key map is expected, a
+ string can be given, which will be looked up in this object. It
+ also contains the "default" key map holding the
+ default bindings.
The values of properties in key maps can be either functions of
+ a single argument (the CodeMirror instance), strings, or
+ false. Strings refer
+ to commands, which are described below. If
+ the property is set to false, CodeMirror leaves
+ handling of the key up to the browser. A key handler function may
+ return CodeMirror.Pass to indicate that it has
+ decided not to handle the key, and other handlers (or the default
+ behavior) should be given a turn.
Keys mapped to command names that start with the
+ characters "go" or to functions that have a
+ truthy motion property (which should be used for
+ cursor-movement actions) will be fired even when an
+ extra Shift modifier is present (i.e. "Up":
+ "goLineUp" matches both up and shift-up). This is used to
+ easily implement shift-selection.
Key maps can defer to each other by defining
+ a fallthrough property. This indicates that when a
+ key is not found in the map itself, one or more other maps should
+ be searched. It can hold either a single key map or an array of
+ key maps.
When a key map needs to set something up when it becomes
+ active, or tear something down when deactivated, it can
+ contain attach and/or detach properties,
+ which should hold functions that take the editor instance and the
+ next or previous keymap. Note that this only works for the
+ top-level keymap, not for fallthrough
+ maps or maps added
+ with extraKeys
+ or addKeyMap.
Commands are parameter-less actions that can be performed on an
+ editor. Their main use is for key bindings. Commands are defined by
+ adding properties to the CodeMirror.commands object.
+ A number of common commands are defined by the library itself,
+ most of them used by the default key bindings. The value of a
+ command property must be a function of one argument (an editor
+ instance).
Some of the commands below are referenced in the default + key map, but not defined by the core library. These are intended to + be defined by user code or addons.
+ +Commands can also be run with
+ the execCommand
+ method.
selectAllCtrl-A (PC), Cmd-A (Mac)singleSelectionEsckillLineCtrl-K (Mac)deleteLineCtrl-D (PC), Cmd-D (Mac)delLineLeftdelWrappedLineLeftCmd-Backspace (Mac)delWrappedLineRightCmd-Delete (Mac)undoCtrl-Z (PC), Cmd-Z (Mac)redoCtrl-Y (PC), Shift-Cmd-Z (Mac), Cmd-Y (Mac)undoSelectionCtrl-U (PC), Cmd-U (Mac)redoSelectionAlt-U (PC), Shift-Cmd-U (Mac)goDocStartCtrl-Home (PC), Cmd-Up (Mac), Cmd-Home (Mac)goDocEndCtrl-End (PC), Cmd-End (Mac), Cmd-Down (Mac)goLineStartAlt-Left (PC), Ctrl-A (Mac)goLineStartSmartHomegoLineEndAlt-Right (PC), Ctrl-E (Mac)goLineRightCmd-Right (Mac)goLineLeftCmd-Left (Mac)goLineLeftSmartgoLineStartSmart.goLineUpUp, Ctrl-P (Mac)goLineDownDown, Ctrl-N (Mac)goPageUpPageUp, Shift-Ctrl-V (Mac)goPageDownPageDown, Ctrl-V (Mac)goCharLeftLeft, Ctrl-B (Mac)goCharRightRight, Ctrl-F (Mac)goColumnLeftgoColumnRightgoWordLeftAlt-B (Mac)goWordRightAlt-F (Mac)goGroupLeftCtrl-Left (PC), Alt-Left (Mac)goGroupRightCtrl-Right (PC), Alt-Right (Mac)delCharBeforeShift-Backspace, Ctrl-H (Mac)delCharAfterDelete, Ctrl-D (Mac)delWordBeforeAlt-Backspace (Mac)delWordAfterAlt-D (Mac)delGroupBeforeCtrl-Backspace (PC), Alt-Backspace (Mac)delGroupAfterCtrl-Delete (PC), Ctrl-Alt-Backspace (Mac), Alt-Delete (Mac)indentAutoShift-TabindentMoreCtrl-] (PC), Cmd-] (Mac)indentLessCtrl-[ (PC), Cmd-[ (Mac)insertTabinsertSoftTabdefaultTabTabtransposeCharsCtrl-T (Mac)newlineAndIndentEntertoggleOverwriteInsertsaveCtrl-S (PC), Cmd-S (Mac)findCtrl-F (PC), Cmd-F (Mac)findNextCtrl-G (PC), Cmd-G (Mac)findPrevShift-Ctrl-G (PC), Shift-Cmd-G (Mac)replaceShift-Ctrl-F (PC), Cmd-Alt-F (Mac)replaceAllShift-Ctrl-R (PC), Shift-Cmd-Alt-F (Mac)Up to a certain extent, CodeMirror's look can be changed by
+ modifying style sheet files. The style sheets supplied by modes
+ simply provide the colors for that mode, and can be adapted in a
+ very straightforward way. To style the editor itself, it is
+ possible to alter or override the styles defined
+ in codemirror.css.
Some care must be taken there, since a lot of the rules in this + file are necessary to have CodeMirror function properly. Adjusting + colors should be safe, of course, and with some care a lot of + other things can be changed as well. The CSS classes defined in + this file serve the following roles:
+ +CodeMirrorheight style to auto will
+ make the editor resize to fit its
+ content (it is recommended to also set
+ the viewportMargin
+ option to Infinity when doing this.CodeMirror-focusedCodeMirror-guttersCodeMirror-linenumbersCodeMirror-linenumberCodeMirror-linenumbers
+ (plural) element, but rather will be absolutely positioned to
+ overlay it. Use this to set alignment and text properties for
+ the line numbers.CodeMirror-linesCodeMirror-cursorCodeMirror-selectedspan elements
+ with this class.CodeMirror-matchingbracket,
+ CodeMirror-nonmatchingbracketIf your page's style sheets do funky things to
+ all div or pre elements (you probably
+ shouldn't do that), you'll have to define rules to cancel these
+ effects out again for elements under the CodeMirror
+ class.
Themes are also simply CSS files, which define colors for
+ various syntactic elements. See the files in
+ the theme directory.
A lot of CodeMirror features are only available through its + API. Thus, you need to write code (or + use addons) if you want to expose them to + your users.
+ +Whenever points in the document are represented, the API uses
+ objects with line and ch properties.
+ Both are zero-based. CodeMirror makes sure to 'clip' any positions
+ passed by client code so that they fit inside the document, so you
+ shouldn't worry too much about sanitizing your coordinates. If you
+ give ch a value of null, or don't
+ specify it, it will be replaced with the length of the specified
+ line. Such positions may also have a sticky property
+ holding "before" or "after", whether the
+ position is associated with the character before or after it. This
+ influences, for example, where the cursor is drawn on a
+ line-break or bidi-direction boundary.
Methods prefixed with doc. can, unless otherwise
+ specified, be called both on CodeMirror (editor)
+ instances and CodeMirror.Doc instances. Methods
+ prefixed with cm. are only available
+ on CodeMirror instances.
Constructing an editor instance is done with
+ the CodeMirror(place: Element|fn(Element),
+ ?option: object) constructor. If the place
+ argument is a DOM element, the editor will be appended to it. If
+ it is a function, it will be called, and is expected to place the
+ editor into the document. options may be an element
+ mapping option names to values. The options
+ that it doesn't explicitly specify (or all options, if it is not
+ passed) will be taken
+ from CodeMirror.defaults.
Note that the options object passed to the constructor will be + mutated when the instance's options + are changed, so you shouldn't share such + objects between instances.
+ +See CodeMirror.fromTextArea
+ for another way to construct an editor instance.
doc.getValue(?separator: string) → string"\n").doc.setValue(content: string)doc.getRange(from: {line, ch}, to: {line, ch}, ?separator: string) → string{line, ch} objects. An optional third
+ argument can be given to indicate the line separator string to
+ use (defaults to "\n").doc.replaceRange(replacement: string, from: {line, ch}, to: {line, ch}, ?origin: string)from
+ and to with the given string. from
+ and to must be {line, ch}
+ objects. to can be left off to simply insert the
+ string at position from. When origin
+ is given, it will be passed on
+ to "change" events, and
+ its first letter will be used to determine whether this change
+ can be merged with previous history events, in the way described
+ for selection origins.doc.getLine(n: integer) → stringn.doc.lineCount() → integerdoc.firstLine() → integerdoc.lastLine() → integerdoc.lineCount() - 1,
+ but for linked sub-views,
+ it might return other values.doc.getLineHandle(num: integer) → LineHandledoc.getLineNumber(handle: LineHandle) → integernull when it is no longer in the
+ document).doc.eachLine(f: (line: LineHandle))doc.eachLine(start: integer, end: integer, f: (line: LineHandle))start
+ and end line numbers are given, the range
+ from start up to (not including) end,
+ and call f for each line, passing the line handle.
+ This is a faster way to visit a range of line handlers than
+ calling getLineHandle
+ for each of them. Note that line handles have
+ a text property containing the line's content (as a
+ string).doc.markClean()changeGeneration,
+ which allows multiple subsystems to track different notions of
+ cleanness without interfering.doc.changeGeneration(?closeEvent: boolean) → integerisClean to test whether
+ any edits were made (and not undone) in the meantime.
+ If closeEvent is true, the current history event
+ will be ‘closed’, meaning it can't be combined with further
+ changes (rapid typing or deleting events are typically
+ combined).doc.isClean(?generation: integer) → booleanmarkClean if no
+ argument is passed, or since the matching call
+ to changeGeneration
+ if a generation value is given.doc.getSelection(?lineSep: string) → stringlineSep in between.doc.getSelections(?lineSep: string) → array<string>doc.replaceSelection(replacement: string, ?select: string)select argument can be used to change
+ this—passing "around" will cause the new text to be
+ selected, passing "start" will collapse the
+ selection to the start of the inserted text.doc.replaceSelections(replacements: array<string>, ?select: string)select argument works the same as
+ in replaceSelection.doc.getCursor(?start: string) → {line, ch}start is an optional string indicating
+ which end of the selection to return. It may
+ be "from", "to", "head"
+ (the side of the selection that moves when you press
+ shift+arrow), or "anchor" (the fixed side of the
+ selection). Omitting the argument is the same as
+ passing "head". A {line, ch} object
+ will be returned.doc.listSelections() → array<{anchor, head}>anchor
+ and head properties referring to {line,
+ ch} objects.doc.somethingSelected() → booleandoc.setCursor(pos: {line, ch}|number, ?ch: number, ?options: object){line, ch} object, or the line and the
+ character as two separate parameters. Will replace all
+ selections with a single, empty selection at the given position.
+ The supported options are the same as for setSelection.doc.setSelection(anchor: {line, ch}, ?head: {line, ch}, ?options: object)anchor
+ and head should be {line, ch}
+ objects. head defaults to anchor when
+ not given. These options are supported:
+ scroll: booleanorigin: string+, and the last recorded selection had
+ the same origin and was similar (close
+ in time, both
+ collapsed or both non-collapsed), the new one will replace the
+ old one. When it starts with *, it will always
+ replace the previous event (if that had the same origin).
+ Built-in motion uses the "+move" origin. User input uses the "+input" origin.bias: numberdoc.setSelections(ranges: array<{anchor, head}>, ?primary: integer, ?options: object)primary is a
+ number, it determines which selection is the primary one. When
+ it is not given, the primary index is taken from the previous
+ selection, or set to the last range if the previous selection
+ had less ranges than the new one. Supports the same options
+ as setSelection.doc.addSelection(anchor: {line, ch}, ?head: {line, ch})doc.extendSelection(from: {line, ch}, ?to: {line, ch}, ?options: object)setSelection, but
+ will, if shift is held or
+ the extending flag is set, move the
+ head of the selection while leaving the anchor at its current
+ place. to is optional, and can be passed to ensure
+ a region (for example a word or paragraph) will end up selected
+ (in addition to whatever lies between that region and the
+ current anchor). When multiple selections are present, all but
+ the primary selection will be dropped by this method.
+ Supports the same options as setSelection.doc.extendSelections(heads: array<{line, ch}>, ?options: object)extendSelection
+ that acts on all selections at once.doc.extendSelectionsBy(f: function(range: {anchor, head}) → {line, ch}), ?options: object)extendSelections
+ on the result.doc.setExtending(value: boolean)extendSelection
+ to leave the selection anchor in place.doc.getExtending() → booleancm.hasFocus() → booleancm.findPosH(start: {line, ch}, amount: integer, unit: string, visually: boolean) → {line, ch, ?hitSide: boolean}start is a {line, ch}
+ object, amount an integer (may be negative),
+ and unit one of the
+ string "char", "column",
+ or "word". Will return a position that is produced
+ by moving amount times the distance specified
+ by unit. When visually is true, motion
+ in right-to-left text will be visual rather than logical. When
+ the motion was clipped by hitting the end or start of the
+ document, the returned value will have a hitSide
+ property set to true.cm.findPosV(start: {line, ch}, amount: integer, unit: string) → {line, ch, ?hitSide: boolean}findPosH,
+ but used for vertical motion. unit may
+ be "line" or "page". The other
+ arguments and the returned value have the same interpretation as
+ they have in findPosH.cm.findWordAt(pos: {line, ch}) → {anchor: {line, ch}, head: {line, ch}}cm.setOption(option: string, value: any)option
+ should the name of an option,
+ and value should be a valid value for that
+ option.cm.getOption(option: string) → anycm.addKeyMap(map: object, bottom: boolean)extraKeys
+ option. Maps added in this way have a higher precedence than
+ the extraKeys
+ and keyMap options,
+ and between them, the maps added earlier have a lower precedence
+ than those added later, unless the bottom argument
+ was passed, in which case they end up below other key maps added
+ with this method.cm.removeKeyMap(map: object)addKeyMap. Either
+ pass in the key map object itself, or a string, which will be
+ compared against the name property of the active
+ key maps.cm.addOverlay(mode: string|object, ?options: object)mode can be a mode
+ spec or a mode object (an object with
+ a token method).
+ The options parameter is optional. If given, it
+ should be an object, optionally containing the following options:
+ opaque: boolnull, to override the styling of
+ the base mode entirely, instead of the two being applied
+ together.priority: numbercm.removeOverlay(mode: string|object)mode
+ parameter to addOverlay,
+ or a string that corresponds to the name property of
+ that value, to remove an overlay again.cm.on(type: string, func: (...args))CodeMirror.on(object, type, func) version
+ that allows registering of events on any object.cm.off(type: string, func: (...args))CodeMirror.off(object, type,
+ func) also exists.Each editor is associated with an instance
+ of CodeMirror.Doc, its document. A document
+ represents the editor content, plus a selection, an undo history,
+ and a mode. A document can only be
+ associated with a single editor at a time. You can create new
+ documents by calling the CodeMirror.Doc(text, mode,
+ firstLineNumber) constructor. The last two arguments are
+ optional and can be used to set a mode for the document and make
+ it start at a line number other than 0, respectively.
cm.getDoc() → Docdoc.getEditor() → CodeMirrornull.cm.swapDoc(doc: CodeMirror.Doc) → Docdoc.copy(copyHistory: boolean) → DoccopyHistory is true, the history will also be
+ copied. Can not be called directly on an editor.doc.linkedDoc(options: object) → DocsharedHist: booleanfrom: integerto: integermode: string|objectdoc.unlinkDoc(doc: CodeMirror.Doc)doc.iterLinkedDocs(function: (doc: CodeMirror.Doc, sharedHist: boolean))doc.undo()doc.redo()doc.undoSelection()doc.redoSelection()doc.historySize() → {undo: integer, redo: integer}{undo, redo} properties,
+ both of which hold integers, indicating the amount of stored
+ undo and redo operations.doc.clearHistory()doc.getHistory() → objectdoc.setHistory(history: object)getHistory. Note that
+ this will have entirely undefined results if the editor content
+ isn't also the same as it was when getHistory was
+ called.doc.markText(from: {line, ch}, to: {line, ch}, ?options: object) → TextMarkerfrom and to should
+ be {line, ch} objects. The options
+ parameter is optional. When given, it should be an object that
+ may contain the following configuration options:
+ className: stringinclusiveLeft: booleaninclusiveRight: booleaninclusiveLeft,
+ but for the right side.atomic: booleaninclusiveLeft
+ and inclusiveRight have a different meaning—they
+ will prevent the cursor from being placed respectively
+ directly before and directly after the range.collapsed: booleanclearOnEnter: boolean"clear" event
+ fired on the range handle can be used to be notified when this
+ happens.clearWhenEmpty: booleanreplacedWith: ElementhandleMouseEvents: booleanreplacedWith is given, this determines
+ whether the editor will capture mouse and drag events
+ occurring in this widget. Default is false—the events will be
+ left alone for the default browser handler, or specific
+ handlers on the widget, to capture.readOnly: booleansetValue to reset
+ the whole document. Note: adding a read-only span
+ currently clears the undo history of the editor, because
+ existing undo events being partially nullified by read-only
+ spans would corrupt the history (in the current
+ implementation).addToHistory: booleanstartStyle: stringendStyle: stringstartStyle, but for the rightmost span.css: string"color: #fe3".title:
+ stringtitle attribute with the
+ given value.shared: booleanshared to true to make the
+ marker appear in all documents. By default, a marker appears
+ only in its target document.CodeMirror.TextMarker), which
+ exposes three methods:
+ clear(), to remove the mark,
+ find(), which returns
+ a {from, to} object (both holding document
+ positions), indicating the current position of the marked range,
+ or undefined if the marker is no longer in the
+ document, and finally changed(),
+ which you can call if you've done something that might change
+ the size of the marker (for example changing the content of
+ a replacedWith
+ node), and want to cheaply update the display.doc.setBookmark(pos: {line, ch}, ?options: object) → TextMarkerfind() and clear(). The first
+ returns the current position of the bookmark, if it is still in
+ the document, and the second explicitly removes the bookmark.
+ The options argument is optional. If given, the following
+ properties are recognized:
+ widget: ElementreplacedWith
+ option to markText).insertLeft: booleanshared: booleanmarkText.handleMouseEvents: booleanmarkText,
+ this determines whether mouse events on the widget inserted
+ for this bookmark are handled by CodeMirror. The default is
+ false.doc.findMarks(from: {line, ch}, to: {line, ch}) → array<TextMarker>doc.findMarksAt(pos: {line, ch}) → array<TextMarker>doc.getAllMarks() → array<TextMarker>doc.setGutterMarker(line: integer|LineHandle, gutterID: string, value: Element) → LineHandlegutters option)
+ to the given value. Value can be either null, to
+ clear the marker, or a DOM element, to set it. The DOM element
+ will be shown in the specified gutter next to the specified
+ line.doc.clearGutter(gutterID: string)doc.addLineClass(line: integer|LineHandle, where: string, class: string) → LineHandleline
+ can be a number or a line handle. where determines
+ to which element this class should be applied, can can be one
+ of "text" (the text element, which lies in front of
+ the selection), "background" (a background element
+ that will be behind the selection), "gutter" (the
+ line's gutter space), or "wrap" (the wrapper node
+ that wraps all of the line's elements, including gutter
+ elements). class should be the name of the class to
+ apply.doc.removeLineClass(line: integer|LineHandle, where: string, class: string) → LineHandleline can be a
+ line handle or number. where should be one
+ of "text", "background",
+ or "wrap"
+ (see addLineClass). class
+ can be left off to remove all classes for the specified node, or
+ be a string to remove only a specific class.doc.lineInfo(line: integer|LineHandle) → object{line, handle, text,
+ gutterMarkers, textClass, bgClass, wrapClass, widgets},
+ where gutterMarkers is an object mapping gutter IDs
+ to marker elements, and widgets is an array
+ of line widgets attached to this
+ line, and the various class properties refer to classes added
+ with addLineClass.cm.addWidget(pos: {line, ch}, node: Element, scrollIntoView: boolean)node, which should be an absolutely
+ positioned DOM node, into the editor, positioned right below the
+ given {line, ch} position.
+ When scrollIntoView is true, the editor will ensure
+ that the entire node is visible (if possible). To remove the
+ widget again, simply use DOM methods (move it somewhere else, or
+ call removeChild on its parent).doc.addLineWidget(line: integer|LineHandle, node: Element, ?options: object) → LineWidgetline should be either an integer or a
+ line handle, and node should be a DOM node, which
+ will be displayed below the given line. options,
+ when given, should be an object that configures the behavior of
+ the widget. The following options are supported (all default to
+ false):
+ coverGutter: booleannoHScroll: booleanabove: booleanhandleMouseEvents: booleaninsertAt: integerline property
+ pointing at the line handle that it is associated with, and the following methods:
+ clear()changed()cm.setSize(width: number|string, height: number|string)width and height
+ can be either numbers (interpreted as pixels) or CSS units
+ ("100%", for example). You can
+ pass null for either of them to indicate that that
+ dimension should not be changed.cm.scrollTo(x: number, y: number)null
+ or undefined to have no effect.cm.getScrollInfo() → {left, top, width, height, clientWidth, clientHeight}{left, top, width, height, clientWidth,
+ clientHeight} object that represents the current scroll
+ position, the size of the scrollable area, and the size of the
+ visible area (minus scrollbars).cm.scrollIntoView(what: {line, ch}|{left, top, right, bottom}|{from, to}|null, ?margin: number)what may
+ be null to scroll the cursor into view,
+ a {line, ch} position to scroll a character into
+ view, a {left, top, right, bottom} pixel range (in
+ editor-local coordinates), or a range {from, to}
+ containing either two character positions or two pixel squares.
+ The margin parameter is optional. When given, it
+ indicates the amount of vertical pixels around the given area
+ that should be made visible as well.cm.cursorCoords(where: boolean|{line, ch}, mode: string) → {left, top, bottom}{left, top, bottom} object
+ containing the coordinates of the cursor position.
+ If mode is "local", they will be
+ relative to the top-left corner of the editable document. If it
+ is "page" or not given, they are relative to the
+ top-left corner of the page. If mode
+ is "window", the coordinates are relative to the
+ top-left corner of the currently visible (scrolled)
+ window. where can be a boolean indicating whether
+ you want the start (true) or the end
+ (false) of the selection, or, if a {line,
+ ch} object is given, it specifies the precise position at
+ which you want to measure.cm.charCoords(pos: {line, ch}, ?mode: string) → {left, right, top, bottom}pos should be a {line, ch}
+ object. This differs from cursorCoords in that
+ it'll give the size of the whole character, rather than just the
+ position that the cursor would have when it would sit at that
+ position.cm.coordsChar(object: {left, top}, ?mode: string) → {line, ch}{left, top} object, returns
+ the {line, ch} position that corresponds to it. The
+ optional mode parameter determines relative to what
+ the coordinates are interpreted. It may
+ be "window", "page" (the default),
+ or "local".cm.lineAtHeight(height: number, ?mode: string) → numbermode can be one of the same strings
+ that coordsChar
+ accepts.cm.heightAtLine(line: integer|LineHandle, ?mode: string, ?includeWidgets: bool) → numbermode
+ (see coordsChar), which
+ defaults to "page". When a line below the bottom of
+ the document is specified, the returned value is the bottom of
+ the last line in the document. By default, the position of the
+ actual text is returned. If `includeWidgets` is true and the
+ line has line widgets, the position above the first line widget
+ is returned.cm.defaultTextHeight() → numbercm.defaultCharWidth() → numbercm.getViewport() → {from: number, to: number}{from, to} object indicating the
+ start (inclusive) and end (exclusive) of the currently rendered
+ part of the document. In big documents, when most content is
+ scrolled out of view, CodeMirror will only render the visible
+ part, and a margin around it. See also
+ the viewportChange
+ event.cm.refresh()When writing language-aware functionality, it can often be + useful to hook into the knowledge that the CodeMirror language + mode has. See the section on modes for a + more detailed description of how these work.
+ +doc.getMode() → objectgetOption("mode"), which gives you
+ the mode specification, rather than the resolved, instantiated
+ mode object.cm.getModeAt(pos: {line, ch}) → objectgetMode for
+ simple modes, but will return an inner mode for nesting modes
+ (such as htmlmixed).cm.getTokenAt(pos: {line, ch}, ?precise: boolean) → object{line, ch} object). The
+ returned object has the following properties:
+ startendstringtype"keyword"
+ or "comment" (may also be null).stateprecise is true, the token will be guaranteed to be accurate based on recent edits. If false or
+ not specified, the token will use cached state information, which will be faster but might not be accurate if
+ edits were recently made and highlighting has not yet completed.
+ cm.getLineTokens(line: integer, ?precise: boolean) → array<{start, end, string, type, state}>getTokenAt, but
+ collects all tokens for a given line into an array. It is much
+ cheaper than repeatedly calling getTokenAt, which
+ re-parses the part of the line before the token for every call.cm.getTokenTypeAt(pos: {line, ch}) → stringgetTokenAt useful for
+ when you just need the type of the token at a given position,
+ and no other information. Will return null for
+ unstyled tokens, and a string, potentially containing multiple
+ space-separated style names, otherwise.cm.getHelpers(pos: {line, ch}, type: string) → array<helper>type argument provides
+ the helper namespace (see
+ registerHelper), in
+ which the values will be looked up. When the mode itself has a
+ property that corresponds to the type, that
+ directly determines the keys that are used to look up the helper
+ values (it may be either a single string, or an array of
+ strings). Failing that, the mode's helperType
+ property and finally the mode's name are used.fold containing "brace". When
+ the brace-fold addon is loaded, that defines a
+ helper named brace in the fold
+ namespace. This is then used by
+ the foldcode addon to
+ figure out that it can use that folding function to fold
+ JavaScript code.cm.getHelper(pos: {line, ch}, type: string) → helpergetHelpers.cm.getStateAfter(?line: integer, ?precise: boolean) → objectprecise is defined
+ as in getTokenAt().cm.operation(func: () → any) → anycm.indentLine(line: integer, ?dir: string|integer)"smart") may be one of:
+ "prev""smart""prev" otherwise."add""subtract"<integer>cm.toggleOverwrite(?value: boolean)cm.isReadOnly() → booleandoc.lineSeparator()null, the
+ string "\n" is returned.cm.execCommand(name: string)doc.posFromIndex(index: integer) → {line, ch}{line, ch} object for a
+ zero-based index who's value is relative to the start of the
+ editor's text. If the index is out of range of the text then
+ the returned object is clipped to start or end of the text
+ respectively.doc.indexFromPos(object: {line, ch}) → integerposFromIndex.cm.focus()cm.getInputField() → ElementinputStyle
+ option.cm.getWrapperElement() → Elementcm.getScrollerElement() → Elementcm.getGutterElement() → ElementThe CodeMirror object itself provides
+ several useful properties.
CodeMirror.version: string"major.minor.patch",
+ where patch is zero for releases, and something
+ else (usually one) for dev snapshots.CodeMirror.fromTextArea(textArea: TextAreaElement, ?config: object)cm.save()cm.toTextArea()cm.getTextArea() → TextAreaElementCodeMirror.defaults: objectCodeMirror.defineExtension(name: string, value: any)defineExtension. This will cause the given
+ value (usually a method) to be added to all CodeMirror instances
+ created from then on.CodeMirror.defineDocExtension(name: string, value: any)defineExtension,
+ but the method will be added to the interface
+ for Doc objects instead.CodeMirror.defineOption(name: string,
+ default: any, updateFunc: function)defineOption can be used to define new options for
+ CodeMirror. The updateFunc will be called with the
+ editor instance and the new value when an editor is initialized,
+ and whenever the option is modified
+ through setOption.CodeMirror.defineInitHook(func: function)CodeMirror.defineInitHook. Give it a function as
+ its only argument, and from then on, that function will be called
+ (with the instance as argument) whenever a new CodeMirror instance
+ is initialized.CodeMirror.registerHelper(type: string, name: string, value: helper)name in
+ the given namespace (type). This is used to define
+ functionality that may be looked up by mode. Will create (if it
+ doesn't already exist) a property on the CodeMirror
+ object for the given type, pointing to an object
+ that maps names to values. I.e. after
+ doing CodeMirror.registerHelper("hint", "foo",
+ myFoo), the value CodeMirror.hint.foo will
+ point to myFoo.CodeMirror.registerGlobalHelper(type: string, name: string, predicate: fn(mode, CodeMirror), value: helper)registerHelper,
+ but also registers this helper as 'global', meaning that it will
+ be included by getHelpers
+ whenever the given predicate returns true when
+ called with the local mode and editor.CodeMirror.Pos(line: integer, ?ch: integer, ?sticky: string)sticky defaults to
+ null, but can be set to "before"
+ or "after" to make the position explicitly
+ associate with the character before or after it.CodeMirror.changeEnd(change: object) → {line, ch}from, to,
+ and text properties, as passed to
+ various event handlers). The
+ returned position will be the end of the changed
+ range, after the change is applied.The addon directory in the distribution contains a
+ number of reusable components that implement extra editor
+ functionality (on top of extension functions
+ like defineOption, defineExtension,
+ and registerHelper). In
+ brief, they are:
dialog/dialog.jsopenDialog(template, callback, options) →
+ closeFunction method to CodeMirror instances,
+ which can be called with an HTML fragment or a detached DOM
+ node that provides the prompt (should include an input
+ or button tag), and a callback function that is called
+ when the user presses enter. It returns a function closeFunction
+ which, if called, will close the dialog immediately.
+ openDialog takes the following options:
+ closeOnEnter: booltrue.closeOnBlur: booltrue.onKeyDown: fn(event: KeyboardEvent, value: string, close: fn()) → boolkeydown fires in the
+ dialog's input. If your callback returns true,
+ the dialog will not do any further processing of the event.onKeyUp: fn(event: KeyboardEvent, value: string, close: fn()) → boolonKeyDown but for the
+ keyup event.onInput: fn(event: InputEvent, value: string, close: fn()) → boolonKeyDown but for the
+ input event.onClose: fn(instance):Also adds an openNotification(template, options) →
+ closeFunction function that simply shows an HTML
+ fragment as a notification at the top of the editor. It takes a
+ single option: duration, the amount of time after
+ which the notification will be automatically closed. If
+ duration is zero, the dialog will not be closed automatically.
Depends on addon/dialog/dialog.css.
search/searchcursor.jsgetSearchCursor(query, start, caseFold) →
+ cursor method to CodeMirror instances, which can be used
+ to implement search/replace functionality. query
+ can be a regular expression or a string (only strings will match
+ across lines—if they contain newlines). start
+ provides the starting position of the search. It can be
+ a {line, ch} object, or can be left off to default
+ to the start of the document. caseFold is only
+ relevant when matching a string. It will cause the search to be
+ case-insensitive. A search cursor has the following methods:
+ findNext() → booleanfindPrevious() → booleanmatch method, in case you
+ want to extract matched groups.from() → {line, ch}to() → {line, ch}findNext or findPrevious did
+ not return false. They will return {line, ch}
+ objects pointing at the start and end of the match.replace(text: string, ?origin: string)search/search.jssearchcursor.js, and will make use
+ of openDialog when
+ available to make prompting for search queries less ugly.search/jump-to-line.jsjumpToLine command and binding Alt-G to it.
+ Accepts linenumber, +/-linenumber, line:char,
+ scroll% and :linenumber formats.
+ This will make use of openDialog
+ when available to make prompting for line number neater.search/matchesonscrollbar.jsshowMatchesOnScrollbar method to editor
+ instances, which should be given a query (string or regular
+ expression), optionally a case-fold flag (only applicable for
+ strings), and optionally a class name (defaults
+ to CodeMirror-search-match) as arguments. When
+ called, matches of the given query will be displayed on the
+ editor's vertical scrollbar. The method returns an object with
+ a clear method that can be called to remove the
+ matches. Depends on
+ the annotatescrollbar
+ addon, and
+ the matchesonscrollbar.css
+ file provides a default (transparent yellowish) definition of
+ the CSS class applied to the matches. Note that the matches are
+ only perfectly aligned if your scrollbar does not have buttons
+ at the top and bottom. You can use
+ the simplescrollbar
+ addon to make sure of this. If this addon is loaded,
+ the search addon will
+ automatically use it.edit/matchbrackets.jsmatchBrackets which, when set
+ to true, causes matching brackets to be highlighted whenever the
+ cursor is next to them. It also adds a
+ method matchBrackets that forces this to happen
+ once, and a method findMatchingBracket that can be
+ used to run the bracket-finding algorithm that this uses
+ internally.edit/closebrackets.jsautoCloseBrackets that will
+ auto-close brackets and quotes when typed. By default, it'll
+ auto-close ()[]{}''"", but you can pass it a string
+ similar to that (containing pairs of matching characters), or an
+ object with pairs and
+ optionally explode properties to customize
+ it. explode should be a similar string that gives
+ the pairs of characters that, when enter is pressed between
+ them, should have the second character also moved to its own
+ line. By default, if the active mode has
+ a closeBrackets property, that overrides the
+ configuration given in the option. But you can add
+ an override property with a truthy value to
+ override mode-specific
+ configuration. Demo
+ here.edit/matchtags.jsmatchTags that, when enabled,
+ will cause the tags around the cursor to be highlighted (using
+ the CodeMirror-matchingtag class). Also
+ defines
+ a command toMatchingTag,
+ which you can bind a key to in order to jump to the tag matching
+ the one under the cursor. Depends on
+ the addon/fold/xml-fold.js
+ addon. Demo here.edit/trailingspace.jsshowTrailingSpace which, when
+ enabled, adds the CSS class cm-trailingspace to
+ stretches of whitespace at the end of lines.
+ The demo has a nice
+ squiggly underline style for this class.edit/closetag.jsautoCloseTags option that will
+ auto-close XML tags when '>' or '/'
+ is typed, and
+ a closeTag command that
+ closes the nearest open tag. Depends on
+ the fold/xml-fold.js addon. See
+ the demo.edit/continuelist.js"newlineAndIndentContinueMarkdownList" command
+ that can be bound to enter to automatically
+ insert the leading characters for continuing a list. See
+ the Markdown mode
+ demo.comment/comment.jstoggleComment(from: {line, ch}, to: {line, ch}, ?options: object)lineComment(from: {line, ch}, to: {line, ch}, ?options: object)blockComment when no line comment
+ style is defined for the mode.blockComment(from: {line, ch}, to: {line, ch}, ?options: object)lineComment when no block comment
+ style is defined for the mode.uncomment(from: {line, ch}, to: {line, ch}, ?options: object) → booleantrue if a comment range was found and
+ removed, false otherwise.options object accepted by these methods may
+ have the following properties:
+ blockCommentStart, blockCommentEnd, blockCommentLead, lineComment: stringpadding: stringcommentBlankLines: booleanindent: booleanfullLines: booleantrue.toggleComment command,
+ which is a shorthand command for calling
+ toggleComment with no options.fold/foldcode.jsfoldCode method
+ to editor instances, which will try to do a code fold starting
+ at the given line, or unfold the fold that is already present.
+ The method takes as first argument the position that should be
+ folded (may be a line number or
+ a Pos), and as second optional
+ argument either a range-finder function, or an options object,
+ supporting the following properties:
+ rangeFinder: fn(CodeMirror, Pos)CodeMirror.fold.auto, which
+ uses getHelpers with
+ a "fold" type to find folding functions
+ appropriate for the local mode. There are files in
+ the addon/fold/
+ directory providing CodeMirror.fold.brace, which
+ finds blocks in brace languages (JavaScript, C, Java,
+ etc), CodeMirror.fold.indent, for languages where
+ indentation determines block structure (Python, Haskell),
+ and CodeMirror.fold.xml, for XML-style languages,
+ and CodeMirror.fold.comment, for folding comment
+ blocks.widget: string|ElementCodeMirror-foldmarker, or a DOM node.scanUp: booleanminFoldSize: integerfold/foldgutter.jsfoldGutter, which can be
+ used to create a gutter with markers indicating the blocks that
+ can be folded. Create a gutter using
+ the gutters option,
+ giving it the class CodeMirror-foldgutter or
+ something else if you configure the addon to use a different
+ class, and this addon will show markers next to folded and
+ foldable blocks, and handle clicks in this gutter. Note that
+ CSS styles should be applied to make the gutter, and the fold
+ markers within it, visible. A default set of CSS styles are
+ available in:
+
+ addon/fold/foldgutter.css
+ .
+ The option
+ can be either set to true, or an object containing
+ the following optional option fields:
+ gutter: string"CodeMirror-foldgutter". You will have to
+ style this yourself to give it a width (and possibly a
+ background). See the default gutter style rules above.indicatorOpen: string | Element"CodeMirror-foldgutter-open".indicatorFolded: string | Element"CodeMirror-foldgutter-folded".rangeFinder: fn(CodeMirror, Pos)CodeMirror.fold.auto
+ will be used as default.foldOptions editor option can be set to an
+ object to provide an editor-wide default configuration.
+ Demo here.runmode/runmode.jsbin/source-highlight for an example of using the latter).runmode/colorize.jsrunmode addon (or
+ its standalone variant). Provides
+ a CodeMirror.colorize function that can be called
+ with an array (or other array-ish collection) of DOM nodes that
+ represent the code snippets. By default, it'll get
+ all pre tags. Will read the data-lang
+ attribute of these nodes to figure out their language, and
+ syntax-color their content using the relevant CodeMirror mode
+ (you'll have to load the scripts for the relevant modes
+ yourself). A second argument may be provided to give a default
+ mode, used when no language attribute is found for a node. Used
+ in this manual to highlight example code.mode/overlay.jsCodeMirror.overlayMode, which is used to
+ create such a mode. See this
+ demo for a detailed example.mode/multiplex.jsCodeMirror.multiplexingMode which, when
+ given as first argument a mode object, and as other arguments
+ any number of {open, close, mode [, delimStyle, innerStyle, parseDelimiters]}
+ objects, will return a mode object that starts parsing using the
+ mode passed as first argument, but will switch to another mode
+ as soon as it encounters a string that occurs in one of
+ the open fields of the passed objects. When in a
+ sub-mode, it will go back to the top mode again when
+ the close string is encountered.
+ Pass "\n" for open or close
+ if you want to switch on a blank line.
+ delimStyle is specified, it will be the token
+ style returned for the delimiter tokens (as well as
+ [delimStyle]-open on the opening token and
+ [delimStyle]-close on the closing token).innerStyle is specified, it will be the token
+ style added for each inner mode token.parseDelimiters is true, the content of
+ the delimiters will also be passed to the inner mode.
+ (And delimStyle is ignored.)hint/show-hint.jseditor.showHint, which takes an optional
+ options object, and pops up a widget that allows the user to
+ select a completion. Finding hints is done with a hinting
+ functions (the hint option), which is a function
+ that take an editor instance and options object, and return
+ a {list, from, to} object, where list
+ is an array of strings or objects (the completions),
+ and from and to give the start and end
+ of the token that is being completed as {line, ch}
+ objects. An optional selectedHint property (an
+ integer) can be added to the completion object to control the
+ initially selected hint.CodeMirror.hint.auto, which
+ calls getHelpers with
+ the "hint" type to find applicable hinting
+ functions, and tries them one by one. If that fails, it looks
+ for a "hintWords" helper to fetch a list of
+ completable words for the mode, and
+ uses CodeMirror.hint.fromList to complete from
+ those.text: stringdisplayText: stringclassName: stringrender: fn(Element, self, data)hint: fn(CodeMirror, self, data)from: {line, ch}from position that will be used by pick() instead
+ of the global one passed with the full list of completions.to: {line, ch}to position that will be used by pick() instead
+ of the global one passed with the full list of completions.hint: functionasync property on a hinting function to
+ true, in which case it will be called with
+ arguments (cm, callback, ?options), and the
+ completion interface will only be popped up when the hinting
+ function calls the callback, passing it the object holding the
+ completions.
+ The hinting function can also return a promise, and the completion
+ interface will only be popped when the promise resolves.
+ By default, hinting only works when there is no
+ selection. You can give a hinting function
+ a supportsSelection property with a truthy value
+ to indicate that it supports selections.completeSingle: booleanalignWithWord: booleancloseOnUnfocus: booleancustomKeys: keymapmoveFocus(n), setFocus(n), pick(),
+ and close() methods (see the source for details),
+ that can be used to change the focused element, pick the
+ current element or close the menu. Additionally menuSize()
+ can give you access to the size of the current dropdown menu,
+ length give you the number of available completions, and
+ data give you full access to the completion returned by the
+ hinting function.extraKeys: keymapcustomKeys above, but the bindings will
+ be added to the set of default bindings, instead of replacing
+ them."shown" ()"select" (completion, Element)"pick" (completion)"close" ()addon/hint/show-hint.css. Check
+ out the demo for an
+ example.hint/javascript-hint.jsCodeMirror.hint.javascript) and CoffeeScript
+ (CodeMirror.hint.coffeescript) code. This will
+ simply use the JavaScript environment that the editor runs in as
+ a source of information about objects and their properties.hint/xml-hint.jsCodeMirror.hint.xml, which produces
+ hints for XML tagnames, attribute names, and attribute values,
+ guided by a schemaInfo option (a property of the
+ second argument passed to the hinting function, or the third
+ argument passed to CodeMirror.showHint)."!top" property
+ containing a list of the names of valid top-level tags. The
+ values of the properties should be objects with optional
+ properties children (an array of valid child
+ element names, omit to simply allow all tags to appear)
+ and attrs (an object mapping attribute names
+ to null for free-form attributes, and an array of
+ valid values for restricted
+ attributes). Demo
+ here.hint/html-hint.jsCodeMirror.htmlSchema that you can pass to
+ as a schemaInfo option, and
+ a CodeMirror.hint.html hinting function that
+ automatically calls CodeMirror.hint.xml with this
+ schema data. See
+ the demo.hint/css-hint.jsCodeMirror.hint.css.hint/anyword-hint.jsCodeMirror.hint.anyword) that simply looks for
+ words in the nearby code and completes to those. Takes two
+ optional options, word, a regular expression that
+ matches words (sequences of one or more character),
+ and range, which defines how many lines the addon
+ should scan when completing (defaults to 500).hint/sql-hint.jsCodeMirror.hint.sql.
+ Takes two optional options, tables, a object with
+ table names as keys and array of respective column names as values,
+ and defaultTable, a string corresponding to a
+ table name in tables for autocompletion.search/match-highlighter.jshighlightSelectionMatches option that
+ can be enabled to highlight all instances of a currently
+ selected word. Can be set either to true or to an object
+ containing the following options: minChars, for the
+ minimum amount of selected characters that triggers a highlight
+ (default 2), style, for the style to be used to
+ highlight the matches (default "matchhighlight",
+ which will correspond to CSS
+ class cm-matchhighlight), trim, which
+ controls whether whitespace is trimmed from the selection,
+ and showToken which can be set to true
+ or to a regexp matching the characters that make up a word. When
+ enabled, it causes the current word to be highlighted when
+ nothing is selected (defaults to off).
+ Demo here.lint/lint.jshtml-lint.js,
+ json-lint.js,
+ javascript-lint.js,
+ coffeescript-lint.js,
+ and css-lint.js
+ in the same directory). Defines a lint option that
+ can be set to an annotation source (for
+ example CodeMirror.lint.javascript), to an options
+ object (in which case the getAnnotations field is
+ used as annotation source), or simply to true. When
+ no annotation source is
+ specified, getHelper with
+ type "lint" is used to find an annotation function.
+ An annotation source function should, when given a document
+ string, an options object, and an editor instance, return an
+ array of {message, severity, from, to} objects
+ representing problems. When the function has
+ an async property with a truthy value, it will be
+ called with an additional second argument, which is a callback
+ to pass the array to.
+ The linting function can also return a promise, in that case the linter
+ will only be executed when the promise resolves.
+ By default, the linter will run (debounced) whenever the document is changed.
+ You can pass a lintOnChange: false option to disable that.
+ Depends on addon/lint/lint.css. A demo can be
+ found here.selection/mark-selection.jsCodeMirror-selectedtext when the styleSelectedText option
+ is enabled. Useful to change the colour of the selection (in addition to the background),
+ like in this demo.selection/active-line.jsstyleActiveLine option that, when
+ enabled, gives the wrapper of the line that contains the cursor
+ the class CodeMirror-activeline, adds a background
+ with the class CodeMirror-activeline-background,
+ and adds the class CodeMirror-activeline-gutter to
+ the line's gutter space is enabled. The option's value may be a
+ boolean or an object specifying the following options:
+ nonEmpty: boolselection/selection-pointer.jsselectionPointer option which you can
+ use to control the mouse cursor appearance when hovering over
+ the selection. It can be set to a string,
+ like "pointer", or to true, in which case
+ the "default" (arrow) cursor will be used. You can
+ see a demo here.mode/loadmode.jsCodeMirror.requireMode(modename,
+ callback) function that will try to load a given mode and
+ call the callback when it succeeded. You'll have to
+ set CodeMirror.modeURL to a string that mode paths
+ can be constructed from, for
+ example "mode/%N/%N.js"—the %N's will
+ be replaced with the mode name. Also
+ defines CodeMirror.autoLoadMode(instance, mode),
+ which will ensure the given mode is loaded and cause the given
+ editor instance to refresh its mode when the loading
+ succeeded. See the demo.mode/meta.jsCodeMirror.modeInfo, an array of objects
+ with {name, mime, mode} properties,
+ where name is the human-readable
+ name, mime the MIME type, and mode the
+ name of the mode file that defines this MIME. There are optional
+ properties mimes, which holds an array of MIME
+ types for modes with multiple MIMEs associated,
+ and ext, which holds an array of file extensions
+ associated with this mode. Four convenience
+ functions, CodeMirror.findModeByMIME,
+ CodeMirror.findModeByExtension,
+ CodeMirror.findModeByFileName
+ and CodeMirror.findModeByName are provided, which
+ return such an object given a MIME, extension, file name or mode name
+ string. Note that, for historical reasons, this file resides in the
+ top-level mode directory, not
+ under addon. Demo.comment/continuecomment.jscontinueComments option, which sets whether the
+ editor will make the next line continue a comment when you press Enter
+ inside a comment block. Can be set to a boolean to enable/disable this
+ functionality. Set to a string, it will continue comments using a custom
+ shortcut. Set to an object, it will use the key property for
+ a custom shortcut and the boolean continueLineComment
+ property to determine whether single-line comments should be continued
+ (defaulting to true).display/placeholder.jsplaceholder option that can be used to
+ make content appear in the editor when it is empty and not
+ focused. It can hold either a string or a DOM node. Also gives
+ the editor a CodeMirror-empty CSS class whenever it
+ doesn't contain any text.
+ See the demo.display/fullscreen.jsfullScreen that, when set
+ to true, will make the editor full-screen (as in,
+ taking up the whole browser window). Depends
+ on fullscreen.css. Demo
+ here.display/autorefresh.jsrefresh when the editor
+ becomes visible. It defines an option autoRefresh
+ which you can set to true to ensure that, if the editor wasn't
+ visible on initialization, it will be refreshed the first time
+ it becomes visible. This is done by polling every 250
+ milliseconds (you can pass a value like {delay:
+ 500} as the option value to configure this). Note that
+ this addon will only refresh the editor once when it
+ first becomes visible, and won't take care of further restyling
+ and resizing.scroll/simplescrollbars.js"simple" and "overlay"
+ (see demo) that can
+ be selected with
+ the scrollbarStyle
+ option. Depends
+ on simplescrollbars.css,
+ which can be further overridden to style your own
+ scrollbars.scroll/annotatescrollbar.jsannotateScrollbar to editor instances that
+ can be called, with a CSS class name as argument, to create a
+ set of annotations. The method returns an object
+ whose update method can be called with a sorted array
+ of {from: Pos, to: Pos} objects marking the ranges
+ to be highlighted. To detach the annotations, call the
+ object's clear method.display/rulers.jsrulers option, which can be used to show
+ one or more vertical rulers in the editor. The option, if
+ defined, should be given an array of {column [, className,
+ color, lineStyle, width]} objects or numbers (which
+ indicate a column). The ruler will be displayed at the column
+ indicated by the number or the column property.
+ The className property can be used to assign a
+ custom style to a ruler. Demo
+ here.display/panel.jsaddPanel method for CodeMirror
+ instances, which places a DOM node above or below an editor, and
+ shrinks the editor to make room for the node. The method takes
+ as first argument as DOM node, and as second an optional options
+ object. The Panel object returned by this method
+ has a clear method that is used to remove the
+ panel, and a changed method that can be used to
+ notify the addon when the size of the panel's DOM node has
+ changed.position: stringtop (default)after-topbottombefore-bottombefore: Panelafter: Panelreplace: Panelstable: boolafter, before or replace options,
+ if the panel doesn't exists or has been removed,
+ the value of the position option will be used as a fallback.
+ wrap/hardwrap.jswrapParagraph(?pos: {line, ch}, ?options: object)pos is not given, it defaults to the cursor
+ position.wrapRange(from: {line, ch}, to: {line, ch}, ?options: object)wrapParagraphsInRange(from: {line, ch}, to: {line, ch}, ?options: object)paragraphStart, paragraphEnd: RegExpcolumn: numberwrapOn: RegExpkillTrailingSpace: booleanmerge/merge.jsCodeMirror.MergeView
+ constructor takes arguments similar to
+ the CodeMirror
+ constructor, first a node to append the interface to, and then
+ an options object. Options are passed through to the editors
+ inside the view. These extra options are recognized:
+ origLeft and origRight: stringrevertButtons: booleanrevertChunk: fn(mv: MergeView, from: CodeMirror, fromStart: Pos, fromEnd: Pos, to: CodeMirror, toStart: Pos, toEnd: Pos)connect: string"align", the smaller chunk is padded to
+ align with the bigger chunk instead.collapseIdentical: boolean|numberallowEditingOriginals: booleanshowDifferences: booleanchunkClassLocation: string|ArrayaddLineClass
+ with "background". Override this to customize it to be any
+ valid `where` parameter or an Array of valid `where`
+ parameters."goNextDiff"
+ and "goPrevDiff" to quickly jump to the next
+ changed chunk. Demo
+ here.tern/tern.jsModes typically consist of a single JavaScript file. This file + defines, in the simplest case, a lexer (tokenizer) for your + language—a function that takes a character stream as input, + advances it past a token, and returns a style for that token. More + advanced modes can also handle indentation for the language.
+ +This section describes the low-level mode interface. Many modes + are written directly against this, since it offers a lot of + control, but for a quick mode definition, you might want to use + the simple mode addon.
+ +The mode script should
+ call CodeMirror.defineMode to
+ register itself with CodeMirror. This function takes two
+ arguments. The first should be the name of the mode, for which you
+ should use a lowercase string, preferably one that is also the
+ name of the files that define the mode (i.e. "xml" is
+ defined in xml.js). The second argument should be a
+ function that, given a CodeMirror configuration object (the thing
+ passed to the CodeMirror function) and an optional
+ mode configuration object (as in
+ the mode option), returns
+ a mode object.
Typically, you should use this second argument
+ to defineMode as your module scope function (modes
+ should not leak anything into the global scope!), i.e. write your
+ whole mode inside this function.
The main responsibility of a mode script is parsing + the content of the editor. Depending on the language and the + amount of functionality desired, this can be done in really easy + or extremely complicated ways. Some parsers can be stateless, + meaning that they look at one element (token) of the code + at a time, with no memory of what came before. Most, however, will + need to remember something. This is done by using a state + object, which is an object that is always passed when + reading a token, and which can be mutated by the tokenizer.
+ +Modes that use a state must define
+ a startState method on their mode
+ object. This is a function of no arguments that produces a state
+ object to be used at the start of a document.
The most important part of a mode object is
+ its token(stream, state) method. All
+ modes must define this method. It should read one token from the
+ stream it is given as an argument, optionally update its state,
+ and return a style string, or null for tokens that do
+ not have to be styled. For your styles, you are encouraged to use
+ the 'standard' names defined in the themes (without
+ the cm- prefix). If that fails, it is also possible
+ to come up with your own and write your own CSS theme file.
+ +
A typical token string would
+ be "variable" or "comment". Multiple
+ styles can be returned (separated by spaces), for
+ example "string error" for a thing that looks like a
+ string but is invalid somehow (say, missing its closing quote).
+ When a style is prefixed by "line-"
+ or "line-background-", the style will be applied to
+ the whole line, analogous to what
+ the addLineClass method
+ does—styling the "text" in the simple case, and
+ the "background" element
+ when "line-background-" is prefixed.
The stream object that's passed
+ to token encapsulates a line of code (tokens may
+ never span lines) and our current position in that line. It has
+ the following API:
eol() → booleansol() → booleanpeek() → stringnull at the end of the
+ line.next() → stringnull when no more characters are
+ available.eat(match: string|regexp|function(char: string) → boolean) → stringmatch can be a character, a regular expression,
+ or a function that takes a character and returns a boolean. If
+ the next character in the stream 'matches' the given argument,
+ it is consumed and returned. Otherwise, undefined
+ is returned.eatWhile(match: string|regexp|function(char: string) → boolean) → booleaneat with the given argument,
+ until it fails. Returns true if any characters were eaten.eatSpace() → booleaneatWhile when matching
+ white-space.skipToEnd()skipTo(str: string) → booleanmatch(pattern: string, ?consume: boolean, ?caseFold: boolean) → booleanmatch(pattern: regexp, ?consume: boolean) → array<string>eat—if consume is true
+ or not given—or a look-ahead that doesn't update the stream
+ position—if it is false. pattern can be either a
+ string or a regular expression starting with ^.
+ When it is a string, caseFold can be set to true to
+ make the match case-insensitive. When successfully matching a
+ regular expression, the returned value will be the array
+ returned by match, in case you need to extract
+ matched groups.backUp(n: integer)n characters. Backing it up
+ further than the start of the current token will cause things to
+ break, so be careful.column() → integerindentation() → integercurrent() → stringBy default, blank lines are simply skipped when
+ tokenizing a document. For languages that have significant blank
+ lines, you can define
+ a blankLine(state) method on your
+ mode that will get called whenever a blank line is passed over, so
+ that it can update the parser state.
Because state object are mutated, and CodeMirror
+ needs to keep valid versions of a state around so that it can
+ restart a parse at any line, copies must be made of state objects.
+ The default algorithm used is that a new state object is created,
+ which gets all the properties of the old object. Any properties
+ which hold arrays get a copy of these arrays (since arrays tend to
+ be used as mutable stacks). When this is not correct, for example
+ because a mode mutates non-array properties of its state object, a
+ mode object should define
+ a copyState method, which is given a
+ state and should return a safe copy of that state.
If you want your mode to provide smart indentation
+ (through the indentLine
+ method and the indentAuto
+ and newlineAndIndent commands, to which keys can be
+ bound), you must define
+ an indent(state, textAfter) method
+ on your mode object.
The indentation method should inspect the given state object,
+ and optionally the textAfter string, which contains
+ the text on the line that is being indented, and return an
+ integer, the amount of spaces to indent. It should usually take
+ the indentUnit
+ option into account. An indentation method may
+ return CodeMirror.Pass to indicate that it
+ could not come up with a precise indentation.
To work well with
+ the commenting addon, a mode may
+ define lineComment (string that
+ starts a line
+ comment), blockCommentStart, blockCommentEnd
+ (strings that start and end block comments),
+ and blockCommentLead (a string to put at the start of
+ continued lines in a block comment). All of these are
+ optional.
Finally, a mode may define either
+ an electricChars or an electricInput
+ property, which are used to automatically reindent the line when
+ certain patterns are typed and
+ the electricChars
+ option is enabled. electricChars may be a string, and
+ will trigger a reindent whenever one of the characters in that
+ string are typed. Often, it is more appropriate to
+ use electricInput, which should hold a regular
+ expression, and will trigger indentation when the part of the
+ line before the cursor matches the expression. It should
+ usually end with a $ character, so that it only
+ matches when the indentation-changing pattern was just typed, not when something was
+ typed after the pattern.
So, to summarize, a mode must provide
+ a token method, and it may
+ provide startState, copyState,
+ and indent methods. For an example of a trivial mode,
+ see the diff mode, for a more
+ involved example, see the C-like
+ mode.
Sometimes, it is useful for modes to nest—to have one
+ mode delegate work to another mode. An example of this kind of
+ mode is the mixed-mode HTML
+ mode. To implement such nesting, it is usually necessary to
+ create mode objects and copy states yourself. To create a mode
+ object, there are CodeMirror.getMode(options,
+ parserConfig), where the first argument is a configuration
+ object as passed to the mode constructor function, and the second
+ argument is a mode specification as in
+ the mode option. To copy a
+ state object, call CodeMirror.copyState(mode, state),
+ where mode is the mode that created the given
+ state.
In a nested mode, it is recommended to add an
+ extra method, innerMode which, given
+ a state object, returns a {state, mode} object with
+ the inner mode and its state for the current position. These are
+ used by utility scripts such as the tag
+ closer to get context information. Use
+ the CodeMirror.innerMode helper function to, starting
+ from a mode and a state, recursively walk down to the innermost
+ mode and state.
To make indentation work properly in a nested parser, it is
+ advisable to give the startState method of modes that
+ are intended to be nested an optional argument that provides the
+ base indentation for the block of code. The JavaScript and CSS
+ parser do this, for example, to allow JavaScript and CSS code
+ inside the mixed-mode HTML mode to be properly indented.
It is possible, and encouraged, to associate
+ your mode, or a certain configuration of your mode, with
+ a MIME type. For
+ example, the JavaScript mode associates itself
+ with text/javascript, and its JSON variant
+ with application/json. To do this,
+ call CodeMirror.defineMIME(mime,
+ modeSpec), where modeSpec can be a string or
+ object specifying a mode, as in
+ the mode option.
If a mode specification wants to add some properties to the
+ resulting mode object, typically for use
+ with getHelpers, it may
+ contain a modeProps property, which holds an object.
+ This object's properties will be copied to the actual mode
+ object.
Sometimes, it is useful to add or override mode
+ object properties from external code.
+ The CodeMirror.extendMode function
+ can be used to add properties to mode objects produced for a
+ specific mode. Its first argument is the name of the mode, its
+ second an object that specifies the properties that should be
+ added. This is mostly useful to add utilities that can later be
+ looked up through getMode.
CodeMirror has a robust VIM mode that attempts to faithfully
+ emulate VIM's most useful features. It can be enabled by
+ including keymap/vim.js
+ and setting the keyMap option to
+ "vim".
VIM mode accepts configuration options for customizing
+ behavior at run time. These methods can be called at any time
+ and will affect all existing CodeMirror instances unless
+ specified otherwise. The methods are exposed on the
+ CodeMirror.Vim object.
setOption(name: string, value: any, ?cm: CodeMirror, ?cfg: object)name should
+ be the name of an option. If cfg.scope is not set
+ and cm is provided, then sets the global and
+ instance values of the option. Otherwise, sets either the
+ global or instance value of the option depending on whether
+ cfg.scope is global or
+ local.getOption(name: string, ?cm: CodeMirror: ?cfg: object)cfg.scope is not set and cm is
+ provided, then gets the instance value of the option, falling
+ back to the global value if not set. If cfg.scope is provided, then gets the global or
+ local value without checking the other.map(lhs: string, rhs: string, ?context: string):map command. To map ; to : in VIM would be
+ :map ; :. That would translate to
+ CodeMirror.Vim.map(';', ':');.
+ The context can be normal,
+ visual, or insert, which correspond
+ to :nmap, :vmap, and
+ :imap
+ respectively.mapCommand(keys: string, type: string, name: string, ?args: object, ?extra: object)motion,
+ operator, or action type command.
+ The args object is passed through to the command when it is
+ invoked by the provided key sequence.
+ extras.context can be normal,
+ visual, or insert, to map the key
+ sequence only in the corresponding mode.
+ extras.isEdit is applicable only to actions,
+ determining whether it is recorded for replay for the
+ . single-repeat command.
+ CodeMirror's VIM mode implements a large subset of VIM's core
+ editing functionality. But since there's always more to be
+ desired, there is a set of APIs for extending VIM's
+ functionality. As with the configuration API, the methods are
+ exposed on CodeMirror.Vim and may
+ be called at any time.
defineOption(name: string, default: any, type: string, ?aliases: array<string>, ?callback: function (?value: any, ?cm: CodeMirror) → ?any):set command. Type can be boolean or
+ string, used for validation and by
+ :set to determine which syntax to accept. If a
+ callback is passed in, VIM does not store the value of the
+ option itself, but instead uses the callback as a setter/getter. If the
+ first argument to the callback is undefined, then the
+ callback should return the value of the option. Otherwise, it should set
+ instead. Since VIM options have global and instance values, whether a
+ CodeMirror instance is passed in denotes whether the global
+ or local value should be used. Consequently, it's possible for the
+ callback to be called twice for a single setOption or
+ getOption call. Note that right now, VIM does not support
+ defining buffer-local options that do not have global values. If an
+ option should not have a global value, either always ignore the
+ cm parameter in the callback, or always pass in a
+ cfg.scope to setOption and
+ getOption.defineMotion(name: string, fn: function(cm: CodeMirror, head: {line, ch}, ?motionArgs: object}) → {line, ch})head
+ is the current position of the cursor. It can differ from
+ cm.getCursor('head') if VIM is in visual mode.
+ motionArgs is the object passed into
+ mapCommand().defineOperator(name: string, fn: function(cm: CodeMirror, ?operatorArgs: object, ranges: array<{anchor, head}>) → ?{line, ch})
+ defineMotion. ranges is the range
+ of text the operator should operate on. If the cursor should
+ be set to a certain position after the operation finishes, it
+ can return a cursor object.defineAction(name: string, fn: function(cm: CodeMirror, ?actionArgs: object))defineMotion. Action commands
+ can have arbitrary behavior, making them more flexible than
+ motions and operators, at the loss of orthogonality.defineEx(name: string, ?prefix: string, fn: function(cm: CodeMirror, ?params: object)):name.
+ If a prefix is provided, it, and any prefixed substring of the
+ name beginning with the prefix can
+ be used to invoke the command. If the prefix is
+ falsy, then name is used as the prefix.
+ params.argString contains the part of the prompted
+ string after the command name. params.args is
+ params.argString split by whitespace. If the
+ command was prefixed with a
+ line range,
+ params.line and params.lineEnd will
+ be set.
+ Create a pull + request if you'd like your project to be added to this list.
+ +
+
+
+ 20-03-2017: Version 5.25.0:
+ +role=presentation to more DOM elements to improve screen reader support.22-02-2017: Version 5.24.2:
+ +20-02-2017: Version 5.24.0:
+ +sticky property which determines whether they should be associated with the character before (value "before") or after (value "after") them.useInnerComments option to optionally suppress descending to the inner modes to get comment strings.lineComment property for LESS and SCSS dialects. Recognize vendor prefixes on pseudo-elements.elseif lines.#, @, and : chars.19-01-2017: Version 5.23.0:
+ +findModeByMIME now understands +json and +xml MIME suffixes.override option to ignore language-specific defaults.stable option that auto-scrolls the content to keep it in the same place when inserting/removing a panel.20-12-2016: Version 5.22.0:
+ +selectBetweenBrackets work with multiple cursors.CodeMirror.emacs to allow other addons to hook into Emacs-style functionality.nonEmpty option.optionChange.21-11-2016: Version 5.21.0:
+ +<body>.setGutterMarker, clearGutter, and lineInfo methods are now available on Doc objects.heightAtLine method now takes an extra argument to allow finding the height at the top of the line's line widgets.else and elsif are now immediately indented.20-10-2016: Version 5.20.0:
+ +newlineAndIndent command work with multiple cursors on the same line.cm-cm-overlay class.abstract keyword, and return type declarations for arrow functions.src/ directory. A git checkout no longer contains a working codemirror.js until you npm build (but when installing from NPM, it is included).refresh event is now documented and stable.20-09-2016: Version 5.19.0:
+ +type keyword.blur and focus events now pass the DOM event to their handlers.23-08-2016: Version 5.18.2:
+ +22-08-2016: Version 5.18.0:
+ +inputStyle now properly supports pasting on pre-Edge IE versions.addOverlay method now supports a priority option to control the order in which overlays are applied.+json now default to the JSON mode when the MIME itself is not defined.19-07-2016: Version 5.17.0:
+ +async, allow trailing commas in import lists.20-06-2016: Version 5.16.0:
+ +20-05-2016: Version 5.15.2:
+ +20-05-2016: Version 5.15.0:
+ +startState method from several wrapping modes.async/await and improve support for TypeScript type syntax.20-04-2016: Version 5.14.0:
+ +posFromIndex and indexFromPos now take lineSeparator into account.save() when it is actually available@ operatorsfindMarks: No longer return marks that touch but don't overlap given rangetrim option to disable ignoring of whitespace21-03-2016: Version 5.13.2:
+ +21-03-2016: Version 5.13:
+ +"dragleave".findMarks sometimes failed to find multi-line marks.swapDoc.19-02-2016: Version 5.12:
+ +unmap method to unmap bindings.text/html to parse HTML.=> functions.<script>, <style>, etc).matchClosing option to configure whether mismatched closing tags should be highlighted as errors.20-01-2016: Version 5.11:
+ +cut, copy, paste, and touchstart. It will also forward mousedown for drag events21-12-2015: Version 5.10:
+ +isReadOnly."beforeSelectionChange" events now has an origin property.23-11-2015: Version 5.9:
+ +20-10-2015: Version 5.8:
+ +allowDropFileTypes. Binary files can no longer be dropped into CodeMirror20-09-2015: Version 5.7:
+ +acync/await and ocal and binary numbers in JavaScript mode20-08-2015: Version 5.6:
+ +readOnly editormaxHighlightLength are now less likely to mess up indentationautorefresh for refreshing an editor the first time it becomes visible, and html-lint for using HTMLHintsearch addon now recognizes \r and \n in pattern and replacement input20-07-2015: Version 5.5:
+ +lineSeparator (with corresponding method)
+ findPersistent command in
+ the search addon, for a dialog
+ that stays open as you cycle through matches25-06-2015: Version 5.4:
+ +20-05-2015: Version 5.3:
+ +show-hint addon (completeSingle option, "shown" and "close" events)switch statements, and roughly recognizes types and defined identifiers20-04-2015: Version 5.2:
+ +show-hint's
+ asynchronous mode"textarea" input stylelineWiseCopyCutfiletype setting23-03-2015: Version 5.1:
+ +goNextDiff and goPrevDiff.20-02-2015: Version 5.0:
+ +inputStyle to switch between hidden textarea and contenteditable input.getInputField
+ method is no longer guaranteed to return a textarea.20-02-2015: Version 4.13:
+ +closetag demo handles the slash character.22-01-2015: Version 4.12:
+ +closetag
+ addon now defines a "closeTag" command.findModeByFileName to the mode metadata
+ addon.sol property to only match at the start
+ of a line.selection-pointer
+ to style the mouse cursor over the selection.9-01-2015: Version 4.11:
+ +Unfortunately, 4.10 did not take care of the + Firefox scrolling issue entirely. This release adds two more patches + to address that.
+ +29-12-2014: Version 4.10:
+ +Emergency single-patch update to 4.9. Fixes + Firefox-specific problem where the cursor could end up behind the + horizontal scrollbar.
+ +23-12-2014: Version 4.9:
+ +22-11-2014: Version 4.8:
+ +getLineTokens."gutter" styles in addLineClass.20-10-2014: Version 4.7:
+ +findModeByMIME
+ and findModeByExtension.19-09-2014: Version 4.6:
+ +findWordAt21-08-2014: Version 4.5:
+ +goLineLeftSmart21-07-2014: Version 4.4:
+ +"change" is still guaranteed to fire
+ before "cursorActivity")23-06-2014: Version 4.3:
+ +:substitute, :global command.
+ cursorBlinkRate
+ to a negative value.19-05-2014: Version 4.2:
+ +22-04-2014: Version 4.1:
+ +"cursorActivity"
+ event now fires after all other events for the operation (and only
+ for handlers that were actually registered at the time the
+ activity happened).insertSoftTab.20-03-2014: Version 4.0:
+ +This is a new major version of CodeMirror. There + are a few incompatible changes in the API. Upgrade + with care, and read the upgrading + guide.
+ +22-04-2014: Version 3.24:
+ +Merges the improvements from 4.1 that could + easily be applied to the 3.x code. Also improves the way the editor + size is updated when line widgets change.
+ +20-03-2014: Version 3.23:
+ +brackets style to angle brackets, fix
+ case-sensitivity of tags for HTML.21-02-2014: Version 3.22:
+ +findMarks method.16-01-2014: Version 3.21:
+ +clearWhenEmpty to control auto-removal.getHelpers, and to register helpers matched on predicates with registerGlobalHelper.21-11-2013: Version 3.20:
+ +21-10-2013: Version 3.19:
+ +23-09-2013: Version 3.18:
+ +Emergency release to fix a problem in 3.17
+ where .setOption("lineNumbers", false) would raise an
+ error.
23-09-2013: Version 3.17:
+ +css-lint, css-hint.box-sizing.21-08-2013: Version 3.16:
+ +CodeMirror.fold.comment.29-07-2013: Version 3.15:
+ +getModeAt.20-06-2013: Version 3.14:
+ +markText
+ and addLineWidget
+ now take a handleMouseEvents option.lineAtHeight,
+ getTokenTypeAt.changeGeneration
+ and isClean."keyHandled"
+ and "inputRead".20-05-2013: Version 3.13:
+ +cursorScrollMargin and coverGutterNextToScrollbar.19-04-2013: Version 3.12:
+ +maxHighlightLength
+ and historyEventDelay.addToHistory
+ option for markText.20-03-2013: Version 3.11:
+ +collapserange,
+ formatting, and simple-hint
+ addons. plsql and mysql modes
+ (use sql mode).continuecomment
+ addon now exposes an option, rather than a command.placeholder, HTML completion.hasFocus, defaultCharWidth.beforeCursorEnter, renderLine.show-hint completion
+ dialog addon.21-02-2013: Version 3.1:
+ +CodeMirror.Pass to signal they
+ didn't handle the key.simple-hint.js.insertLeft option
+ to setBookmark.eachLine
+ method to iterate over a document."beforeChange"
+ and "beforeSelectionChange"
+ events."hide"
+ and "unhide"
+ events to marked ranges.coordsChar's
+ interpretation of its argument to match the documentation.25-01-2013: Version 3.02:
+ +Single-bugfix release. Fixes a problem that + prevents CodeMirror instances from being garbage-collected after + they become unused.
+ +21-01-2013: Version 3.01:
+ +/addon. You might have to adjust your
+ paths.rtlMoveVisually option.showIfHidden option for line widgets.fixedGutter option.10-12-2012: Version 3.0:
+ +New major version. Only + partially backwards-compatible. See + the upgrading guide for more + information. Changes since release candidate 2:
+ +20-11-2012: Version 3.0, release candidate 2:
+ +addKeyMap and removeKeyMap methods.formatting and closetag add-ons.20-11-2012: Version 3.0, release candidate 1:
+ +addLineClass
+ and removeLineClass,
+ drop setLineClass.isClean/markClean methods.compoundChange method, use better undo-event-combining heuristic.22-10-2012: Version 3.0, beta 2:
+ +gutterClick event.cursorHeight option.viewportMargin option.flattenSpans option.19-09-2012: Version 3.0, beta 1:
+ +21-01-2013: Version 2.38:
+ +Integrate some bugfixes, enhancements to the vim keymap, and new + modes + (D, Sass, APL) + from the v3 branch.
+ +20-12-2012: Version 2.37:
+ +20-11-2012: Version 2.36:
+ +scrollIntoView public.defaultTextHeight method.22-10-2012: Version 2.35:
+ +markText/undo interaction.defineInitHook function.19-09-2012: Version 2.34:
+ +compareStates is no longer needed.onHighlightComplete no longer works.CodeMirror.version property.23-08-2012: Version 2.33:
+ +getViewPort and onViewportChange API.false disabling handling (again).innerHTML. Remove CodeMirror.htmlEscape.23-07-2012: Version 2.32:
+ +Emergency fix for a bug where an editor with + line wrapping on IE will break when there is no + scrollbar.
+ +20-07-2012: Version 2.31:
+ +setSize method for programmatic resizing.getHistory and setHistory methods.getValue and getRange.22-06-2012: Version 2.3:
+ +getScrollInfo method.23-05-2012: Version 2.25:
+ +23-04-2012: Version 2.24:
+ +dragDrop
+ and onDragEvent
+ options.compoundChange API method.catchall in key maps,
+ add nofallthrough boolean field instead.26-03-2012: Version 2.23:
+ +setLineClass.charCoords
+ and cursorCoords with a mode argument.autofocus option.findMarksAt method.27-02-2012: Version 2.22:
+ +autoClearEmptyLines option.27-01-2012: Version 2.21:
+ +smartIndent
+ option.readOnly-mode.scrollTo method.20-12-2011: Version 2.2:
+ +coordsFromIndex
+ to posFromIndex,
+ add indexFromPos
+ method.21-11-2011: Version 2.18:
+Fixes TextMarker.clear, which is broken in 2.17.
21-11-2011: Version 2.17:
+setBookmark method.lib/util.27-10-2011: Version 2.16:
+coordsFromIndex method.setValue now no longer clears history. Use clearHistory for that.markText now
+ returns an object with clear and find
+ methods. Marked text is now more robust when edited.26-09-2011: Version 2.15:
+Fix bug that snuck into 2.14: Clicking the + character that currently has the cursor didn't re-focus the + editor.
+ +26-09-2011: Version 2.14:
+fixedGutter option.setValue breaking cursor movement.23-08-2011: Version 2.13:
+getGutterElement to API.smartHome option.25-07-2011: Version 2.12:
+innerHTML for HTML-escaping.04-07-2011: Version 2.11:
+replace method to search cursors, for cursor-preserving replacements.getStateAfter API and compareState mode API methods for finer-grained mode magic.getScrollerElement API method to manipulate the scrolling DIV.07-06-2011: Version 2.1:
+Add + a theme system + (demo). Note that this is not + backwards-compatible—you'll have to update your styles and + modes!
+ +07-06-2011: Version 2.02:
+26-05-2011: Version 2.01:
+coordsChar now worksonCursorActivity interfered with onChange.onChange."nocursor" mode for readOnly option.onHighlightComplete option.28-03-2011: Version 2.0:
+CodeMirror 2 is a complete rewrite that's + faster, smaller, simpler to use, and less dependent on browser + quirks. See this + and this + for more information.
+ +22-02-2011: Version 2.0 beta 2:
+Somewhat more mature API, lots of bugs shaken out.
+ +17-02-2011: Version 0.94:
+tabMode: "spaces" was modified slightly (now indents when something is selected).08-02-2011: Version 2.0 beta 1:
+CodeMirror 2 is a complete rewrite of + CodeMirror, no longer depending on an editable frame.
+ +19-01-2011: Version 0.93:
+save method to instances created with fromTextArea.28-03-2011: Version 1.0:
+17-12-2010: Version 0.92:
+styleNumbers option is now officially
+ supported and documented.onLineNumberClick option added.onLoad and
+ onCursorActivity callbacks. Old names still work, but
+ are deprecated.11-11-2010: Version 0.91:
+toTextArea to update the code in the textarea.noScriptCaching option (hack to ease development).02-10-2010: Version 0.9:
+height: "dynamic" more robust.enterMode and electricChars options to make indentation even more customizable.firstLineNumber option.@media rules by the CSS parser.22-07-2010: Version 0.8:
+cursorCoords method to find the screen
+ coordinates of the cursor.height: dynamic mode, where the editor's
+ height will adjust to the size of its content.toTextArea method in instances created with
+ fromTextArea.27-04-2010: Version + 0.67:
+More consistent page-up/page-down behaviour
+ across browsers. Fix some issues with hidden editors looping forever
+ when line-numbers were enabled. Make PHP parser parse
+ "\\" correctly. Have jumpToLine work on
+ line handles, and add cursorLine function to fetch the
+ line handle where the cursor currently is. Add new
+ setStylesheet function to switch style-sheets in a
+ running editor.
01-03-2010: Version + 0.66:
+Adds removeLine method to API.
+ Introduces the PLSQL parser.
+ Marks XML errors by adding (rather than replacing) a CSS class, so
+ that they can be disabled by modifying their style. Fixes several
+ selection bugs, and a number of small glitches.
12-11-2009: Version + 0.65:
+Add support for having both line-wrapping and
+ line-numbers turned on, make paren-highlighting style customisable
+ (markParen and unmarkParen config
+ options), work around a selection bug that Opera
+ reintroduced in version 10.
23-10-2009: Version + 0.64:
+Solves some issues introduced by the
+ paste-handling changes from the previous release. Adds
+ setSpellcheck, setTextWrapping,
+ setIndentUnit, setUndoDepth,
+ setTabMode, and setLineNumbers to
+ customise a running editor. Introduces an SQL parser. Fixes a few small
+ problems in the Python
+ parser. And, as usual, add workarounds for various newly discovered
+ browser incompatibilities.
31-08-2009: Version 0.63:
+Overhaul of paste-handling (less fragile), fixes for several + serious IE8 issues (cursor jumping, end-of-document bugs) and a number + of small problems.
+ +30-05-2009: Version 0.62:
+Introduces Python
+ and Lua parsers. Add
+ setParser (on-the-fly mode changing) and
+ clearHistory methods. Make parsing passes time-based
+ instead of lines-based (see the passTime option).
So you found a problem in CodeMirror. By all means, report it! Bug +reports from users are the main drive behind improvements to +CodeMirror. But first, please read over these points:
+ +There are a few things in the 2.2 release that require some care +when upgrading.
+ +The default theme is now included
+in codemirror.css, so
+you do not have to included it separately anymore. (It was tiny, so
+even if you're not using it, the extra data overhead is negligible.)
+
+
CodeMirror has moved to a system +where keymaps are used to +bind behavior to keys. This means custom +bindings are now possible.
+ +Three options that influenced key
+behavior, tabMode, enterMode,
+and smartHome, are no longer supported. Instead, you can
+provide custom bindings to influence the way these keys act. This is
+done through the
+new extraKeys
+option, which can hold an object mapping key names to functionality. A
+simple example would be:
extraKeys: {
+ "Ctrl-S": function(instance) { saveText(instance.getValue()); },
+ "Ctrl-/": "undo"
+ }
+
+Keys can be mapped either to functions, which will be given the
+editor instance as argument, or to strings, which are mapped through
+functions through the CodeMirror.commands table, which
+contains all the built-in editing commands, and can be inspected and
+extended by external code.
By default, the Home key is bound to
+the "goLineStartSmart" command, which moves the cursor to
+the first non-whitespace character on the line. You can set do this to
+make it always go to the very start instead:
extraKeys: {"Home": "goLineStart"}
+
+Similarly, Enter is bound
+to "newlineAndIndent" by default. You can bind it to
+something else to get different behavior. To disable special handling
+completely and only get a newline character inserted, you can bind it
+to false:
extraKeys: {"Enter": false}
+
+The same works for Tab. If you don't want CodeMirror
+to handle it, bind it to false. The default behaviour is
+to indent the current line more ("indentMore" command),
+and indent it less when shift is held ("indentLess").
+There are also "indentAuto" (smart indent)
+and "insertTab" commands provided for alternate
+behaviors. Or you can write your own handler function to do something
+different altogether.
Handling of tabs changed completely. The display width of tabs can
+now be set with the tabSize option, and tabs can
+be styled by setting CSS rules
+for the cm-tab class.
The default width for tabs is now 4, as opposed to the 8 that is
+hard-wired into browsers. If you are relying on 8-space tabs, make
+sure you explicitly set tabSize: 8 in your options.
+
+
+
+Version 3 does not depart too much from 2.x API, and sites that use +CodeMirror in a very simple way might be able to upgrade without +trouble. But it does introduce a number of incompatibilities. Please +at least skim this text before upgrading.
+ +Note that version 3 drops full support for Internet +Explorer 7. The editor will mostly work on that browser, but +it'll be significantly glitchy.
+ +This one is the most likely to cause problems. The internal +structure of the editor has changed quite a lot, mostly to implement a +new scrolling model.
+ +Editor height is now set on the outer wrapper element (CSS
+class CodeMirror), not on the scroller element
+(CodeMirror-scroll).
Other nodes were moved, dropped, and added. If you have any code +that makes assumptions about the internal DOM structure of the editor, +you'll have to re-test it and probably update it to work with v3.
+ +See the styling section of the +manual for more information.
+In CodeMirror 2.x, there was a single gutter, and line markers
+created with setMarker would have to somehow coexist with
+the line numbers (if present). Version 3 allows you to specify an
+array of gutters, by class
+name,
+use setGutterMarker
+to add or remove markers in individual gutters, and clear whole
+gutters
+with clearGutter.
+Gutter markers are now specified as DOM nodes, rather than HTML
+snippets.
The gutters no longer horizontally scrolls along with the content.
+The fixedGutter option was removed (since it is now the
+only behavior).
+<style>
+ /* Define a gutter style */
+ .note-gutter { width: 3em; background: cyan; }
+</style>
+<script>
+ // Create an instance with two gutters -- line numbers and notes
+ var cm = new CodeMirror(document.body, {
+ gutters: ["note-gutter", "CodeMirror-linenumbers"],
+ lineNumbers: true
+ });
+ // Add a note to line 0
+ cm.setGutterMarker(0, "note-gutter", document.createTextNode("hi"));
+</script>
+
+Most of the onXYZ options have been removed. The same
+effect is now obtained by calling
+the on method with a string
+identifying the event type. Multiple handlers can now be registered
+(and individually unregistered) for an event, and objects such as line
+handlers now also expose events. See the
+full list here.
(The onKeyEvent and onDragEvent options,
+which act more as hooks than as event handlers, are still there in
+their old form.)
+cm.on("change", function(cm, change) {
+ console.log("something changed! (" + change.origin + ")");
+});
+
+The markText method
+(which has gained some interesting new features, such as creating
+atomic and read-only spans, or replacing spans with widgets) no longer
+takes the CSS class name as a separate argument, but makes it an
+optional field in the options object instead.
+// Style first ten lines, and forbid the cursor from entering them
+cm.markText({line: 0, ch: 0}, {line: 10, ch: 0}, {
+ className: "magic-text",
+ inclusiveLeft: true,
+ atomic: true
+});
+
+The interface for hiding lines has been
+removed. markText can
+now be used to do the same in a more flexible and powerful way.
The folding script has been +updated to use the new interface, and should now be more robust.
+ +
+// Fold a range, replacing it with the text "??"
+var range = cm.markText({line: 4, ch: 2}, {line: 8, ch: 1}, {
+ replacedWith: document.createTextNode("??"),
+ // Auto-unfold when cursor moves into the range
+ clearOnEnter: true
+});
+// Get notified when auto-unfolding
+CodeMirror.on(range, "clear", function() {
+ console.log("boom");
+});
+
+The setLineClass method has been replaced
+by addLineClass
+and removeLineClass,
+which allow more modular control over the classes attached to a line.
+var marked = cm.addLineClass(10, "background", "highlighted-line");
+setTimeout(function() {
+ cm.removeLineClass(marked, "background", "highlighted-line");
+});
+
+All methods that take or return objects that represent screen
+positions now use {left, top, bottom, right} properties
+(not always all of them) instead of the {x, y, yBot} used
+by some methods in v2.x.
Affected methods
+are cursorCoords, charCoords, coordsChar,
+and getScrollInfo.
The matchBrackets
+option is no longer defined in the core editor.
+Load addon/edit/matchbrackets.js to enable it.
The CodeMirror.listModes
+and CodeMirror.listMIMEs functions, used for listing
+defined modes, are gone. You are now encouraged to simply
+inspect CodeMirror.modes (mapping mode names to mode
+constructors) and CodeMirror.mimeModes (mapping MIME
+strings to mode specs).
Some more reasons to upgrade to version 3.
+ +CodeMirror.defineOption.
+
+
+
+CodeMirror 4's interface is very close version 3, but it +does fix a few awkward details in a backwards-incompatible ways. At +least skim the text below before upgrading.
+ +The main new feature in version 4 is multiple selections. The +single-selection variants of methods are still there, but now +typically act only on the primary selection (usually the last +one added).
+ +The exception to this
+is getSelection,
+which will now return the content of all selections
+(separated by newlines, or whatever lineSep parameter you passed
+it).
This event still exists, but the object it is passed has +a completely new +interface, because such changes now concern multiple +selections.
+ +By
+default, replaceSelection
+would leave the newly inserted text selected. This is only rarely what
+you want, and also (slightly) more expensive in the new model, so the
+default was changed to "end", meaning the old behavior
+must be explicitly specified by passing a second argument
+of "around".
Rather than forcing client code to follow next
+pointers from one change object to the next, the library will now
+simply fire
+multiple "change"
+events. Existing code will probably continue to work unmodified.
This option, which conceptually caused line widgets to be visible +even if their line was hidden, was never really well-defined, and was +buggy from the start. It would be a rather expensive feature, both in +code complexity and run-time performance, to implement properly. It +has been dropped entirely in 4.0.
+ +All modules in the CodeMirror distribution are now wrapped in a
+shim function to make them compatible with both AMD
+(requirejs) and CommonJS (as used
+by node
+and browserify) module loaders.
+When neither of these is present, they fall back to simply using the
+global CodeMirror variable.
If you have a module loader present in your environment, CodeMirror +will attempt to use it, and you might need to change the way you load +CodeMirror modules.
+ +Data structures produced by the library should not be mutated
+unless explicitly allowed, in general. This is slightly more strict in
+4.0 than it was in earlier versions, which copied the position objects
+returned by getCursor
+for nebulous, historic reasons. In 4.0, mutating these
+objects will corrupt your editor's selection.
A few properties and methods that have been deprecated for a while
+are now gone. Most notably, the onKeyEvent
+and onDragEvent options (use the
+corresponding events instead).
Two silly methods, which were mostly there to stay close to the 0.x
+API, setLine and removeLine are now gone.
+Use the more
+flexible replaceRange
+method instead.
The long names for folding and completing functions
+(CodeMirror.braceRangeFinder, CodeMirror.javascriptHint,
+etc) are also gone
+(use CodeMirror.fold.brace, CodeMirror.hint.javascript).
The className property in the return value
+of getTokenAt, which
+has been superseded by the type property, is also no
+longer present.
CodeMirror is a versatile text editor + implemented in JavaScript for the browser. It is specialized for + editing code, and comes with a number of language modes and addons + that implement more advanced editing functionality.
+ +A rich programming API and a + CSS theming system are + available for customizing CodeMirror to fit your application, and + extending it with new functionality.
+CodeMirror is an open-source project shared under + an MIT license. It is the editor used in the + dev tools for + both Firefox + and Chrome, Light + Table, Adobe + Brackets, Bitbucket, + and many other projects.
+ +Development and bug tracking happens + on github + (alternate git + repository). + Please read these + pointers before submitting a bug. Use pull requests to submit + patches. All contributions must be released under the same MIT + license that CodeMirror uses.
+ +Discussion around the project is done on + a discussion forum. + Announcements related to the project, such as new versions, are + posted in the + forum's "announce" + category. If needed, you can + contact the maintainer + directly. We aim to be an inclusive, welcoming community. To make + that explicit, we have + a code of + conduct that applies to communication around the project.
+ +A list of CodeMirror-related software that is not part of the + main distribution is maintained + on our + wiki. Feel free to add your project.
+The desktop versions of the following browsers,
+ in standards mode (HTML5 <!doctype html>
+ recommended) are supported:
| Firefox | version 4 and up |
|---|---|
| Chrome | any version |
| Safari | version 5.2 and up |
| Internet Explorer | version 8 and up |
| Opera | version 9 and up |
Support for modern mobile browsers is experimental. Recent + versions of the iOS browser and Chrome on Android should work + pretty well.
+Simple mode that tries to handle APL as well as it can.
+It attempts to label functions/operators based upon + monadic/dyadic usage (but this is far from fully fleshed out). + This means there are meaningful classnames so hover states can + have popups etc.
+ +MIME types defined: text/apl (APL code)
MIME types
+defined: application/pgp, application/pgp-keys, application/pgp-signature
Language: Abstract Syntax Notation One + (ASN.1) +
+MIME types defined: text/x-ttcn-asn
The development of this mode has been sponsored by Ericsson + .
+Coded by Asmelash Tsegay Gebretsadkan
+MIME types defined: text/x-asterisk.
A mode for Brainfuck
+ +MIME types defined: text/x-brainfuck
Simple mode that tries to handle C-like languages as well as it
+ can. Takes two configuration parameters: keywords, an
+ object whose property names are the keywords in the language,
+ and useCPP, which determines whether C preprocessor
+ directives are recognized.
MIME types defined: text/x-csrc
+ (C), text/x-c++src (C++), text/x-java
+ (Java), text/x-csharp (C#),
+ text/x-objectivec (Objective-C),
+ text/x-scala (Scala), text/x-vertex
+ x-shader/x-fragment (shader programs),
+ text/x-squirrel (Squirrel) and
+ text/x-ceylon (Ceylon)
MIME types defined: text/x-clojure.
MIME types defined: text/x-cmake.
Select Theme Select Font Size + + + + +
+ + +MIME types defined: text/x-coffeescript.
The CoffeeScript mode was written by Jeff Pickhardt.
+ +MIME types defined: text/x-common-lisp.
MIME types defined: text/x-crystal.
A mode for Closure Stylesheets (GSS).
+MIME type defined: text/x-gss.
MIME types defined: text/css, text/x-scss (demo), text/x-less (demo).
The LESS mode is a sub-mode of the CSS mode (defined in css.js).
The SCSS mode is a sub-mode of the CSS mode (defined in css.js).
MIME types defined:
+ application/x-cypher-query
+
Simple mode that handle D-Syntax (DLang Homepage).
+ +MIME types defined: text/x-d
+ .
MIME types defined: text/x-diff.
Mode for HTML with embedded Django template markup.
+ +MIME types defined: text/x-django
Dockerfile syntax highlighting for CodeMirror. Depends on + the simplemode addon.
+ +MIME types defined: text/x-dockerfile
MIME types defined: application/xml-dtd.
MIME types defined: text/x-dylan.
Created by Robert Plummer
+Based on CodeMirror's clike mode. For more information see HPCC Systems web site.
+MIME types defined: text/x-ecl.
MIME types defined: text/x-eiffel.
Created by YNH.
+MIME types defined: text/x-elm.
MIME types defined: text/x-erlang.
Simple mode that handles Factor Syntax (Factor on WikiPedia).
+ +MIME types defined: text/x-factor.
MIME type: text/x-fcl
Simple mode that handle Forth-Syntax (Forth on WikiPedia).
+ +MIME types defined: text/x-forth.
MIME types defined: text/x-fortran.
Handles AT&T assembler syntax (more specifically this handles
+ the GNU Assembler (gas) syntax.)
+ It takes a single optional configuration parameter:
+ architecture, which can be one of "ARM",
+ "ARMv6" or "x86".
+ Including the parameter adds syntax for the registers and special
+ directives for the supplied architecture.
+
+
MIME types defined: text/x-gas
Optionally depends on other modes for properly highlighted code blocks.
+ + + +MIME types defined: text/x-feature.
MIME type: text/x-go
MIME types defined: text/x-groovy
MIME types defined: text/x-haml.
Handlebars syntax highlighting for CodeMirror.
+ +MIME types defined: text/x-handlebars-template
Supported options: base to set the mode to
+ wrap. For example, use
mode: {name: "handlebars", base: "text/html"}
+ to highlight an HTML template.
+MIME types
+ defined: text/x-literate-haskell.
Parser configuration parameters recognized: base to
+ set the base mode (defaults to "haskell").
MIME types defined: text/x-haskell.
Hxml mode:
+ + +MIME types defined: text/x-haxe, text/x-hxml.
Mode for html embedded scripts like JSP and ASP.NET. Depends on multiplex and HtmlMixed which in turn depends on
+ JavaScript, CSS and XML.
Other dependencies include those of the scripting language chosen.
MIME types defined: application/x-aspx (ASP.NET),
+ application/x-ejs (Embedded Javascript), application/x-jsp (JavaServer Pages)
+ and application/x-erb
The HTML mixed mode depends on the XML, JavaScript, and CSS modes.
+ +It takes an optional mode configuration
+ option, tags, which can be used to add custom
+ behavior for specific tags. When given, it should be an object
+ mapping tag names (for example script) to arrays or
+ three-element arrays. Those inner arrays indicate [attributeName,
+ valueRegexp, modeSpec]
+ specifications. For example, you could use ["type", /^foo$/,
+ "foo"] to map the attribute type="foo" to
+ the foo mode. When the first two fields are null
+ ([null, null, "mode"]), the given mode is used for
+ any such tag that doesn't match any of the previously given
+ attributes. For example:
var myModeSpec = {
+ name: "htmlmixed",
+ tags: {
+ style: [["type", /^text/(x-)?scss$/, "text/x-scss"],
+ [null, null, "css"]],
+ custom: [[null, null, "customMode"]]
+ }
+}
+
+ MIME types defined: text/html
+ (redefined, only takes effect if you load this parser after the
+ XML parser).
MIME types defined: message/http.
MIME types defined: text/x-idl.
This is a list of every mode in the distribution. Each mode lives
+in a subdirectory of the mode/ directory, and typically
+defines a single JavaScript file that implements the mode. Loading
+such file will make the language available to CodeMirror, through
+the mode
+option.
+ JavaScript mode supports several configuration options: +
json which will set the mode to expect JSON
+ data rather than a JavaScript program.jsonld which will set the mode to expect
+ JSON-LD linked data rather
+ than a JavaScript program (demo).typescript which will activate additional
+ syntax highlighting and some other things for TypeScript code
+ (demo).statementIndent which (given a number) will
+ determine the amount of indentation to use for statements
+ continued on a new line.wordCharacters, a regexp that indicates which
+ characters should be considered part of an identifier.
+ Defaults to /[\w$]/, which does not handle
+ non-ASCII identifiers. Can be set to something more elaborate
+ to improve Unicode support.MIME types defined: text/javascript, application/json, application/ld+json, text/typescript, application/typescript.
This is a specialization of the JavaScript mode.
+This is a specialization of the JavaScript mode.
+JSX Mode for React's +JavaScript syntax extension.
+ +MIME types defined: text/jsx, text/typescript-jsx.
MIME types defined: text/x-julia.
MIME types defined: text/x-livescript.
The LiveScript mode was written by Kenneth Bentley.
+ +Loosely based on Franciszek
+ Wawrzak's CodeMirror
+ 1 mode. One configuration parameter is
+ supported, specials, to which you can provide an
+ array of strings to have those identifiers highlighted with
+ the lua-special style.
MIME types defined: text/x-lua.
You might want to use the Github-Flavored Markdown mode instead, which adds support for fenced code blocks and a few other things.
+ +Optionally depends on the XML mode for properly highlighted inline XML blocks.
+ +MIME types defined: text/x-markdown.
MIME types defined: text/x-mathematica (Mathematica).
MIME types defined: application/mbox.
MIME types defined: text/mirc.
MIME types defined: text/x-ocaml (OCaml) and text/x-fsharp (F#).
Simple mode that tries to handle Modelica as well as it can.
+ +MIME types defined: text/x-modelica
+ (Modlica code).
+ Simple mode for highlighting MscGen and two derived sequence + chart languages. +
+ + + +MIME types defined:
+ text/x-mscgen
+ text/x-xu
+ text/x-msgenny
+
MIME types defined: text/nginx.
MIME types defined: text/x-nsis.
MIME types defined: text/n-triples.
MIME types defined: text/x-octave.
MIME type defined: text/x-oz.
MIME types defined: text/x-pascal.
Created by Forbes Lindesay.
+MIME types defined: text/x-perl.
Simple HTML/PHP mode based on + the C-like mode. Depends on XML, + JavaScript, CSS, HTMLMixed, and C-like modes.
+ +MIME types defined: application/x-httpd-php (HTML with PHP code), text/x-php (plain, non-wrapped PHP code).
+ Simple mode that handles Pig Latin language. +
+ +MIME type defined: text/x-pig
+ (PIG code)
+
MIME types defined: application/x-powershell.
MIME types defined: text/x-properties,
+ text/x-ini.
MIME types defined: text/x-protobuf.
Created by Forbes Lindesay. Managed as part of a Brackets extension at https://github.com/ForbesLindesay/jade-brackets.
+MIME type defined: text/x-pug, text/x-jade.
MIME types defined: text/x-puppet.
Usefull for superset of python syntax like Enthought enaml, IPython magics and questionmark help
+^[\\+\\-\\*/%&|\\^~<>!]including
@on Python 3
^[\\(\\)\\[\\]\\{\\}@,:`=;\\.]^((==)|(!=)|(<=)|(>=)|(<>)|(<<)|(>>)|(//)|(\\*\\*))
^((\\+=)|(\\-=)|(\\*=)|(%=)|(/=)|(&=)|(\\|=)|(\\^=))
^((//=)|(>>=)|(<<=)|(\\*\\*=))
^[_A-Za-z][_A-Za-z0-9]*on Python 2 and
^[_A-Za-z\u00A1-\uFFFF][_A-Za-z0-9\u00A1-\uFFFF]*on Python 3.
MIME types defined: text/x-python and text/x-cython.
MIME type defined: text/x-q.
MIME types defined: text/x-rsrc.
Development of the CodeMirror R mode was kindly sponsored + by Ubalo.
+ +MIME types defined: text/x-rpm-changes.
MIME types defined: text/x-rpm-spec, text/x-rpm-changes.
+ The python mode will be used for highlighting blocks
+ containing Python/IPython terminal sessions: blocks starting with
+ >>> (for Python) or In [num]: (for
+ IPython).
+
+ Further, the stex mode will be used for highlighting
+ blocks containing LaTex code.
+
MIME types defined: text/x-rst.
MIME types defined: text/x-ruby.
Development of the CodeMirror Ruby mode was kindly sponsored + by Ubalo.
+ +MIME types defined: text/x-rustsrc.
MIME types defined: text/x-sas.
MIME types defined: text/x-sass.
MIME types defined: text/x-scheme.
MIME types defined: text/x-sh.
MIME types defined: application/sieve.
MIME types defined: application/x-slim.
Simple Smalltalk mode.
+ +MIME types defined: text/x-stsrc.
Mode for Smarty version 2 or 3, which allows for custom delimiter tags.
+ +Several configuration parameters are supported:
+ +leftDelimiter and rightDelimiter,
+ which should be strings that determine where the Smarty syntax
+ starts and ends.version, which should be 2 or 3.baseMode, which can be a mode spec
+ like "text/html" to set a different background mode.MIME types defined: text/x-smarty
MIME types defined: text/x-solr.
A mode for Closure Templates (Soy).
+MIME type defined: text/x-soy.
MIME types defined: application/sparql-query.
MIME types defined: text/x-spreadsheet.
Created by Robert Plummer
+MIME types defined:
+ text/x-sql,
+ text/x-mysql,
+ text/x-mariadb,
+ text/x-cassandra,
+ text/x-plsql,
+ text/x-mssql,
+ text/x-hive,
+ text/x-pgsql,
+ text/x-gql.
+
MIME types defined: text/x-stex.
MIME types defined: text/x-styl.
Created by Dmitry Kiselyov
+A simple mode for Swift
+ +MIME types defined: text/x-swift (Swift code)
MIME types defined: text/x-tcl.
MIME types defined: text/x-textile.
TiddlyWiki mode supports a single configuration.
+ +MIME types defined: text/x-tiddlywiki.
Created by Forbes Lindesay.
+MIME type defined: text/x-toml.
Mode for HTML with embedded Tornado template markup.
+ +MIME types defined: text/x-tornado
MIME types defined: troff.
Language: Testing and Test Control Notation - + Configuration files + (TTCN-CFG) +
+MIME types defined: text/x-ttcn-cfg.
The development of this mode has been sponsored by Ericsson + .
+Coded by Asmelash Tsegay Gebretsadkan
+Language: Testing and Test Control Notation + (TTCN) +
+MIME types defined: text/x-ttcn,
+ text/x-ttcn3, text/x-ttcnpp.
The development of this mode has been sponsored by Ericsson + .
+Coded by Asmelash Tsegay Gebretsadkan
+MIME types defined: text/turtle.
MIME type defined: text/x-vb.
MIME types defined: text/vbscript.
MIME types defined: text/velocity.
+Syntax highlighting and indentation for the Verilog and SystemVerilog languages (IEEE 1800). +
MIME types defined: text/x-verilog and text/x-systemverilog.
+Syntax highlighting and indentation for the VHDL language. +
MIME types defined: text/x-vhdl.
MIME types defined: text/x-vue
MIME type defined: text/x-webidl.
The XML mode supports these configuration parameters:
+htmlMode (boolean)br) do not require a closing tag.matchClosing (boolean)alignCDATA (boolean)MIME types defined: application/xml, text/html.
MIME types defined: application/xquery.
Development of the CodeMirror XQuery mode was sponsored by + MarkLogic and developed by + Mike Brevoort. +
+ +][variable hello] [variable world][tag
]"); + + MT("test namespaced variable", + "[keyword declare] [keyword namespace] [variable e] [keyword =] [string \"http://example.com/ANamespace\"][variable ;declare] [keyword variable] [variable $e:exampleComThisVarIsNotRecognized] [keyword as] [keyword element]([keyword *]) [variable external;]"); + + MT("test EQName variable", + "[keyword declare] [keyword variable] [variable $\"http://www.example.com/ns/my\":var] [keyword :=] [atom 12][variable ;]", + "[tagMIME types defined: text/x-yacas (yacas).
Defines a mode that parses
+a YAML frontmatter
+at the start of a file, switching to a base mode at the end of that.
+Takes a mode configuration option base to configure the
+base mode, which defaults to "gfm".
MIME types defined: text/x-yaml.
MIME types defined: text/x-z80, text/x-ez80.
A limited set of programmatic sanity tests for CodeMirror.
+ +Please enable JavaScript...
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +' + esc(text) + ''; + s += '
| ' + + '' + + esc(val.replace(/ /g,'\xb7')) + // · MIDDLE DOT + '' + + ' | '; + } + s += '
| ' + (output[i].style || null) + ' | '; + } + if(output[0].state) { + s += '
' + esc(output[i].state) + ' | ';
+ }
+ }
+ s += '
", "يتم السحب في 05 فبراير 2014"], function(line) {
+ cm.setValue(line + "\n"); cm.execCommand("goLineStart");
+ var cursors = byClassName(cm.getWrapperElement(), "CodeMirror-cursors")[0];
+ var cursor = cursors.firstChild;
+ var prevX = cursor.offsetLeft, prevY = cursor.offsetTop;
+ for (var i = 0; i <= line.length; ++i) {
+ cm.execCommand("goCharRight");
+ cursor = cursors.firstChild;
+ if (i == line.length) is(cursor.offsetTop > prevY, "next line");
+ else is(cursor.offsetLeft > prevX, "moved right");
+ prevX = cursor.offsetLeft; prevY = cursor.offsetTop;
+ }
+ cm.setCursor(0, 0); cm.execCommand("goLineEnd");
+ prevX = cursors.firstChild.offsetLeft;
+ for (var i = 0; i < line.length; ++i) {
+ cm.execCommand("goCharLeft");
+ cursor = cursors.firstChild;
+ is(cursor.offsetLeft < prevX, "moved left");
+ prevX = cursor.offsetLeft;
+ }
+ });
+}, null, ie_lt9);
+
+// Verify that updating a line clears its bidi ordering
+testCM("bidiUpdate", function(cm) {
+ cm.setCursor(Pos(0, 2, "before"));
+ cm.replaceSelection("خحج", "start");
+ cm.execCommand("goCharRight");
+ eqCursorPos(cm.getCursor(), Pos(0, 6, "before"));
+}, {value: "abcd\n"});
+
+testCM("movebyTextUnit", function(cm) {
+ cm.setValue("בְּרֵאשִ\nééé́\n");
+ cm.execCommand("goLineStart");
+ for (var i = 0; i < 4; ++i) cm.execCommand("goCharRight");
+ eqCursorPos(cm.getCursor(), Pos(0, 0, "after"));
+ cm.execCommand("goCharRight");
+ eqCursorPos(cm.getCursor(), Pos(1, 0, "after"));
+ cm.execCommand("goCharRight");
+ cm.execCommand("goCharRight");
+ eqCursorPos(cm.getCursor(), Pos(1, 4, "before"));
+ cm.execCommand("goCharRight");
+ eqCursorPos(cm.getCursor(), Pos(1, 7, "before"));
+});
+
+testCM("lineChangeEvents", function(cm) {
+ addDoc(cm, 3, 5);
+ var log = [], want = ["ch 0", "ch 1", "del 2", "ch 0", "ch 0", "del 1", "del 3", "del 4"];
+ for (var i = 0; i < 5; ++i) {
+ CodeMirror.on(cm.getLineHandle(i), "delete", function(i) {
+ return function() {log.push("del " + i);};
+ }(i));
+ CodeMirror.on(cm.getLineHandle(i), "change", function(i) {
+ return function() {log.push("ch " + i);};
+ }(i));
+ }
+ cm.replaceRange("x", Pos(0, 1));
+ cm.replaceRange("xy", Pos(1, 1), Pos(2));
+ cm.replaceRange("foo\nbar", Pos(0, 1));
+ cm.replaceRange("", Pos(0, 0), Pos(cm.lineCount()));
+ eq(log.length, want.length, "same length");
+ for (var i = 0; i < log.length; ++i)
+ eq(log[i], want[i]);
+});
+
+testCM("scrollEntirelyToRight", function(cm) {
+ if (phantom || cm.getOption("inputStyle") != "textarea") return;
+ addDoc(cm, 500, 2);
+ cm.setCursor(Pos(0, 500));
+ var wrap = cm.getWrapperElement(), cur = byClassName(wrap, "CodeMirror-cursor")[0];
+ is(wrap.getBoundingClientRect().right > cur.getBoundingClientRect().left);
+});
+
+testCM("lineWidgets", function(cm) {
+ addDoc(cm, 500, 3);
+ var last = cm.charCoords(Pos(2, 0));
+ var node = document.createElement("div");
+ node.innerHTML = "hi";
+ var widget = cm.addLineWidget(1, node);
+ is(last.top < cm.charCoords(Pos(2, 0)).top, "took up space");
+ cm.setCursor(Pos(1, 1));
+ cm.execCommand("goLineDown");
+ eqCharPos(cm.getCursor(), Pos(2, 1));
+ cm.execCommand("goLineUp");
+ eqCharPos(cm.getCursor(), Pos(1, 1));
+});
+
+testCM("lineWidgetFocus", function(cm) {
+ var place = document.getElementById("testground");
+ place.className = "offscreen";
+ try {
+ addDoc(cm, 500, 10);
+ var node = document.createElement("input");
+ var widget = cm.addLineWidget(1, node);
+ node.focus();
+ eq(document.activeElement, node);
+ cm.replaceRange("new stuff", Pos(1, 0));
+ eq(document.activeElement, node);
+ } finally {
+ place.className = "";
+ }
+});
+
+testCM("lineWidgetCautiousRedraw", function(cm) {
+ var node = document.createElement("div");
+ node.innerHTML = "hahah";
+ var w = cm.addLineWidget(0, node);
+ var redrawn = false;
+ w.on("redraw", function() { redrawn = true; });
+ cm.replaceSelection("0");
+ is(!redrawn);
+}, {value: "123\n456"});
+
+
+var knownScrollbarWidth;
+function scrollbarWidth(measure) {
+ if (knownScrollbarWidth != null) return knownScrollbarWidth;
+ var div = document.createElement('div');
+ div.style.cssText = "width: 50px; height: 50px; overflow-x: scroll";
+ document.body.appendChild(div);
+ knownScrollbarWidth = div.offsetHeight - div.clientHeight;
+ document.body.removeChild(div);
+ return knownScrollbarWidth || 0;
+}
+
+testCM("lineWidgetChanged", function(cm) {
+ addDoc(cm, 2, 300);
+ var halfScrollbarWidth = scrollbarWidth(cm.display.measure)/2;
+ cm.setOption('lineNumbers', true);
+ cm.setSize(600, cm.defaultTextHeight() * 50);
+ cm.scrollTo(null, cm.heightAtLine(125, "local"));
+
+ var expectedWidgetHeight = 60;
+ var expectedLinesInWidget = 3;
+ function w() {
+ var node = document.createElement("div");
+ // we use these children with just under half width of the line to check measurements are made with correct width
+ // when placed in the measure div.
+ // If the widget is measured at a width much narrower than it is displayed at, the underHalf children will span two lines and break the test.
+ // If the widget is measured at a width much wider than it is displayed at, the overHalf children will combine and break the test.
+ // Note that this test only checks widgets where coverGutter is true, because these require extra styling to get the width right.
+ // It may also be worthwhile to check this for non-coverGutter widgets.
+ // Visually:
+ // Good:
+ // | ------------- display width ------------- |
+ // | ------- widget-width when measured ------ |
+ // | | -- under-half -- | | -- under-half -- | |
+ // | | --- over-half --- | |
+ // | | --- over-half --- | |
+ // Height: measured as 3 lines, same as it will be when actually displayed
+
+ // Bad (too narrow):
+ // | ------------- display width ------------- |
+ // | ------ widget-width when measured ----- | < -- uh oh
+ // | | -- under-half -- | |
+ // | | -- under-half -- | | < -- when measured, shoved to next line
+ // | | --- over-half --- | |
+ // | | --- over-half --- | |
+ // Height: measured as 4 lines, more than expected . Will be displayed as 3 lines!
+
+ // Bad (too wide):
+ // | ------------- display width ------------- |
+ // | -------- widget-width when measured ------- | < -- uh oh
+ // | | -- under-half -- | | -- under-half -- | |
+ // | | --- over-half --- | | --- over-half --- | | < -- when measured, combined on one line
+ // Height: measured as 2 lines, less than expected. Will be displayed as 3 lines!
+
+ var barelyUnderHalfWidthHtml = '';
+ var barelyOverHalfWidthHtml = '';
+ node.innerHTML = new Array(3).join(barelyUnderHalfWidthHtml) + new Array(3).join(barelyOverHalfWidthHtml);
+ node.style.cssText = "background: yellow;font-size:0;line-height: " + (expectedWidgetHeight/expectedLinesInWidget) + "px;";
+ return node;
+ }
+ var info0 = cm.getScrollInfo();
+ var w0 = cm.addLineWidget(0, w(), { coverGutter: true });
+ var w150 = cm.addLineWidget(150, w(), { coverGutter: true });
+ var w300 = cm.addLineWidget(300, w(), { coverGutter: true });
+ var info1 = cm.getScrollInfo();
+ eq(info0.height + (3 * expectedWidgetHeight), info1.height);
+ eq(info0.top + expectedWidgetHeight, info1.top);
+ expectedWidgetHeight = 12;
+ w0.node.style.lineHeight = w150.node.style.lineHeight = w300.node.style.lineHeight = (expectedWidgetHeight/expectedLinesInWidget) + "px";
+ w0.changed(); w150.changed(); w300.changed();
+ var info2 = cm.getScrollInfo();
+ eq(info0.height + (3 * expectedWidgetHeight), info2.height);
+ eq(info0.top + expectedWidgetHeight, info2.top);
+});
+
+testCM("getLineNumber", function(cm) {
+ addDoc(cm, 2, 20);
+ var h1 = cm.getLineHandle(1);
+ eq(cm.getLineNumber(h1), 1);
+ cm.replaceRange("hi\nbye\n", Pos(0, 0));
+ eq(cm.getLineNumber(h1), 3);
+ cm.setValue("");
+ eq(cm.getLineNumber(h1), null);
+});
+
+testCM("jumpTheGap", function(cm) {
+ if (phantom) return;
+ var longLine = "abcdef ghiklmnop qrstuvw xyz ";
+ longLine += longLine; longLine += longLine; longLine += longLine;
+ cm.replaceRange(longLine, Pos(2, 0), Pos(2));
+ cm.setSize("200px", null);
+ cm.getWrapperElement().style.lineHeight = 2;
+ cm.refresh();
+ cm.setCursor(Pos(0, 1));
+ cm.execCommand("goLineDown");
+ eqCharPos(cm.getCursor(), Pos(1, 1));
+ cm.execCommand("goLineDown");
+ eqCharPos(cm.getCursor(), Pos(2, 1));
+ cm.execCommand("goLineDown");
+ eq(cm.getCursor().line, 2);
+ is(cm.getCursor().ch > 1);
+ cm.execCommand("goLineUp");
+ eqCharPos(cm.getCursor(), Pos(2, 1));
+ cm.execCommand("goLineUp");
+ eqCharPos(cm.getCursor(), Pos(1, 1));
+ var node = document.createElement("div");
+ node.innerHTML = "hi"; node.style.height = "30px";
+ cm.addLineWidget(0, node);
+ cm.addLineWidget(1, node.cloneNode(true), {above: true});
+ cm.setCursor(Pos(0, 2));
+ cm.execCommand("goLineDown");
+ eqCharPos(cm.getCursor(), Pos(1, 2));
+ cm.execCommand("goLineUp");
+ eqCharPos(cm.getCursor(), Pos(0, 2));
+}, {lineWrapping: true, value: "abc\ndef\nghi\njkl\n"});
+
+testCM("addLineClass", function(cm) {
+ function cls(line, text, bg, wrap, gutter) {
+ var i = cm.lineInfo(line);
+ eq(i.textClass, text);
+ eq(i.bgClass, bg);
+ eq(i.wrapClass, wrap);
+ if (typeof i.handle.gutterClass !== 'undefined') {
+ eq(i.handle.gutterClass, gutter);
+ }
+ }
+ cm.addLineClass(0, "text", "foo");
+ cm.addLineClass(0, "text", "bar");
+ cm.addLineClass(1, "background", "baz");
+ cm.addLineClass(1, "wrap", "foo");
+ cm.addLineClass(1, "gutter", "gutter-class");
+ cls(0, "foo bar", null, null, null);
+ cls(1, null, "baz", "foo", "gutter-class");
+ var lines = cm.display.lineDiv;
+ eq(byClassName(lines, "foo").length, 2);
+ eq(byClassName(lines, "bar").length, 1);
+ eq(byClassName(lines, "baz").length, 1);
+ eq(byClassName(lines, "gutter-class").length, 2); // Gutter classes are reflected in 2 nodes
+ cm.removeLineClass(0, "text", "foo");
+ cls(0, "bar", null, null, null);
+ cm.removeLineClass(0, "text", "foo");
+ cls(0, "bar", null, null, null);
+ cm.removeLineClass(0, "text", "bar");
+ cls(0, null, null, null);
+
+ cm.addLineClass(1, "wrap", "quux");
+ cls(1, null, "baz", "foo quux", "gutter-class");
+ cm.removeLineClass(1, "wrap");
+ cls(1, null, "baz", null, "gutter-class");
+ cm.removeLineClass(1, "gutter", "gutter-class");
+ eq(byClassName(lines, "gutter-class").length, 0);
+ cls(1, null, "baz", null, null);
+
+ cm.addLineClass(1, "gutter", "gutter-class");
+ cls(1, null, "baz", null, "gutter-class");
+ cm.removeLineClass(1, "gutter", "gutter-class");
+ cls(1, null, "baz", null, null);
+
+}, {value: "hohoho\n", lineNumbers: true});
+
+testCM("atomicMarker", function(cm) {
+ addDoc(cm, 10, 10);
+ function atom(ll, cl, lr, cr, li, ri) {
+ return cm.markText(Pos(ll, cl), Pos(lr, cr),
+ {atomic: true, inclusiveLeft: li, inclusiveRight: ri});
+ }
+ var m = atom(0, 1, 0, 5);
+ cm.setCursor(Pos(0, 1));
+ cm.execCommand("goCharRight");
+ eqCursorPos(cm.getCursor(), Pos(0, 5));
+ cm.execCommand("goCharLeft");
+ eqCursorPos(cm.getCursor(), Pos(0, 1));
+ m.clear();
+ m = atom(0, 0, 0, 5, true);
+ eqCursorPos(cm.getCursor(), Pos(0, 5), "pushed out");
+ cm.execCommand("goCharLeft");
+ eqCursorPos(cm.getCursor(), Pos(0, 5));
+ m.clear();
+ m = atom(8, 4, 9, 10, false, true);
+ cm.setCursor(Pos(9, 8));
+ eqCursorPos(cm.getCursor(), Pos(8, 4), "set");
+ cm.execCommand("goCharRight");
+ eqCursorPos(cm.getCursor(), Pos(8, 4), "char right");
+ cm.execCommand("goLineDown");
+ eqCursorPos(cm.getCursor(), Pos(8, 4), "line down");
+ cm.execCommand("goCharLeft");
+ eqCursorPos(cm.getCursor(), Pos(8, 3, "after"));
+ m.clear();
+ m = atom(1, 1, 3, 8);
+ cm.setCursor(Pos(0, 0));
+ cm.setCursor(Pos(2, 0));
+ eqCursorPos(cm.getCursor(), Pos(3, 8));
+ cm.execCommand("goCharLeft");
+ eqCursorPos(cm.getCursor(), Pos(1, 1));
+ cm.execCommand("goCharRight");
+ eqCursorPos(cm.getCursor(), Pos(3, 8));
+ cm.execCommand("goLineUp");
+ eqCursorPos(cm.getCursor(), Pos(1, 1));
+ cm.execCommand("goLineDown");
+ eqCursorPos(cm.getCursor(), Pos(3, 8));
+ cm.execCommand("delCharBefore");
+ eq(cm.getValue().length, 80, "del chunk");
+ m = atom(3, 0, 5, 5);
+ cm.setCursor(Pos(3, 0));
+ cm.execCommand("delWordAfter");
+ eq(cm.getValue().length, 53, "del chunk");
+});
+
+testCM("selectionBias", function(cm) {
+ cm.markText(Pos(0, 1), Pos(0, 3), {atomic: true});
+ cm.setCursor(Pos(0, 2));
+ eqCursorPos(cm.getCursor(), Pos(0, 1));
+ cm.setCursor(Pos(0, 2));
+ eqCursorPos(cm.getCursor(), Pos(0, 3));
+ cm.setCursor(Pos(0, 2));
+ eqCursorPos(cm.getCursor(), Pos(0, 1));
+ cm.setCursor(Pos(0, 2), null, {bias: -1});
+ eqCursorPos(cm.getCursor(), Pos(0, 1));
+ cm.setCursor(Pos(0, 4));
+ cm.setCursor(Pos(0, 2), null, {bias: 1});
+ eqCursorPos(cm.getCursor(), Pos(0, 3));
+}, {value: "12345"});
+
+testCM("selectionHomeEnd", function(cm) {
+ cm.markText(Pos(1, 0), Pos(1, 1), {atomic: true, inclusiveLeft: true});
+ cm.markText(Pos(1, 3), Pos(1, 4), {atomic: true, inclusiveRight: true});
+ cm.setCursor(Pos(1, 2));
+ cm.execCommand("goLineStart");
+ eqCursorPos(cm.getCursor(), Pos(1, 1));
+ cm.execCommand("goLineEnd");
+ eqCursorPos(cm.getCursor(), Pos(1, 3));
+}, {value: "ab\ncdef\ngh"});
+
+testCM("readOnlyMarker", function(cm) {
+ function mark(ll, cl, lr, cr, at) {
+ return cm.markText(Pos(ll, cl), Pos(lr, cr),
+ {readOnly: true, atomic: at});
+ }
+ var m = mark(0, 1, 0, 4);
+ cm.setCursor(Pos(0, 2));
+ cm.replaceSelection("hi", "end");
+ eqCursorPos(cm.getCursor(), Pos(0, 2));
+ eq(cm.getLine(0), "abcde");
+ cm.execCommand("selectAll");
+ cm.replaceSelection("oops", "around");
+ eq(cm.getValue(), "oopsbcd");
+ cm.undo();
+ eqCursorPos(m.find().from, Pos(0, 1));
+ eqCursorPos(m.find().to, Pos(0, 4));
+ m.clear();
+ cm.setCursor(Pos(0, 2));
+ cm.replaceSelection("hi", "around");
+ eq(cm.getLine(0), "abhicde");
+ eqCursorPos(cm.getCursor(), Pos(0, 4));
+ m = mark(0, 2, 2, 2, true);
+ cm.setSelection(Pos(1, 1), Pos(2, 4));
+ cm.replaceSelection("t", "end");
+ eqCursorPos(cm.getCursor(), Pos(2, 3));
+ eq(cm.getLine(2), "klto");
+ cm.execCommand("goCharLeft");
+ cm.execCommand("goCharLeft");
+ eqCursorPos(cm.getCursor(), Pos(0, 2));
+ cm.setSelection(Pos(0, 1), Pos(0, 3));
+ cm.replaceSelection("xx", "around");
+ eqCursorPos(cm.getCursor(), Pos(0, 3));
+ eq(cm.getLine(0), "axxhicde");
+}, {value: "abcde\nfghij\nklmno\n"});
+
+testCM("dirtyBit", function(cm) {
+ eq(cm.isClean(), true);
+ cm.replaceSelection("boo", null, "test");
+ eq(cm.isClean(), false);
+ cm.undo();
+ eq(cm.isClean(), true);
+ cm.replaceSelection("boo", null, "test");
+ cm.replaceSelection("baz", null, "test");
+ cm.undo();
+ eq(cm.isClean(), false);
+ cm.markClean();
+ eq(cm.isClean(), true);
+ cm.undo();
+ eq(cm.isClean(), false);
+ cm.redo();
+ eq(cm.isClean(), true);
+});
+
+testCM("changeGeneration", function(cm) {
+ cm.replaceSelection("x");
+ var softGen = cm.changeGeneration();
+ cm.replaceSelection("x");
+ cm.undo();
+ eq(cm.getValue(), "");
+ is(!cm.isClean(softGen));
+ cm.replaceSelection("x");
+ var hardGen = cm.changeGeneration(true);
+ cm.replaceSelection("x");
+ cm.undo();
+ eq(cm.getValue(), "x");
+ is(cm.isClean(hardGen));
+});
+
+testCM("addKeyMap", function(cm) {
+ function sendKey(code) {
+ cm.triggerOnKeyDown({type: "keydown", keyCode: code,
+ preventDefault: function(){}, stopPropagation: function(){}});
+ }
+
+ sendKey(39);
+ eqCursorPos(cm.getCursor(), Pos(0, 1, "before"));
+ var test = 0;
+ var map1 = {Right: function() { ++test; }}, map2 = {Right: function() { test += 10; }}
+ cm.addKeyMap(map1);
+ sendKey(39);
+ eqCursorPos(cm.getCursor(), Pos(0, 1, "before"));
+ eq(test, 1);
+ cm.addKeyMap(map2, true);
+ sendKey(39);
+ eq(test, 2);
+ cm.removeKeyMap(map1);
+ sendKey(39);
+ eq(test, 12);
+ cm.removeKeyMap(map2);
+ sendKey(39);
+ eq(test, 12);
+ eqCursorPos(cm.getCursor(), Pos(0, 2, "before"));
+ cm.addKeyMap({Right: function() { test = 55; }, name: "mymap"});
+ sendKey(39);
+ eq(test, 55);
+ cm.removeKeyMap("mymap");
+ sendKey(39);
+ eqCursorPos(cm.getCursor(), Pos(0, 3, "before"));
+}, {value: "abc"});
+
+testCM("findPosH", function(cm) {
+ forEach([{from: Pos(0, 0), to: Pos(0, 1, "before"), by: 1},
+ {from: Pos(0, 0), to: Pos(0, 0), by: -1, hitSide: true},
+ {from: Pos(0, 0), to: Pos(0, 4, "before"), by: 1, unit: "word"},
+ {from: Pos(0, 0), to: Pos(0, 8, "before"), by: 2, unit: "word"},
+ {from: Pos(0, 0), to: Pos(2, 0, "after"), by: 20, unit: "word", hitSide: true},
+ {from: Pos(0, 7), to: Pos(0, 5, "after"), by: -1, unit: "word"},
+ {from: Pos(0, 4), to: Pos(0, 8, "before"), by: 1, unit: "word"},
+ {from: Pos(1, 0), to: Pos(1, 18, "before"), by: 3, unit: "word"},
+ {from: Pos(1, 22), to: Pos(1, 5, "after"), by: -3, unit: "word"},
+ {from: Pos(1, 15), to: Pos(1, 10, "after"), by: -5},
+ {from: Pos(1, 15), to: Pos(1, 10, "after"), by: -5, unit: "column"},
+ {from: Pos(1, 15), to: Pos(1, 0, "after"), by: -50, unit: "column", hitSide: true},
+ {from: Pos(1, 15), to: Pos(1, 24, "before"), by: 50, unit: "column", hitSide: true},
+ {from: Pos(1, 15), to: Pos(2, 0, "after"), by: 50, hitSide: true}], function(t) {
+ var r = cm.findPosH(t.from, t.by, t.unit || "char");
+ eqCursorPos(r, t.to);
+ eq(!!r.hitSide, !!t.hitSide);
+ });
+}, {value: "line one\nline two.something.other\n"});
+
+testCM("beforeChange", function(cm) {
+ cm.on("beforeChange", function(cm, change) {
+ var text = [];
+ for (var i = 0; i < change.text.length; ++i)
+ text.push(change.text[i].replace(/\s/g, "_"));
+ change.update(null, null, text);
+ });
+ cm.setValue("hello, i am a\nnew document\n");
+ eq(cm.getValue(), "hello,_i_am_a\nnew_document\n");
+ CodeMirror.on(cm.getDoc(), "beforeChange", function(doc, change) {
+ if (change.from.line == 0) change.cancel();
+ });
+ cm.setValue("oops"); // Canceled
+ eq(cm.getValue(), "hello,_i_am_a\nnew_document\n");
+ cm.replaceRange("hey hey hey", Pos(1, 0), Pos(2, 0));
+ eq(cm.getValue(), "hello,_i_am_a\nhey_hey_hey");
+}, {value: "abcdefghijk"});
+
+testCM("beforeChangeUndo", function(cm) {
+ cm.replaceRange("hi", Pos(0, 0), Pos(0));
+ cm.replaceRange("bye", Pos(0, 0), Pos(0));
+ eq(cm.historySize().undo, 2);
+ cm.on("beforeChange", function(cm, change) {
+ is(!change.update);
+ change.cancel();
+ });
+ cm.undo();
+ eq(cm.historySize().undo, 0);
+ eq(cm.getValue(), "bye\ntwo");
+}, {value: "one\ntwo"});
+
+testCM("beforeSelectionChange", function(cm) {
+ function notAtEnd(cm, pos) {
+ var len = cm.getLine(pos.line).length;
+ if (!len || pos.ch == len) return Pos(pos.line, pos.ch - 1);
+ return pos;
+ }
+ cm.on("beforeSelectionChange", function(cm, obj) {
+ obj.update([{anchor: notAtEnd(cm, obj.ranges[0].anchor),
+ head: notAtEnd(cm, obj.ranges[0].head)}]);
+ });
+
+ addDoc(cm, 10, 10);
+ cm.execCommand("goLineEnd");
+ eqCursorPos(cm.getCursor(), Pos(0, 9));
+ cm.execCommand("selectAll");
+ eqCursorPos(cm.getCursor("start"), Pos(0, 0));
+ eqCursorPos(cm.getCursor("end"), Pos(9, 9));
+});
+
+testCM("change_removedText", function(cm) {
+ cm.setValue("abc\ndef");
+
+ var removedText = [];
+ cm.on("change", function(cm, change) {
+ removedText.push(change.removed);
+ });
+
+ cm.operation(function() {
+ cm.replaceRange("xyz", Pos(0, 0), Pos(1,1));
+ cm.replaceRange("123", Pos(0,0));
+ });
+
+ eq(removedText.length, 2);
+ eq(removedText[0].join("\n"), "abc\nd");
+ eq(removedText[1].join("\n"), "");
+
+ var removedText = [];
+ cm.undo();
+ eq(removedText.length, 2);
+ eq(removedText[0].join("\n"), "123");
+ eq(removedText[1].join("\n"), "xyz");
+
+ var removedText = [];
+ cm.redo();
+ eq(removedText.length, 2);
+ eq(removedText[0].join("\n"), "abc\nd");
+ eq(removedText[1].join("\n"), "");
+});
+
+testCM("lineStyleFromMode", function(cm) {
+ CodeMirror.defineMode("test_mode", function() {
+ return {token: function(stream) {
+ if (stream.match(/^\[[^\]]*\]/)) return " line-brackets ";
+ if (stream.match(/^\([^\)]*\)/)) return " line-background-parens ";
+ if (stream.match(/^<[^>]*>/)) return " span line-line line-background-bg ";
+ stream.match(/^\s+|^\S+/);
+ }};
+ });
+ cm.setOption("mode", "test_mode");
+ var bracketElts = byClassName(cm.getWrapperElement(), "brackets");
+ eq(bracketElts.length, 1, "brackets count");
+ eq(bracketElts[0].nodeName, "PRE");
+ is(!/brackets.*brackets/.test(bracketElts[0].className));
+ var parenElts = byClassName(cm.getWrapperElement(), "parens");
+ eq(parenElts.length, 1, "parens count");
+ eq(parenElts[0].nodeName, "DIV");
+ is(!/parens.*parens/.test(parenElts[0].className));
+ eq(parenElts[0].parentElement.nodeName, "DIV");
+
+ is(byClassName(cm.getWrapperElement(), "bg").length > 0);
+ is(byClassName(cm.getWrapperElement(), "line").length > 0);
+ var spanElts = byClassName(cm.getWrapperElement(), "cm-span");
+ eq(spanElts.length, 2);
+ is(/^\s*cm-span\s*$/.test(spanElts[0].className));
+}, {value: "line1: [br] [br]\nline2: (par) (par)\nline3: