Commit 82a0168e authored by Ayush's avatar Ayush

Optimize imports and code

parent b3aed7ac
import { TestBed } from '@angular/core/testing';
import {TestBed} from '@angular/core/testing';
import { ApiService } from './api.service';
import {ApiService} from './api.service';
describe('ApiService', () => {
let service: ApiService;
......
import { NgModule } from '@angular/core';
import { Routes, RouterModule } from '@angular/router';
import { HomeComponent } from './home/home.component';
import { ArenaComponent } from './arena/arena.component';
import { UserComponent } from './user/user.component';
import { IdeComponent } from './ide/ide.component';
import { LoginComponent } from './login/login.component';
import { RegisterComponent } from './register/register.component';
import { AuthGuard } from './auth.guard';
import { FileComponent } from './file/file.component';
import {NgModule} from '@angular/core';
import {RouterModule, Routes} from '@angular/router';
import {HomeComponent} from './home/home.component';
import {ArenaComponent} from './arena/arena.component';
import {UserComponent} from './user/user.component';
import {IdeComponent} from './ide/ide.component';
import {LoginComponent} from './login/login.component';
import {RegisterComponent} from './register/register.component';
import {AuthGuard} from './auth.guard';
import {FileComponent} from './file/file.component';
const routes: Routes = [
{ path: 'home', component: HomeComponent, canActivate: [AuthGuard] },
{ path: 'arena/problem/:id', component: ArenaComponent, canActivate: [AuthGuard] },
{ path: 'arena/file/:id', component: IdeComponent, canActivate: [AuthGuard] },
{ path: 'arena/file/new', component: IdeComponent, canActivate: [AuthGuard] },
{ path: 'user', component: UserComponent, canActivate: [AuthGuard] },
{ path: 'files', component: FileComponent, canActivate: [AuthGuard] },
{ path: 'login', component: LoginComponent },
{ path: 'register', component: RegisterComponent },
{ path: '**', redirectTo: '/home' },
{path: 'home', component: HomeComponent, canActivate: [AuthGuard]},
{path: 'arena/problem/:id', component: ArenaComponent, canActivate: [AuthGuard]},
{path: 'arena/file/:id', component: IdeComponent, canActivate: [AuthGuard]},
{path: 'arena/file/new', component: IdeComponent, canActivate: [AuthGuard]},
{path: 'user', component: UserComponent, canActivate: [AuthGuard]},
{path: 'files', component: FileComponent, canActivate: [AuthGuard]},
{path: 'login', component: LoginComponent},
{path: 'register', component: RegisterComponent},
{path: '**', redirectTo: '/home'},
];
@NgModule({
imports: [RouterModule.forRoot(routes)],
exports: [RouterModule]
})
export class AppRoutingModule { }
export class AppRoutingModule {
}
export var routerComponents = [
HomeComponent,
......
import { TestBed } from '@angular/core/testing';
import { RouterTestingModule } from '@angular/router/testing';
import { AppComponent } from './app.component';
import {TestBed} from '@angular/core/testing';
import {RouterTestingModule} from '@angular/router/testing';
import {AppComponent} from './app.component';
describe('AppComponent', () => {
beforeEach(async () => {
......
import { Component } from '@angular/core';
import {Component} from '@angular/core';
@Component({
selector: 'app-root',
......
import { BrowserModule } from '@angular/platform-browser';
import { NgModule } from '@angular/core';
import {BrowserModule} from '@angular/platform-browser';
import {CUSTOM_ELEMENTS_SCHEMA, NgModule} from '@angular/core';
import { AppRoutingModule } from './app-routing.module';
import { routerComponents } from './app-routing.module';
import { FormsModule, ReactiveFormsModule } from '@angular/forms';
import { HttpClientModule } from '@angular/common/http';
import { AppComponent } from './app.component';
import { HeaderComponent } from './header/header.component';
import { CUSTOM_ELEMENTS_SCHEMA } from '@angular/core';
import { ApiService } from './api.service';
import { IdeCompileComponent } from './ide-compile/ide-compile.component';
import { InputComponent } from './input/input.component';
import { TestcaseStatusComponent } from './testcase-status/testcase-status.component';
import { SaveFileComponent } from './save-file/save-file.component';
import { FileComponent } from './file/file.component';
import { FileService } from './file.service';
import {AppRoutingModule, routerComponents} from './app-routing.module';
import {FormsModule, ReactiveFormsModule} from '@angular/forms';
import {HttpClientModule} from '@angular/common/http';
import {AppComponent} from './app.component';
import {HeaderComponent} from './header/header.component';
import {ApiService} from './api.service';
import {IdeCompileComponent} from './ide-compile/ide-compile.component';
import {InputComponent} from './input/input.component';
import {TestcaseStatusComponent} from './testcase-status/testcase-status.component';
import {SaveFileComponent} from './save-file/save-file.component';
import {FileComponent} from './file/file.component';
import {FileService} from './file.service';
@NgModule({
declarations: [
......@@ -38,4 +36,5 @@ import { FileService } from './file.service';
bootstrap: [AppComponent],
schemas: [CUSTOM_ELEMENTS_SCHEMA]
})
export class AppModule { }
export class AppModule {
}
<div class="container">
<div class="card" id="ps" [innerHTML]="problem.problem_statement">
<div [innerHTML]="problem.problem_statement" class="card" id="ps">
</div>
<div id="toggle-lang">C++</div>
<div id="attempt">
......@@ -8,5 +8,6 @@
<button id="submit">Submit Code</button>
</div>
<label for="editor"></label>
<textarea name="editor" id="editor"></textarea>
<textarea id="editor" name="editor"></textarea>
</div>
<app-input></app-input>
import { ComponentFixture, TestBed } from '@angular/core/testing';
import {ComponentFixture, TestBed} from '@angular/core/testing';
import { ArenaComponent } from './arena.component';
import {ArenaComponent} from './arena.component';
describe('ArenaComponent', () => {
let component: ArenaComponent;
......@@ -8,7 +8,7 @@ describe('ArenaComponent', () => {
beforeEach(async () => {
await TestBed.configureTestingModule({
declarations: [ ArenaComponent ]
declarations: [ArenaComponent]
})
.compileComponents();
});
......
......@@ -2,6 +2,8 @@ import {Component, OnInit} from '@angular/core';
import {Router} from '@angular/router';
import {Problem} from '../problem';
import {ProblemService} from '../problem.service';
import {File} from '../file';
import {ApiService} from '../api.service';
declare const CodeMirror: any;
......@@ -14,8 +16,9 @@ export class ArenaComponent implements OnInit {
id: number;
problem: Problem;
files: File[] = [];
constructor(public router: Router, private problemService: ProblemService) {
constructor(public router: Router, private problemService: ProblemService, private apiService: ApiService) {
}
ngOnInit(): void {
......@@ -34,11 +37,12 @@ export class ArenaComponent implements OnInit {
Object.assign((document.getElementsByClassName('CodeMirror')[0] as HTMLTextAreaElement).style, {
borderBottom: '1px solid #ddf',
padding: '20px',
fontFamily: '"Space Mono", monospace',
fontFamily: '"Anonymous Pro", monospace',
});
let activeLang = 0;
const langs = ['C++', 'Python', 'Java'];
const extensions = ['.cpp', '.py', '.java'];
const langsMime = ['text/x-c++src', 'text/x-python', 'text/x-java'];
const problem = this.problem;
......@@ -51,6 +55,18 @@ export class ArenaComponent implements OnInit {
editor.setOption('mode', langsMime[activeLang]);
};
const filename = this.id.toString(10);
const username = JSON.parse(this.apiService.getToken()).username;
for (const ext of extensions) {
this.files.push({
id: null,
username,
filename,
language: ext,
text: ''
});
}
}
getProblem(): void {
......
import { Injectable } from '@angular/core';
import { ActivatedRouteSnapshot, RouterStateSnapshot, UrlTree, CanActivate, Router } from '@angular/router';
import { ApiService } from './api.service';
import {Injectable} from '@angular/core';
import {ActivatedRouteSnapshot, CanActivate, Router, RouterStateSnapshot} from '@angular/router';
import {ApiService} from './api.service';
@Injectable({
providedIn: 'root'
})
export class AuthGuard implements CanActivate {
constructor(private dataService: ApiService, private router: Router) { }
constructor(private dataService: ApiService, private router: Router) {
}
canActivate(
route: ActivatedRouteSnapshot,
state: RouterStateSnapshot
......@@ -21,6 +23,6 @@ export class AuthGuard implements CanActivate {
return true;
}
this.dataService.redirectUrl = routeUrl;
this.router.navigate(['/login'], { queryParams: { returnUrl: routeUrl } });
this.router.navigate(['/login'], {queryParams: {returnUrl: routeUrl}});
}
}
import { TestBed } from '@angular/core/testing';
import {TestBed} from '@angular/core/testing';
import { FileService } from './file.service';
import {FileService} from './file.service';
describe('FileService', () => {
let service: FileService;
......
import { Injectable } from '@angular/core';
import { File } from './file';
import { Observable, of } from 'rxjs';
import { HttpClient } from '@angular/common/http';
import {Injectable} from '@angular/core';
import {File} from './file';
import {Observable, of} from 'rxjs';
import {HttpClient} from '@angular/common/http';
@Injectable({
......@@ -11,7 +11,8 @@ export class FileService {
baseApiUrl = 'https://file.io';
constructor(private http: HttpClient) { }
constructor(private http: HttpClient) {
}
getFiles(): Observable<File[]> {
......
......@@ -52,13 +52,13 @@
<div [class.open]="uploadPopupActive" id="file-upload-popup">
<p>Upload a file:</p>
<input id="file-upload-input" type="file" (change)="onChange($event)">
<div style="margin-top: 10px" *ngIf="shortLink !== ''">
<a target="_blank" href="{{shortLink}}">Link to last upload</a>
<input (change)="onChange($event)" id="file-upload-input" type="file">
<div *ngIf="shortLink !== ''" style="margin-top: 10px">
<a href="{{shortLink}}" target="_blank">Link to last upload</a>
</div>
<p *ngIf="!loading">
<button id="upload-cancel-btn" (click)="uploadPopupActive = false;">Close</button>
<button id="file-upload-btn" (click)="onUpload()">Upload</button>
<button (click)="uploadPopupActive = false;" id="upload-cancel-btn">Close</button>
<button (click)="onUpload()" id="file-upload-btn">Upload</button>
</p>
<p *ngIf="loading" style="margin-top: 20px; text-align: center">
Uploading...
......
......@@ -22,7 +22,7 @@
button {
float: right;
border:none;
border: none;
}
.title {
......
import { ComponentFixture, TestBed } from '@angular/core/testing';
import {ComponentFixture, TestBed} from '@angular/core/testing';
import { FileComponent } from './file.component';
import {FileComponent} from './file.component';
describe('FileComponent', () => {
let component: FileComponent;
......@@ -8,7 +8,7 @@ describe('FileComponent', () => {
beforeEach(async () => {
await TestBed.configureTestingModule({
declarations: [ FileComponent ]
declarations: [FileComponent]
})
.compileComponents();
});
......
import { ComponentFixture, TestBed } from '@angular/core/testing';
import {ComponentFixture, TestBed} from '@angular/core/testing';
import { HeaderComponent } from './header.component';
import {HeaderComponent} from './header.component';
describe('HeaderComponent', () => {
let component: HeaderComponent;
......@@ -8,7 +8,7 @@ describe('HeaderComponent', () => {
beforeEach(async () => {
await TestBed.configureTestingModule({
declarations: [ HeaderComponent ]
declarations: [HeaderComponent]
})
.compileComponents();
});
......
import { Component, OnInit } from '@angular/core';
import { ApiService } from '../api.service';
import {Component, OnInit} from '@angular/core';
import {ApiService} from '../api.service';
@Component({
selector: 'app-header',
......
import { ComponentFixture, TestBed } from '@angular/core/testing';
import {ComponentFixture, TestBed} from '@angular/core/testing';
import { HomeComponent } from './home.component';
import {HomeComponent} from './home.component';
describe('HomeComponent', () => {
let component: HomeComponent;
......@@ -8,7 +8,7 @@ describe('HomeComponent', () => {
beforeEach(async () => {
await TestBed.configureTestingModule({
declarations: [ HomeComponent ]
declarations: [HomeComponent]
})
.compileComponents();
});
......
import { Component, OnInit } from '@angular/core';
import { ProblemService } from '../problem.service';
import { Problem } from '../problem';
import {Component, OnInit} from '@angular/core';
import {ProblemService} from '../problem.service';
import {Problem} from '../problem';
@Component({
selector: 'app-home',
......@@ -9,9 +9,11 @@ import { Problem } from '../problem';
})
export class HomeComponent implements OnInit {
constructor(private problemService: ProblemService) { }
problems: Problem[];
constructor(private problemService: ProblemService) {
}
ngOnInit(): void {
this.getProblems();
}
......
<div id="compile-cover" [class.open]="isActive"></div>
<div [class.open]="isActive" id="compile-cover"></div>
<div id="compile-popup" [class.open]="isActive">
<p id="compile-status" [innerHTML]="statusVal"></p>
<button id="compile-done" [class.disabled]="resultVal === 0" (click)="resultVal !== 0 && setState(false)">Done</button>
<div [class.open]="isActive" id="compile-popup">
<p [innerHTML]="statusVal" id="compile-status"></p>
<button (click)="resultVal !== 0 && setState(false)" [class.disabled]="resultVal === 0" id="compile-done">Done
</button>
</div>
import { ComponentFixture, TestBed } from '@angular/core/testing';
import {ComponentFixture, TestBed} from '@angular/core/testing';
import { IdeCompileComponent } from './ide-compile.component';
import {IdeCompileComponent} from './ide-compile.component';
describe('IdeCompileComponent', () => {
let component: IdeCompileComponent;
......@@ -8,7 +8,7 @@ describe('IdeCompileComponent', () => {
beforeEach(async () => {
await TestBed.configureTestingModule({
declarations: [ IdeCompileComponent ]
declarations: [IdeCompileComponent]
})
.compileComponents();
});
......
import { Component, OnInit, Input } from '@angular/core';
import {Component, Input, OnInit} from '@angular/core';
@Component({
selector: 'app-ide-compile',
......@@ -9,15 +9,18 @@ export class IdeCompileComponent implements OnInit {
@Input() statusVal: string;
@Input() resultVal: number;
isActive = false;
constructor() { }
ngOnInit(): void { }
setState(inp: boolean): void {
this.isActive = inp;
constructor() {
}
set status(value: string) {
this.statusVal = value;
}
ngOnInit(): void {
}
setState(inp: boolean): void {
this.isActive = inp;
}
}
......@@ -3,13 +3,15 @@
<div id="attempt">
<span *ngIf="!isUpToDate"></span>
<span id="ideFileName">{{file.filename + file.language}}</span>
<button id="inputBtn" (click)="inputField.setState(true);">Input</button>
<button id="runBtn" (click)="this.runField.isActive = true; this.runCodeService.run()">Run Code</button>
<button id="saveBtn" (click)="!isSaved ? this.saveField.setState(true) : updateFile();" [class.disabled]="isUploading || isUpToDate">{{isUploading ? "Saving File..." : "Save File"}}</button>
<button (click)="inputField.setState(true);" id="inputBtn">Input</button>
<button (click)="this.runField.isActive = true; this.runCodeService.run()" id="runBtn">Run Code</button>
<button (click)="!isSaved ? this.saveField.setState(true) : updateFile();"
[class.disabled]="isUploading || isUpToDate"
id="saveBtn">{{isUploading ? "Saving File..." : "Save File"}}</button>
</div>
<label for="editor"></label>
<textarea name="editor" id="editor" value="{{inp}}"></textarea>
<textarea id="editor" name="editor" value="{{inp}}"></textarea>
</div>
<app-input (valueEmit)="updateInput($event)"></app-input>
<app-ide-compile [statusVal]="runCodeService.status" [resultVal]="runCodeService.result"></app-ide-compile>
<app-save-file [file]="file" (savedFile)="isSaved = isUpToDate = true;"></app-save-file>
<app-ide-compile [resultVal]="runCodeService.result" [statusVal]="runCodeService.status"></app-ide-compile>
<app-save-file (savedFile)="isSaved = isUpToDate = true;" [file]="file"></app-save-file>
import { ComponentFixture, TestBed } from '@angular/core/testing';
import {ComponentFixture, TestBed} from '@angular/core/testing';
import { IdeComponent } from './ide.component';
import {IdeComponent} from './ide.component';
describe('IdeComponent', () => {
let component: IdeComponent;
......@@ -8,7 +8,7 @@ describe('IdeComponent', () => {
beforeEach(async () => {
await TestBed.configureTestingModule({
declarations: [ IdeComponent ]
declarations: [IdeComponent]
})
.compileComponents();
});
......
......@@ -50,7 +50,7 @@ export class IdeComponent implements OnInit {
Object.assign((document.getElementsByClassName('CodeMirror')[0] as HTMLTextAreaElement).style, {
borderBottom: '1px solid #ddf',
padding: '20px',
fontFamily: '"Space Mono", monospace',
fontFamily: '"Anonymous Pro", monospace',
});
let activeLang = 0;
......
<div id="input-cover" [class.open]="isActive"></div>
<div [class.open]="isActive" id="input-cover"></div>
<div id="input" [class.open]="isActive">
<div [class.open]="isActive" id="input">
<label for="inp" id="title">Enter the input:</label>
<br>
<textarea name="inp" id="inp" cols="30" rows="10" spellcheck="false"></textarea>
<textarea cols="30" id="inp" name="inp" rows="10" spellcheck="false"></textarea>
<br>
<button id="done" (click)="this.setState(false);">Done</button>
<button (click)="this.setState(false);" id="done">Done</button>
</div>
......@@ -24,7 +24,7 @@
font-size: 1.1em;
background: transparent;
border: none;
font-family: 'Space Mono', monospace;
font-family: 'Anonymous Pro', monospace;
resize: none;
}
......
import { ComponentFixture, TestBed } from '@angular/core/testing';
import {ComponentFixture, TestBed} from '@angular/core/testing';
import { InputComponent } from './input.component';
import {InputComponent} from './input.component';
describe('InputComponent', () => {
let component: InputComponent;
......@@ -8,7 +8,7 @@ describe('InputComponent', () => {
beforeEach(async () => {
await TestBed.configureTestingModule({
declarations: [ InputComponent ]
declarations: [InputComponent]
})
.compileComponents();
});
......
import { Component, OnInit, EventEmitter, Output } from '@angular/core';
import {Component, EventEmitter, OnInit, Output} from '@angular/core';
@Component({
selector: 'app-input',
......@@ -9,9 +9,12 @@ export class InputComponent implements OnInit {
isActive = false;
value: string;
@Output() valueEmit = new EventEmitter<string>();
constructor() { }
ngOnInit(): void { }
constructor() {
}
ngOnInit(): void {
}
setState(value: boolean): void {
const text = document.getElementById('inp') as HTMLTextAreaElement;
......
<form id="login_form" action="" [formGroup]="form" novalidate (ngSubmit)="postData(form)">
<form (ngSubmit)="postData(form)" [formGroup]="form" action="" id="login_form" novalidate>
<div class="form-row">
<label for="username">Username</label>
<input id="username" type="text" formControlName="username" spellcheck="false">
<input formControlName="username" id="username" spellcheck="false" type="text">
</div>
<div class="form-row">
<label for="password">Password</label>
<input id="password" type="password" formControlName="password" spellcheck="false">
<input formControlName="password" id="password" spellcheck="false" type="password">
</div>
<div class="form-row">
<input id="submit" value="Submit" type="submit">
<input id="submit" type="submit" value="Submit">
</div>
</form>
......
import { ComponentFixture, TestBed } from '@angular/core/testing';
import {ComponentFixture, TestBed} from '@angular/core/testing';
import { LoginComponent } from './login.component';
import {LoginComponent} from './login.component';
describe('LoginComponent', () => {
let component: LoginComponent;
......@@ -8,7 +8,7 @@ describe('LoginComponent', () => {
beforeEach(async () => {
await TestBed.configureTestingModule({
declarations: [ LoginComponent ]
declarations: [LoginComponent]
})
.compileComponents();
});
......
import { TestBed } from '@angular/core/testing';
import {TestBed} from '@angular/core/testing';
import { ProblemService } from './problem.service';
import {ProblemService} from './problem.service';
describe('ProblemService', () => {
let service: ProblemService;
......
import { Injectable } from '@angular/core';
import { Problem } from './problem';
import { Observable, of } from 'rxjs';
import {Injectable} from '@angular/core';
import {Problem} from './problem';
import {Observable, of} from 'rxjs';
@Injectable({
providedIn: 'root'
})
export class ProblemService {
constructor() { }
constructor() {
}
getProblems(): Observable<Problem[]> {
......
<form id="login_form" action="" [formGroup]="form" (ngSubmit)="postData(form)">
<form (ngSubmit)="postData(form)" [formGroup]="form" action="" id="login_form">
<div class="form-row">
<label for="name">Name</label>
<input id="name" type="text" formControlName="name" spellcheck="false">
<input formControlName="name" id="name" spellcheck="false" type="text">
</div>
<div class="form-row">
<label for="username">Username</label>
<input id="username" type="text" formControlName="username" spellcheck="false">
<input formControlName="username" id="username" spellcheck="false" type="text">
</div>
<div class="form-row">
<label for="email">Email</label>
<input id="email" type="text" formControlName="email" spellcheck="false">
<input formControlName="email" id="email" spellcheck="false" type="text">
</div>
<div class="form-row">
<label for="password">Password</label>
<input id="password" type="password" formControlName="password" spellcheck="false">
<input formControlName="password" id="password" spellcheck="false" type="password">
</div>
<div class="form-row">
<label for="confirm_password">Confirm Password</label>
<input id="confirm_password" type="password" formControlName="confirm_password" spellcheck="false">
<input formControlName="confirm_password" id="confirm_password" spellcheck="false" type="password">
</div>
<div class="form-row">
<input id="submit" value="Submit" type="submit">
<input id="submit" type="submit" value="Submit">
</div>
</form>
......
import { ComponentFixture, TestBed } from '@angular/core/testing';
import {ComponentFixture, TestBed} from '@angular/core/testing';
import { RegisterComponent } from './register.component';
import {RegisterComponent} from './register.component';
describe('RegisterComponent', () => {
let component: RegisterComponent;
......@@ -8,7 +8,7 @@ describe('RegisterComponent', () => {
beforeEach(async () => {
await TestBed.configureTestingModule({
declarations: [ RegisterComponent ]
declarations: [RegisterComponent]
})
.compileComponents();
});
......
import { Component, OnInit } from '@angular/core';
import {Component, OnInit} from '@angular/core';
import {AbstractControl, FormBuilder, FormControl, FormGroup, Validators} from '@angular/forms';
import { first } from 'rxjs/operators';
import { ApiService } from '../api.service';
import {first} from 'rxjs/operators';
import {ApiService} from '../api.service';
@Component({
selector: 'app-register',
......@@ -11,6 +11,8 @@ import { ApiService } from '../api.service';
export class RegisterComponent implements OnInit {
form: FormGroup;
timeout: any;
set: NodeListOf<HTMLInputElement>;
submitElement: HTMLInputElement;
constructor(private fb: FormBuilder, private dataService: ApiService) {
this.form = this.fb.group({
......@@ -22,8 +24,21 @@ export class RegisterComponent implements OnInit {
});
}
set: NodeListOf<HTMLInputElement>;
submitElement: HTMLInputElement;
get email(): AbstractControl {
return this.form.get('email');
}
get password(): AbstractControl {
return this.form.get('password');
}
get name(): AbstractControl {
return this.form.get('name');
}
get username(): AbstractControl {
return this.form.get('name');
}
ngOnInit(): void {
this.submitElement = document.getElementById('submit') as HTMLInputElement;
......@@ -44,8 +59,7 @@ export class RegisterComponent implements OnInit {
this.set.forEach(item => {
if (item.value !== '' && item.value !== null) {
item.parentElement.querySelector('label').classList.add('active');
}
else {
} else {
item.parentElement.querySelector('label').classList.remove('active');
}
});
......@@ -53,7 +67,9 @@ export class RegisterComponent implements OnInit {
postData(frm: any): void {
if (this.submitElement.classList.contains('disabled')) { return; }
if (this.submitElement.classList.contains('disabled')) {
return;
}
this.submitElement.classList.add('disabled');
if (this.form.get('name').invalid) {
......@@ -148,10 +164,5 @@ export class RegisterComponent implements OnInit {
}
get email(): AbstractControl { return this.form.get('email'); }
get password(): AbstractControl { return this.form.get('password'); }
get name(): AbstractControl { return this.form.get('name'); }
get username(): AbstractControl { return this.form.get('name'); }
}
import { TestBed } from '@angular/core/testing';
import {TestBed} from '@angular/core/testing';
import { RunCodeService } from './run-code.service';
import {RunCodeService} from './run-code.service';
describe('RunCodeService', () => {
let service: RunCodeService;
......
import { Injectable } from '@angular/core';
import { Observable, of } from 'rxjs';
import {Injectable} from '@angular/core';
@Injectable({
providedIn: 'root'
......@@ -9,6 +8,9 @@ export class RunCodeService {
status = 'Compiling...';
result = 0;
constructor() {
}
run(): void {
const output = `25
abcd
......@@ -30,6 +32,4 @@ abcd
this.result = 1;
}, 6000);
}
constructor() { }
}
<div id="save-cover" [class.open]="isActive"></div>
<div [class.open]="isActive" id="save-cover"></div>
<div id="save-popup" [class.open]="isActive">
<div [class.open]="isActive" id="save-popup">
<div *ngIf="!isUploading">
<label for="filenameInput" id="filenameInputLabel">Enter Filename:</label><br>
<input id="filenameInput" type="text" name="filenameInput" [(ngModel)]="file.filename">
<input [(ngModel)]="file.filename" id="filenameInput" name="filenameInput" type="text">
<span>{{file.language}}</span>
<br>
<button id="save-cancel" (click)="isActive = false">Cancel</button>
<button id="save-done" [class.disabled]="file.filename === ''" (click)="file.filename !== '' && submitFile()">Done</button>
<button (click)="isActive = false" id="save-cancel">Cancel</button>
<button (click)="file.filename !== '' && submitFile()" [class.disabled]="file.filename === ''" id="save-done">Done
</button>
</div>
<div *ngIf="isUploading">Uploading...</div>
</div>
import { ComponentFixture, TestBed } from '@angular/core/testing';
import {ComponentFixture, TestBed} from '@angular/core/testing';
import { SaveFileComponent } from './save-file.component';
import {SaveFileComponent} from './save-file.component';
describe('SaveFileComponent', () => {
let component: SaveFileComponent;
......@@ -8,7 +8,7 @@ describe('SaveFileComponent', () => {
beforeEach(async () => {
await TestBed.configureTestingModule({
declarations: [ SaveFileComponent ]
declarations: [SaveFileComponent]
})
.compileComponents();
});
......
import {Component, OnInit, Output, EventEmitter, Input} from '@angular/core';
import {Component, EventEmitter, Input, OnInit, Output} from '@angular/core';
import {FileService} from '../file.service';
import {File} from '../file';
import {Location} from '@angular/common';
......
import { ComponentFixture, TestBed } from '@angular/core/testing';
import {ComponentFixture, TestBed} from '@angular/core/testing';
import { TestcaseStatusComponent } from './testcase-status.component';
import {TestcaseStatusComponent} from './testcase-status.component';
describe('TestcaseStatusComponent', () => {
let component: TestcaseStatusComponent;
......@@ -8,7 +8,7 @@ describe('TestcaseStatusComponent', () => {
beforeEach(async () => {
await TestBed.configureTestingModule({
declarations: [ TestcaseStatusComponent ]
declarations: [TestcaseStatusComponent]
})
.compileComponents();
});
......
import { Component, OnInit } from '@angular/core';
import {Component, OnInit} from '@angular/core';
@Component({
selector: 'app-testcase-status',
......@@ -7,7 +7,8 @@ import { Component, OnInit } from '@angular/core';
})
export class TestcaseStatusComponent implements OnInit {
constructor() { }
constructor() {
}
ngOnInit(): void {
}
......
<div class="container">
<div class="card" id="user_cred">
<div id="grid1">
<img id="profile-icon" src="{{user.img_url}}" alt="Profile Icon">
<img alt="Profile Icon" id="profile-icon" src="{{user.img_url}}">
<div class="name-and-id">
<div class="tc">
<div>{{user.name}}</div>
......@@ -9,8 +9,12 @@
<div class="inline-code">{{user.username}}</div>
<div class="inline-code"><i class="lnr lnr-star"></i> {{user.rating}}</div>
<div>
<a href="" id="edit"><button>Edit Details</button></a>
<a href="" id=" Logout" (click)="logout()"><button>Logout</button></a>
<a href="" id="edit">
<button>Edit Details</button>
</a>
<a (click)="logout()" href="" id=" Logout">
<button>Logout</button>
</a>
</div>
</div>
</div>
......@@ -31,7 +35,7 @@
</tr>
</table>
<div id="canvas">
<canvas id="myChart" width="100px" height="100px"></canvas>
<canvas height="100px" id="myChart" width="100px"></canvas>
</div>
</div>
</div>
......@@ -31,7 +31,7 @@
height: 106px;
vertical-align: middle;
&>div {
& > div {
margin: 4px;
.lnr {
......
import { ComponentFixture, TestBed } from '@angular/core/testing';
import {ComponentFixture, TestBed} from '@angular/core/testing';
import { UserComponent } from './user.component';
import {UserComponent} from './user.component';
describe('UserComponent', () => {
let component: UserComponent;
......@@ -8,7 +8,7 @@ describe('UserComponent', () => {
beforeEach(async () => {
await TestBed.configureTestingModule({
declarations: [ UserComponent ]
declarations: [UserComponent]
})
.compileComponents();
});
......
......@@ -7,32 +7,41 @@ var number, bumpOnly;
for (var i = 2; i < process.argv.length; i++) {
if (process.argv[i] == "-bump") bumpOnly = true;
else if (/^\d+\.\d+\.\d+$/.test(process.argv[i])) number = process.argv[i];
else { console.log("Bogus command line arg: " + process.argv[i]); process.exit(1); }
else {
console.log("Bogus command line arg: " + process.argv[i]);
process.exit(1);
}
}
if (!number) { console.log("Must give a version"); process.exit(1); }
if (!number) {
console.log("Must give a version");
process.exit(1);
}
function rewrite(file, f) {
fs.writeFileSync(file, f(fs.readFileSync(file, "utf8")), "utf8");
}
rewrite("src/edit/main.js", function(lib) {
rewrite("src/edit/main.js", function (lib) {
return lib.replace(/CodeMirror\.version = "\d+\.\d+\.\d+"/,
"CodeMirror.version = \"" + number + "\"");
});
function rewriteJSON(pack) {
return pack.replace(/"version":\s*"\d+\.\d+\.\d+"/, "\"version\": \"" + number + "\"");
}
rewrite("package.json", rewriteJSON);
rewrite("doc/manual.html", function(manual) {
rewrite("doc/manual.html", function (manual) {
return manual.replace(/>version \d+\.\d+\.\d+<\/span>/, ">version " + number + "</span>");
});
if (bumpOnly) process.exit(0);
child.exec("bash bin/authors.sh", function(){});
child.exec("bash bin/authors.sh", function () {
});
rewrite("index.html", function(index) {
rewrite("index.html", function (index) {
return index.replace(/\.zip">\d+\.\d+\.\d+<\/a>/,
".zip\">" + number + "</a>");
});
......@@ -27,20 +27,24 @@ if (!CodeMirror.modes[modeName])
require("../mode/" + modeName + "/" + modeName + ".js");
function esc(str) {
return str.replace(/[<&]/g, function(ch) { return ch == "&" ? "&amp;" : "&lt;"; });
return str.replace(/[<&]/g, function (ch) {
return ch == "&" ? "&amp;" : "&lt;";
});
}
var code = fs.readFileSync("/dev/stdin", "utf8");
var curStyle = null, accum = "";
function flush() {
if (curStyle) process.stdout.write("<span class=\"" + curStyle.replace(/(^|\s+)/g, "$1cm-") + "\">" + esc(accum) + "</span>");
else process.stdout.write(esc(accum));
}
CodeMirror.runMode(code, lang, function(text, style) {
CodeMirror.runMode(code, lang, function (text, style) {
if (style != curStyle) {
flush();
curStyle = style; accum = text;
curStyle = style;
accum = text;
} else {
accum += text;
}
......
......@@ -2,15 +2,18 @@
<title>CodeMirror: Active Line Demo</title>
<meta charset="utf-8"/>
<link rel=stylesheet href="../doc/docs.css">
<link href="../doc/docs.css" rel=stylesheet>
<link rel="stylesheet" href="../lib/codemirror.css">
<link href="../lib/codemirror.css" rel="stylesheet">
<script src="../lib/codemirror.js"></script>
<script src="../mode/xml/xml.js"></script>
<script src="../addon/selection/active-line.js"></script>
<style>
.CodeMirror {border-top: 1px solid black; border-bottom: 1px solid black;}
</style>
.CodeMirror {
border-top: 1px solid black;
border-bottom: 1px solid black;
}
</style>
<div id=nav>
<a href="https://codemirror.net"><h1>CodeMirror</h1><img id=logo src="../doc/logo.png"></a>
......@@ -25,17 +28,16 @@
</div>
<article>
<h2>Active Line Demo</h2>
<form><textarea id="code" name="code">
<h2>Active Line Demo</h2>
<form><textarea id="code" name="code">
<?xml version="1.0" encoding="UTF-8"?>
<rss xmlns:atom="http://www.w3.org/2005/Atom" version="2.0"
xmlns:georss="http://www.georss.org/georss"
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom"
xmlns:twitter="http://api.twitter.com">
<channel>
<title>Twitter / codemirror</title>
<link>http://twitter.com/codemirror</link>
<atom:link type="application/rss+xml"
href="http://twitter.com/statuses/user_timeline/242283288.rss" rel="self"/>
<atom:link href="http://twitter.com/statuses/user_timeline/242283288.rss"
rel="self" type="application/rss+xml"/>
<description>Twitter updates from CodeMirror / codemirror.</description>
<language>en-us</language>
<ttl>40</ttl>
......@@ -65,24 +67,24 @@
</rss></textarea></form>
<script>
var nonEmpty = false;
var editor = CodeMirror.fromTextArea(document.getElementById("code"), {
var nonEmpty = false;
var editor = CodeMirror.fromTextArea(document.getElementById("code"), {
mode: "application/xml",
styleActiveLine: true,
lineNumbers: true,
lineWrapping: true
});
});
function toggleSelProp() {
function toggleSelProp() {
nonEmpty = !nonEmpty;
editor.setOption("styleActiveLine", {nonEmpty: nonEmpty});
var label = nonEmpty ? 'Disable nonEmpty option' : 'Enable nonEmpty option';
document.getElementById('toggleButton').innerText = label;
}
</script>
}
</script>
<p>Styling the current cursor line.</p>
<button onclick="toggleSelProp()" id="toggleButton">Enable <code>nonEmpty</code> option</button>
<button id="toggleButton" onclick="toggleSelProp()">Enable <code>nonEmpty</code> option</button>
</article>
</article>
......@@ -2,10 +2,10 @@
<title>CodeMirror: Any Word Completion Demo</title>
<meta charset="utf-8"/>
<link rel=stylesheet href="../doc/docs.css">
<link href="../doc/docs.css" rel=stylesheet>
<link rel="stylesheet" href="../lib/codemirror.css">
<link rel="stylesheet" href="../addon/hint/show-hint.css">
<link href="../lib/codemirror.css" rel="stylesheet">
<link href="../addon/hint/show-hint.css" rel="stylesheet">
<script src="../lib/codemirror.js"></script>
<script src="../addon/hint/show-hint.js"></script>
<script src="../addon/hint/anyword-hint.js"></script>
......@@ -24,8 +24,8 @@
</div>
<article>
<h2>Any Word Completion Demo</h2>
<form><textarea id="code" name="code">
<h2>Any Word Completion Demo</h2>
<form><textarea id="code" name="code">
(function() {
"use strict";
......@@ -61,14 +61,14 @@
})();
</textarea></form>
<p>Press <strong>ctrl-space</strong> to activate autocompletion. The
completion uses
the <a href="../doc/manual.html#addon_anyword-hint">anyword-hint.js</a>
module, which simply looks at nearby words in the buffer and completes
to those.</p>
<p>Press <strong>ctrl-space</strong> to activate autocompletion. The
completion uses
the <a href="../doc/manual.html#addon_anyword-hint">anyword-hint.js</a>
module, which simply looks at nearby words in the buffer and completes
to those.</p>
<script>
CodeMirror.commands.autocomplete = function(cm) {
CodeMirror.commands.autocomplete = function (cm) {
cm.showHint({hint: CodeMirror.hint.anyword});
}
var editor = CodeMirror.fromTextArea(document.getElementById("code"), {
......@@ -76,4 +76,4 @@ to those.</p>
extraKeys: {"Ctrl-Space": "autocomplete"}
});
</script>
</article>
</article>
......@@ -2,14 +2,20 @@
<title>CodeMirror: Bi-directional Text Demo</title>
<meta charset="utf-8"/>
<link rel=stylesheet href="../doc/docs.css">
<link href="../doc/docs.css" rel=stylesheet>
<link rel="stylesheet" href="../lib/codemirror.css">
<link href="../lib/codemirror.css" rel="stylesheet">
<script src="../lib/codemirror.js"></script>
<script src="../mode/xml/xml.js"></script>
<style>
.CodeMirror {border-top: 1px solid black; border-bottom: 1px solid black;}
fieldset {border: none}
.CodeMirror {
border-top: 1px solid black;
border-bottom: 1px solid black;
}
fieldset {
border: none
}
</style>
<div id=nav>
<a href="https://codemirror.net"><h1>CodeMirror</h1><img id=logo src="../doc/logo.png"></a>
......@@ -25,9 +31,9 @@
</div>
<article>
<h2>Bi-directional Text Demo</h2>
<form><textarea id="code" name="code"><!-- Piece of the CodeMirror manual, 'translated' into Arabic by Google Translate -->
<!-- قطعة من دليل CodeMirror، "ترجم" إلى العربية بواسطة جوجل ترجمة -->
<h2>Bi-directional Text Demo</h2>
<form><textarea id="code" name="code"><!-- Piece of the CodeMirror manual, 'translated' into Arabic by Google Translate -->
<!-- قطعة من دليل CodeMirror، "ترجم" إلى العربية بواسطة جوجل ترجمة -->
<dl>
<dt id=option_value><code>value (string or Doc)</code></dt>
......@@ -57,47 +63,52 @@
</textarea>
<fieldset>
Editor default direction:
<input type="radio" id="ltr" name="direction"/><label for="ltr">LTR</label>
<input type="radio" id="rtl" name="direction"/><label for="rtl">RTL</label>
<input id="ltr" name="direction" type="radio"/><label for="ltr">LTR</label>
<input id="rtl" name="direction" type="radio"/><label for="rtl">RTL</label>
</fieldset>
<fieldset>
HTML document direction:
<input type="radio" id="htmlltr" name="htmldirection"/><label for="htmlltr">LTR</label>
<input type="radio" id="htmlrtl" name="htmldirection"/><label for="htmlrtl">RTL</label>
<input id="htmlltr" name="htmldirection" type="radio"/><label for="htmlltr">LTR</label>
<input id="htmlrtl" name="htmldirection" type="radio"/><label for="htmlrtl">RTL</label>
</fieldset>
<fieldset>
<input type="checkbox" id="rtlMoveVisually"/><label for="rtlMoveVisually">Use visual order for arrow key movement.</label>
<input id="rtlMoveVisually" type="checkbox"/><label for="rtlMoveVisually">Use visual order for arrow key
movement.</label>
</fieldset>
</form>
</form>
<script>
var editor = CodeMirror.fromTextArea(document.getElementById("code"), {
var editor = CodeMirror.fromTextArea(document.getElementById("code"), {
mode: "text/html",
lineNumbers: true,
lineWrapping: true,
direction: "rtl"
});
});
var dirRadios = {ltr: document.getElementById("ltr"),
rtl: document.getElementById("rtl")};
dirRadios[editor.getOption("direction")].checked = true;
dirRadios["rtl"].onchange = dirRadios["ltr"].onchange = function() {
var dirRadios = {
ltr: document.getElementById("ltr"),
rtl: document.getElementById("rtl")
};
dirRadios[editor.getOption("direction")].checked = true;
dirRadios["rtl"].onchange = dirRadios["ltr"].onchange = function () {
editor.setOption("direction", dirRadios["rtl"].checked ? "rtl" : "ltr");
};
};
var HtmlDirRadios = {ltr: document.getElementById("htmlltr"),
rtl: document.getElementById("htmlrtl")};
HtmlDirRadios["ltr"].checked = true;
HtmlDirRadios["rtl"].onchange = HtmlDirRadios["ltr"].onchange = function() {
var HtmlDirRadios = {
ltr: document.getElementById("htmlltr"),
rtl: document.getElementById("htmlrtl")
};
HtmlDirRadios["ltr"].checked = true;
HtmlDirRadios["rtl"].onchange = HtmlDirRadios["ltr"].onchange = function () {
document.dir = (HtmlDirRadios["rtl"].checked ? "rtl" : "ltr");
};
};
var moveCheckbox = document.getElementById("rtlMoveVisually");
moveCheckbox.checked = editor.getOption("rtlMoveVisually");
moveCheckbox.onchange = function() {
var moveCheckbox = document.getElementById("rtlMoveVisually");
moveCheckbox.checked = editor.getOption("rtlMoveVisually");
moveCheckbox.onchange = function () {
editor.setOption("rtlMoveVisually", moveCheckbox.checked);
};
</script>
};
</script>
<p>Demonstration of bi-directional text support. See
the <a href="http://marijnhaverbeke.nl/blog/cursor-in-bidi-text.html">related
......
......@@ -2,14 +2,22 @@
<title>CodeMirror: B-Tree visualization</title>
<meta charset="utf-8"/>
<link rel=stylesheet href="../doc/docs.css">
<link href="../doc/docs.css" rel=stylesheet>
<link rel="stylesheet" href="../lib/codemirror.css">
<link href="../lib/codemirror.css" rel="stylesheet">
<script src="../lib/codemirror.js"></script>
<style>
.lineblock { display: inline-block; margin: 1px; height: 5px; }
.CodeMirror {border: 1px solid #aaa; height: 400px}
</style>
.lineblock {
display: inline-block;
margin: 1px;
height: 5px;
}
.CodeMirror {
border: 1px solid #aaa;
height: 400px
}
</style>
<div id=nav>
<a href="https://codemirror.net"><h1>CodeMirror</h1><img id=logo src="../doc/logo.png"></a>
......@@ -24,23 +32,23 @@
</div>
<article>
<h2>B-Tree visualization</h2>
<form><textarea id="code" name="code">type here, see a summary of the document b-tree below</textarea></form>
<div style="display: inline-block; height: 402px; overflow-y: auto" id="output"></div>
<h2>B-Tree visualization</h2>
<form><textarea id="code" name="code">type here, see a summary of the document b-tree below</textarea></form>
<div id="output" style="display: inline-block; height: 402px; overflow-y: auto"></div>
<script id="me">
var editor = CodeMirror.fromTextArea(document.getElementById("code"), {
var editor = CodeMirror.fromTextArea(document.getElementById("code"), {
lineNumbers: true,
lineWrapping: true
});
var updateTimeout;
editor.on("change", function(cm) {
});
var updateTimeout;
editor.on("change", function (cm) {
clearTimeout(updateTimeout);
updateTimeout = setTimeout(updateVisual, 200);
});
updateVisual();
});
updateVisual();
function updateVisual() {
function updateVisual() {
var out = document.getElementById("output");
out.innerHTML = "";
......@@ -49,7 +57,8 @@ function updateVisual() {
out.appendChild(document.createElement("div")).innerHTML =
"<b>leaf</b>: " + node.lines.length + " lines, " + Math.round(node.height) + " px";
var lines = out.appendChild(document.createElement("div"));
lines.style.lineHeight = "6px"; lines.style.marginLeft = "10px";
lines.style.lineHeight = "6px";
lines.style.marginLeft = "10px";
for (var i = 0; i < node.lines.length; ++i) {
var line = node.lines[i], lineElt = lines.appendChild(document.createElement("div"));
lineElt.className = "lineblock";
......@@ -67,17 +76,25 @@ function updateVisual() {
drawTree(sub, node.children[i]);
}
}
drawTree(out, editor.getDoc());
}
}
function fillEditor() {
function fillEditor() {
var sc = document.getElementById("me");
var doc = (sc.textContent || sc.innerText || sc.innerHTML).replace(/^\s*/, "") + "\n";
doc += doc; doc += doc; doc += doc; doc += doc; doc += doc; doc += doc;
doc += doc;
doc += doc;
doc += doc;
doc += doc;
doc += doc;
doc += doc;
editor.setValue(doc);
}
}
</script>
<p><button onclick="fillEditor()">Add a lot of content</button></p>
<p>
<button onclick="fillEditor()">Add a lot of content</button>
</p>
</article>
</article>
......@@ -2,15 +2,18 @@
<title>CodeMirror: Multiple Buffer & Split View Demo</title>
<meta charset="utf-8"/>
<link rel=stylesheet href="../doc/docs.css">
<link href="../doc/docs.css" rel=stylesheet>
<link rel="stylesheet" href="../lib/codemirror.css">
<link href="../lib/codemirror.css" rel="stylesheet">
<script src="../lib/codemirror.js"></script>
<script src="../mode/javascript/javascript.js"></script>
<script src="../mode/css/css.js"></script>
<style id=style>
.CodeMirror {border: 1px solid black; height: 250px;}
</style>
.CodeMirror {
border: 1px solid black;
height: 250px;
}
</style>
<div id=nav>
<a href="https://codemirror.net"><h1>CodeMirror</h1><img id=logo src="../doc/logo.png"></a>
......@@ -25,42 +28,44 @@
</div>
<article>
<h2>Multiple Buffer & Split View Demo</h2>
<h2>Multiple Buffer & Split View Demo</h2>
<div id=code_top></div>
<div>
Select buffer: <select id=buffers_top></select>
&nbsp; &nbsp; <button onclick="newBuf('top')">New buffer</button>
&nbsp; &nbsp;
<button onclick="newBuf('top')">New buffer</button>
</div>
<div id=code_bot></div>
<div>
Select buffer: <select id=buffers_bot></select>
&nbsp; &nbsp; <button onclick="newBuf('bot')">New buffer</button>
&nbsp; &nbsp;
<button onclick="newBuf('bot')">New buffer</button>
</div>
<script id=script>
var sel_top = document.getElementById("buffers_top");
CodeMirror.on(sel_top, "change", function() {
var sel_top = document.getElementById("buffers_top");
CodeMirror.on(sel_top, "change", function () {
selectBuffer(ed_top, sel_top.options[sel_top.selectedIndex].value);
});
});
var sel_bot = document.getElementById("buffers_bot");
CodeMirror.on(sel_bot, "change", function() {
var sel_bot = document.getElementById("buffers_bot");
CodeMirror.on(sel_bot, "change", function () {
selectBuffer(ed_bot, sel_bot.options[sel_bot.selectedIndex].value);
});
});
var buffers = {};
var buffers = {};
function openBuffer(name, text, mode) {
function openBuffer(name, text, mode) {
buffers[name] = CodeMirror.Doc(text, mode);
var opt = document.createElement("option");
opt.appendChild(document.createTextNode(name));
sel_top.appendChild(opt);
sel_bot.appendChild(opt.cloneNode(true));
}
}
function newBuf(where) {
function newBuf(where) {
var name = prompt("Name for the buffer", "*scratch*");
if (name == null) return;
if (buffers.hasOwnProperty(name)) {
......@@ -71,34 +76,37 @@ function newBuf(where) {
selectBuffer(where == "top" ? ed_top : ed_bot, name);
var sel = where == "top" ? sel_top : sel_bot;
sel.value = name;
}
}
function selectBuffer(editor, name) {
function selectBuffer(editor, name) {
var buf = buffers[name];
if (buf.getEditor()) buf = buf.linkedDoc({sharedHist: true});
var old = editor.swapDoc(buf);
var linked = old.iterLinkedDocs(function(doc) {linked = doc;});
var linked = old.iterLinkedDocs(function (doc) {
linked = doc;
});
if (linked) {
// Make sure the document in buffers is the one the other view is looking at
for (var name in buffers) if (buffers[name] == old) buffers[name] = linked;
old.unlinkDoc(linked);
}
editor.focus();
}
}
function nodeContent(id) {
function nodeContent(id) {
var node = document.getElementById(id), val = node.textContent || node.innerText;
val = val.slice(val.match(/^\s*/)[0].length, val.length - val.match(/\s*$/)[0].length) + "\n";
return val;
}
openBuffer("js", nodeContent("script"), "javascript");
openBuffer("css", nodeContent("style"), "css");
}
openBuffer("js", nodeContent("script"), "javascript");
openBuffer("css", nodeContent("style"), "css");
var ed_top = CodeMirror(document.getElementById("code_top"), {lineNumbers: true});
selectBuffer(ed_top, "js");
var ed_bot = CodeMirror(document.getElementById("code_bot"), {lineNumbers: true});
selectBuffer(ed_bot, "js");
</script>
var ed_top = CodeMirror(document.getElementById("code_top"), {lineNumbers: true});
selectBuffer(ed_top, "js");
var ed_bot = CodeMirror(document.getElementById("code_bot"), {lineNumbers: true});
selectBuffer(ed_bot, "js");
</script>
<p>Demonstration of
using <a href="../doc/manual.html#linkedDoc">linked documents</a>
......@@ -106,4 +114,4 @@ selectBuffer(ed_bot, "js");
using <a href="../doc/manual.html#swapDoc"><code>swapDoc</code></a>
to use a single editor to display multiple documents.</p>
</article>
</article>
......@@ -2,15 +2,17 @@
<title>CodeMirror: Mode-Changing Demo</title>
<meta charset="utf-8"/>
<link rel=stylesheet href="../doc/docs.css">
<link href="../doc/docs.css" rel=stylesheet>
<link rel="stylesheet" href="../lib/codemirror.css">
<link href="../lib/codemirror.css" rel="stylesheet">
<script src="../lib/codemirror.js"></script>
<script src="../mode/javascript/javascript.js"></script>
<script src="../mode/scheme/scheme.js"></script>
<style>
.CodeMirror {border: 1px solid black;}
</style>
.CodeMirror {
border: 1px solid black;
}
</style>
<div id=nav>
<a href="https://codemirror.net"><h1>CodeMirror</h1><img id=logo src="../doc/logo.png"></a>
......@@ -25,8 +27,8 @@
</div>
<article>
<h2>Mode-Changing Demo</h2>
<form><textarea id="code" name="code">
<h2>Mode-Changing Demo</h2>
<form><textarea id="code" name="code">
;; If there is Scheme code in here, the editor will be in Scheme mode.
;; If you put in JS instead, it'll switch to JS mode.
......@@ -34,25 +36,27 @@
(* x x))
</textarea></form>
<p>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.</p>
<p>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.</p>
<script>
<script>
var editor = CodeMirror.fromTextArea(document.getElementById("code"), {
mode: "scheme",
lineNumbers: true
});
var pending;
editor.on("change", function() {
editor.on("change", function () {
clearTimeout(pending);
pending = setTimeout(update, 400);
});
function looksLikeScheme(code) {
return !/^\s*\(\s*function\b/.test(code) && /^\s*[;\(]/.test(code);
}
function update() {
editor.setOption("mode", looksLikeScheme(editor.getValue()) ? "scheme" : "javascript");
}
</script>
</article>
</script>
</article>
......@@ -2,15 +2,18 @@
<title>CodeMirror: Closebrackets Demo</title>
<meta charset="utf-8"/>
<link rel=stylesheet href="../doc/docs.css">
<link href="../doc/docs.css" rel=stylesheet>
<link rel="stylesheet" href="../lib/codemirror.css">
<link href="../lib/codemirror.css" rel="stylesheet">
<script src="../lib/codemirror.js"></script>
<script src="../addon/edit/closebrackets.js"></script>
<script src="../mode/javascript/javascript.js"></script>
<style>
.CodeMirror {border-top: 1px solid #888; border-bottom: 1px solid #888;}
</style>
.CodeMirror {
border-top: 1px solid #888;
border-bottom: 1px solid #888;
}
</style>
<div id=nav>
<a href="https://codemirror.net"><h1>CodeMirror</h1><img id=logo src="../doc/logo.png"></a>
......@@ -25,8 +28,8 @@
</div>
<article>
<h2>Closebrackets Demo</h2>
<form><textarea id="code" name="code">function Grid(width, height) {
<h2>Closebrackets Demo</h2>
<form><textarea id="code" name="code">function Grid(width, height) {
this.width = width;
this.height = height;
this.cells = new Array(width * height);
......@@ -49,4 +52,4 @@ Grid.prototype.moveValue = function(from, to) {
<script>
var editor = CodeMirror.fromTextArea(document.getElementById("code"), {autoCloseBrackets: true});
</script>
</article>
</article>
......@@ -2,9 +2,9 @@
<title>CodeMirror: Close-Tag Demo</title>
<meta charset="utf-8"/>
<link rel=stylesheet href="../doc/docs.css">
<link href="../doc/docs.css" rel=stylesheet>
<link rel="stylesheet" href="../lib/codemirror.css">
<link href="../lib/codemirror.css" rel="stylesheet">
<script src="../lib/codemirror.js"></script>
<script src="../addon/edit/closetag.js"></script>
<script src="../addon/fold/xml-fold.js"></script>
......@@ -13,8 +13,11 @@
<script src="../mode/css/css.js"></script>
<script src="../mode/htmlmixed/htmlmixed.js"></script>
<style>
.CodeMirror {border-top: 1px solid #888; border-bottom: 1px solid #888;}
</style>
.CodeMirror {
border-top: 1px solid #888;
border-bottom: 1px solid #888;
}
</style>
<div id=nav>
<a href="https://codemirror.net"><h1>CodeMirror</h1><img id=logo src="../doc/logo.png"></a>
......@@ -29,8 +32,8 @@
</div>
<article>
<h2>Close-Tag Demo</h2>
<form><textarea id="code" name="code"><html</textarea></form>
<h2>Close-Tag Demo</h2>
<form><textarea id="code" name="code"><html</textarea></form>
<script>
var editor = CodeMirror.fromTextArea(document.getElementById("code"), {
......@@ -38,4 +41,4 @@
autoCloseTags: true
});
</script>
</article>
</article>
......@@ -2,10 +2,10 @@
<title>CodeMirror: Autocomplete Demo</title>
<meta charset="utf-8"/>
<link rel=stylesheet href="../doc/docs.css">
<link href="../doc/docs.css" rel=stylesheet>
<link rel="stylesheet" href="../lib/codemirror.css">
<link rel="stylesheet" href="../addon/hint/show-hint.css">
<link href="../lib/codemirror.css" rel="stylesheet">
<link href="../addon/hint/show-hint.css" rel="stylesheet">
<script src="../lib/codemirror.js"></script>
<script src="../addon/hint/show-hint.js"></script>
<script src="../addon/hint/javascript-hint.js"></script>
......@@ -26,8 +26,8 @@
</div>
<article>
<h2>Autocomplete Demo</h2>
<form><textarea id="code" name="code">
<h2>Autocomplete Demo</h2>
<form><textarea id="code" name="code">
function getCompletions(token, context) {
var found = [], start = token.string;
function maybeAdd(str) {
......@@ -65,31 +65,31 @@ function getCompletions(token, context) {
}
</textarea></form>
<p>Press <strong>ctrl-space</strong> to activate autocompletion. Built
on top of the <a href="../doc/manual.html#addon_show-hint"><code>show-hint</code></a>
and <a href="../doc/manual.html#addon_javascript-hint"><code>javascript-hint</code></a>
addons.</p>
<p>Press <strong>ctrl-space</strong> to activate autocompletion. Built
on top of the <a href="../doc/manual.html#addon_show-hint"><code>show-hint</code></a>
and <a href="../doc/manual.html#addon_javascript-hint"><code>javascript-hint</code></a>
addons.</p>
<form><textarea style="display: none" id="synonyms" name="synonyms">
<form><textarea id="synonyms" name="synonyms" style="display: none">
Here, the completion use an asynchronous hinting functions to provide
synonyms for each words. If your browser support `Promises`, the
hinting function can also return one.
</textarea></form>
<script>
var editor = CodeMirror.fromTextArea(document.getElementById("code"), {
<script>
var editor = CodeMirror.fromTextArea(document.getElementById("code"), {
lineNumbers: true,
extraKeys: {"Ctrl-Space": "autocomplete"},
mode: {name: "javascript", globalVars: true}
});
});
if (typeof Promise !== "undefined") {
if (typeof Promise !== "undefined") {
var comp = [
["here", "hither"],
["asynchronous", "nonsynchronous"],
["completion", "achievement", "conclusion", "culmination", "expirations"],
["hinting", "advive", "broach", "imply"],
["function","action"],
["function", "action"],
["provide", "add", "bring", "give"],
["synonyms", "equivalents"],
["words", "token"],
......@@ -97,17 +97,19 @@ if (typeof Promise !== "undefined") {
]
function synonyms(cm, option) {
return new Promise(function(accept) {
setTimeout(function() {
return new Promise(function (accept) {
setTimeout(function () {
var cursor = cm.getCursor(), line = cm.getLine(cursor.line)
var start = cursor.ch, end = cursor.ch
while (start && /\w/.test(line.charAt(start - 1))) --start
while (end < line.length && /\w/.test(line.charAt(end))) ++end
var word = line.slice(start, end).toLowerCase()
for (var i = 0; i < comp.length; i++) if (comp[i].indexOf(word) != -1)
return accept({list: comp[i],
return accept({
list: comp[i],
from: CodeMirror.Pos(cursor.line, start),
to: CodeMirror.Pos(cursor.line, end)})
to: CodeMirror.Pos(cursor.line, end)
})
return accept(null)
}, 100)
})
......@@ -120,7 +122,7 @@ if (typeof Promise !== "undefined") {
mode: "text/x-markdown",
hintOptions: {hint: synonyms}
})
}
</script>
}
</script>
</article>
......@@ -2,10 +2,10 @@
<title>CodeMirror: Emacs bindings demo</title>
<meta charset="utf-8"/>
<link rel=stylesheet href="../doc/docs.css">
<link href="../doc/docs.css" rel=stylesheet>
<link rel="stylesheet" href="../lib/codemirror.css">
<link rel="stylesheet" href="../addon/dialog/dialog.css">
<link href="../lib/codemirror.css" rel="stylesheet">
<link href="../addon/dialog/dialog.css" rel="stylesheet">
<script src="../lib/codemirror.js"></script>
<script src="../mode/clike/clike.js"></script>
<script src="../keymap/emacs.js"></script>
......@@ -15,8 +15,11 @@
<script src="../addon/search/searchcursor.js"></script>
<script src="../addon/search/search.js"></script>
<style>
.CodeMirror {border-top: 1px solid #eee; border-bottom: 1px solid #eee;}
</style>
.CodeMirror {
border-top: 1px solid #eee;
border-bottom: 1px solid #eee;
}
</style>
<div id=nav>
<a href="https://codemirror.net"><h1>CodeMirror</h1><img id=logo src="../doc/logo.png"></a>
......@@ -31,12 +34,12 @@
</div>
<article>
<h2>Emacs bindings demo</h2>
<form><textarea id="code" name="code">
<h2>Emacs bindings demo</h2>
<form><textarea id="code" name="code">
#include "syscalls.h"
/* getchar: simple buffered version */
int getchar(void)
{
{
static char buf[BUFSIZ];
static char *bufp = buf;
static int n = 0;
......@@ -48,22 +51,24 @@ int getchar(void)
}
</textarea></form>
<p>The emacs keybindings are enabled by
including <a href="../keymap/emacs.js">keymap/emacs.js</a> and setting
the <code>keyMap</code> option to <code>"emacs"</code>. Because
CodeMirror's internal API is quite different from Emacs, they are only
a loose approximation of actual emacs bindings, though.</p>
<p>The emacs keybindings are enabled by
including <a href="../keymap/emacs.js">keymap/emacs.js</a> and setting
the <code>keyMap</code> option to <code>"emacs"</code>. Because
CodeMirror's internal API is quite different from Emacs, they are only
a loose approximation of actual emacs bindings, though.</p>
<p>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.</p>
<p>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.</p>
<script>
CodeMirror.commands.save = function() {
CodeMirror.commands.save = function () {
var elt = editor.getWrapperElement();
elt.style.background = "#def";
setTimeout(function() { elt.style.background = ""; }, 300);
setTimeout(function () {
elt.style.background = "";
}, 300);
};
var editor = CodeMirror.fromTextArea(document.getElementById("code"), {
lineNumbers: true,
......@@ -73,4 +78,4 @@ or open a new window.</p>
});
</script>
</article>
</article>
......@@ -3,7 +3,7 @@
<head>
<title>CodeMirror: Code Folding Demo</title>
<meta charset="utf-8"/>
<link rel=stylesheet href="../doc/docs.css">
<link href="../doc/docs.css" rel=stylesheet>
<style>
.some-css {
......@@ -12,8 +12,8 @@
}
</style>
<link rel="stylesheet" href="../lib/codemirror.css">
<link rel="stylesheet" href="../addon/fold/foldgutter.css" />
<link href="../lib/codemirror.css" rel="stylesheet">
<link href="../addon/fold/foldgutter.css" rel="stylesheet"/>
<script src="../lib/codemirror.js"></script>
<script src="../addon/fold/foldcode.js"></script>
<script src="../addon/fold/foldgutter.js"></script>
......@@ -29,7 +29,10 @@
<script src="../mode/python/python.js"></script>
<script src="../mode/markdown/markdown.js"></script>
<style>
.CodeMirror {border-top: 1px solid black; border-bottom: 1px solid black;}
.CodeMirror {
border-top: 1px solid black;
border-bottom: 1px solid black;
}
</style>
</head>
......@@ -92,10 +95,10 @@ class Bar:
<textarea id="code-markdown" name="code"></textarea></div>
</form>
<script id="script">
/*
/*
* Demonstration of code folding
*/
window.onload = function() {
window.onload = function () {
var te = document.getElementById("code");
var sc = document.getElementById("script");
te.value = (sc.textContent || sc.innerText || sc.innerHTML).replace(/^\s*/, "");
......@@ -111,7 +114,11 @@ window.onload = function() {
mode: "javascript",
lineNumbers: true,
lineWrapping: true,
extraKeys: {"Ctrl-Q": function(cm){ cm.foldCode(cm.getCursor()); }},
extraKeys: {
"Ctrl-Q": function (cm) {
cm.foldCode(cm.getCursor());
}
},
foldGutter: true,
gutters: ["CodeMirror-linenumbers", "CodeMirror-foldgutter"]
});
......@@ -121,7 +128,11 @@ window.onload = function() {
mode: {name: "javascript", json: true},
lineNumbers: true,
lineWrapping: true,
extraKeys: {"Ctrl-Q": function(cm){ cm.foldCode(cm.getCursor()); }},
extraKeys: {
"Ctrl-Q": function (cm) {
cm.foldCode(cm.getCursor());
}
},
foldGutter: true,
gutters: ["CodeMirror-linenumbers", "CodeMirror-foldgutter"],
foldOptions: {
......@@ -143,7 +154,8 @@ window.onload = function() {
try {
var parsed = JSON.parse(toParse);
count = Object.keys(parsed).length;
} catch(e) { }
} catch (e) {
}
return count ? `\u21A4${count}\u21A6` : '\u2194';
}
......@@ -155,7 +167,11 @@ window.onload = function() {
mode: "text/html",
lineNumbers: true,
lineWrapping: true,
extraKeys: {"Ctrl-Q": function(cm){ cm.foldCode(cm.getCursor()); }},
extraKeys: {
"Ctrl-Q": function (cm) {
cm.foldCode(cm.getCursor());
}
},
foldGutter: true,
gutters: ["CodeMirror-linenumbers", "CodeMirror-foldgutter"]
});
......@@ -165,7 +181,11 @@ window.onload = function() {
window.editor_python = CodeMirror.fromTextArea(te_python, {
mode: "python",
lineNumbers: true,
extraKeys: {"Ctrl-Q": function(cm){ cm.foldCode(cm.getCursor()); }},
extraKeys: {
"Ctrl-Q": function (cm) {
cm.foldCode(cm.getCursor());
}
},
foldGutter: true,
gutters: ["CodeMirror-linenumbers", "CodeMirror-foldgutter"]
});
......@@ -174,11 +194,16 @@ window.onload = function() {
mode: "markdown",
lineNumbers: true,
lineWrapping: true,
extraKeys: {"Ctrl-Q": function(cm){ cm.foldCode(cm.getCursor()); }},
extraKeys: {
"Ctrl-Q": function (cm) {
cm.foldCode(cm.getCursor());
}
},
foldGutter: true,
gutters: ["CodeMirror-linenumbers", "CodeMirror-foldgutter"]
});
};
</script>
</article>
</body>
};
<
/script>
< /article>
< /body>
......@@ -2,11 +2,11 @@
<title>CodeMirror: Full Screen Editing</title>
<meta charset="utf-8"/>
<link rel=stylesheet href="../doc/docs.css">
<link href="../doc/docs.css" rel=stylesheet>
<link rel="stylesheet" href="../lib/codemirror.css">
<link rel="stylesheet" href="../addon/display/fullscreen.css">
<link rel="stylesheet" href="../theme/night.css">
<link href="../lib/codemirror.css" rel="stylesheet">
<link href="../addon/display/fullscreen.css" rel="stylesheet">
<link href="../theme/night.css" rel="stylesheet">
<script src="../lib/codemirror.js"></script>
<script src="../mode/xml/xml.js"></script>
<script src="../addon/display/fullscreen.js"></script>
......@@ -25,8 +25,8 @@
</div>
<article>
<h2>Full Screen Editing</h2>
<form><textarea id="code" name="code" rows="5">
<h2>Full Screen Editing</h2>
<form><textarea id="code" name="code" rows="5">
<dl>
<dt id="option_indentWithTabs"><code><strong>indentWithTabs</strong>: boolean</code></dt>
<dd>Whether, when indenting, the first N*<code>tabSize</code>
......@@ -65,10 +65,10 @@
lineNumbers: true,
theme: "night",
extraKeys: {
"F11": function(cm) {
"F11": function (cm) {
cm.setOption("fullScreen", !cm.getOption("fullScreen"));
},
"Esc": function(cm) {
"Esc": function (cm) {
if (cm.getOption("fullScreen")) cm.setOption("fullScreen", false);
}
}
......@@ -80,4 +80,4 @@
addon. Press <strong>F11</strong> when cursor is in the editor to
toggle full screen editing. <strong>Esc</strong> can also be used
to <i>exit</i> full screen editing.</p>
</article>
</article>
......@@ -2,14 +2,17 @@
<title>CodeMirror: Hard-wrapping Demo</title>
<meta charset="utf-8"/>
<link rel=stylesheet href="../doc/docs.css">
<link href="../doc/docs.css" rel=stylesheet>
<link rel="stylesheet" href="../lib/codemirror.css">
<link href="../lib/codemirror.css" rel="stylesheet">
<script src="../lib/codemirror.js"></script>
<script src="../mode/markdown/markdown.js"></script>
<script src="../addon/wrap/hardwrap.js"></script>
<style>
.CodeMirror {border-top: 1px solid black; border-bottom: 1px solid black;}
.CodeMirror {
border-top: 1px solid black;
border-bottom: 1px solid black;
}
</style>
<div id=nav>
<a href="https://codemirror.net"><h1>CodeMirror</h1><img id=logo src="../doc/logo.png"></a>
......@@ -25,8 +28,8 @@
</div>
<article>
<h2>Hard-wrapping Demo</h2>
<form><textarea id="code" name="code">Lorem ipsum dolor sit amet, vim augue dictas constituto ex,
<h2>Hard-wrapping Demo</h2>
<form><textarea id="code" name="code">Lorem ipsum dolor sit amet, vim augue dictas constituto ex,
sit falli simul viderer te. Graeco scaevola maluisset sit
ut, in idque viris praesent sea. Ea sea eirmod indoctum
repudiare. Vel noluisse suscipit pericula ut. In ius nulla
......@@ -46,30 +49,32 @@ id, quas doming malorum nec ad. Tollit eruditi vivendum ad
ius, eos soleat ignota ad.
</textarea></form>
<p>Demonstration of
the <a href="../doc/manual.html#addon_hardwrap">hardwrap</a> addon.
The above editor has its change event hooked up to
the <code>wrapParagraphsInRange</code> method, so that the paragraphs
are reflown as you are typing.</p>
<p>Demonstration of
the <a href="../doc/manual.html#addon_hardwrap">hardwrap</a> addon.
The above editor has its change event hooked up to
the <code>wrapParagraphsInRange</code> method, so that the paragraphs
are reflown as you are typing.</p>
<script>
var editor = CodeMirror.fromTextArea(document.getElementById("code"), {
<script>
var editor = CodeMirror.fromTextArea(document.getElementById("code"), {
mode: "markdown",
lineNumbers: true,
extraKeys: {
"Ctrl-Q": function(cm) { cm.wrapParagraph(cm.getCursor(), options); }
"Ctrl-Q": function (cm) {
cm.wrapParagraph(cm.getCursor(), options);
}
}
});
var wait, options = {column: 60}, changing = false;
editor.on("change", function(cm, change) {
});
var wait, options = {column: 60}, changing = false;
editor.on("change", function (cm, change) {
if (changing) return;
clearTimeout(wait);
wait = setTimeout(function() {
wait = setTimeout(function () {
changing = true;
cm.wrapParagraphsInRange(change.from, CodeMirror.changeEnd(change), options);
changing = false;
}, 200);
});
</script>
});
</script>
</article>
......@@ -3,10 +3,10 @@
<head>
<title>CodeMirror: HTML completion demo</title>
<meta charset="utf-8"/>
<link rel=stylesheet href="../doc/docs.css">
<link href="../doc/docs.css" rel=stylesheet>
<link rel="stylesheet" href="../lib/codemirror.css">
<link rel="stylesheet" href="../addon/hint/show-hint.css">
<link href="../lib/codemirror.css" rel="stylesheet">
<link href="../addon/hint/show-hint.css" rel="stylesheet">
<script src="../lib/codemirror.js"></script>
<script src="../addon/hint/show-hint.js"></script>
<script src="../addon/hint/xml-hint.js"></script>
......@@ -16,12 +16,15 @@
<script src="../mode/css/css.js"></script>
<script src="../mode/htmlmixed/htmlmixed.js"></script>
<style>
.CodeMirror {border-top: 1px solid #888; border-bottom: 1px solid #888;}
.CodeMirror {
border-top: 1px solid #888;
border-bottom: 1px solid #888;
}
</style>
</head>
<body>
<div id=nav>
<div id=nav>
<a href="https://codemirror.net"><h1>CodeMirror</h1><img id=logo src="../doc/logo.png"></a>
<ul>
......@@ -32,9 +35,9 @@
<ul>
<li><a class=active href="#">HTML completion</a>
</ul>
</div>
</div>
<article>
<article>
<h2>HTML completion demo</h2>
<p>Shows the <a href="xmlcomplete.html">XML completer</a>
......@@ -44,7 +47,7 @@
<div id="code"></div>
<script>
window.onload = function() {
window.onload = function () {
editor = CodeMirror(document.getElementById("code"), {
mode: "text/html",
extraKeys: {"Ctrl-Space": "autocomplete"},
......@@ -52,5 +55,5 @@
});
};
</script>
</article>
</article>
</body>
......@@ -2,15 +2,21 @@
<title>CodeMirror: Indented wrapped line demo</title>
<meta charset="utf-8"/>
<link rel=stylesheet href="../doc/docs.css">
<link href="../doc/docs.css" rel=stylesheet>
<link rel="stylesheet" href="../lib/codemirror.css">
<link href="../lib/codemirror.css" rel="stylesheet">
<script src="../lib/codemirror.js"></script>
<script src="../mode/xml/xml.js"></script>
<style>
.CodeMirror {border-top: 1px solid black; border-bottom: 1px solid black;}
.CodeMirror pre > * { text-indent: 0px; }
</style>
.CodeMirror {
border-top: 1px solid black;
border-bottom: 1px solid black;
}
.CodeMirror pre > * {
text-indent: 0px;
}
</style>
<div id=nav>
<a href="https://codemirror.net"><h1>CodeMirror</h1><img id=logo src="../doc/logo.png"></a>
......@@ -25,15 +31,18 @@
</div>
<article>
<h2>Indented wrapped line demo</h2>
<form><textarea id="code" name="code">
<h2>Indented wrapped line demo</h2>
<form><textarea id="code" name="code">
<!doctype html>
<body>
<h2 id="overview">Overview</h2>
<p>CodeMirror is a code-editor component that can be embedded in Web pages. The core library provides <em>only</em> 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 <a href="#addons">add-ons</a> included in the distribution, and the <a href="https://github.com/jagthedrummer/codemirror-ui">CodeMirror UI</a> project, for reusable implementations of extra features.</p>
<p>CodeMirror is a code-editor component that can be embedded in Web pages. The core library provides <em>only</em> 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 <a
href="#addons">add-ons</a> included in the distribution, and the <a
href="https://github.com/jagthedrummer/codemirror-ui">CodeMirror UI</a> project, for reusable implementations of extra features.</p>
<p>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 <a href="../mode/"><code>mode/</code></a> directory), and it isn't hard to <a href="#modeapi">write new ones</a> for other languages.</p>
<p>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 <a
href="../mode/"><code>mode/</code></a> directory), and it isn't hard to <a href="#modeapi">write new ones</a> for other languages.</p>
</body>
</textarea></form>
......@@ -48,7 +57,7 @@
mode: "text/html"
});
var charWidth = editor.defaultCharWidth(), basePadding = 4;
editor.on("renderLine", function(cm, line, elt) {
editor.on("renderLine", function (cm, line, elt) {
var off = CodeMirror.countColumn(line.text, null, cm.getOption("tabSize")) * charWidth;
elt.style.textIndent = "-" + off + "px";
elt.style.paddingLeft = (basePadding + off) + "px";
......@@ -56,4 +65,4 @@
editor.refresh();
</script>
</article>
</article>
......@@ -2,10 +2,10 @@
<title>CodeMirror: Linter Demo</title>
<meta charset="utf-8"/>
<link rel=stylesheet href="../doc/docs.css">
<link href="../doc/docs.css" rel=stylesheet>
<link rel="stylesheet" href="../lib/codemirror.css">
<link rel="stylesheet" href="../addon/lint/lint.css">
<link href="../lib/codemirror.css" rel="stylesheet">
<link href="../addon/lint/lint.css" rel="stylesheet">
<script src="../lib/codemirror.js"></script>
<script src="../mode/javascript/javascript.js"></script>
<script src="../mode/css/css.js"></script>
......@@ -17,8 +17,10 @@
<script src="../addon/lint/json-lint.js"></script>
<script src="../addon/lint/css-lint.js"></script>
<style>
.CodeMirror {border: 1px solid black;}
</style>
.CodeMirror {
border: 1px solid black;
}
</style>
<div id=nav>
<a href="https://codemirror.net"><h1>CodeMirror</h1><img id=logo src="../doc/logo.png"></a>
......@@ -33,7 +35,7 @@
</div>
<article>
<h2>Linter Demo</h2>
<h2>Linter Demo</h2>
<p><textarea id="code-js">var widgets = []
......@@ -44,7 +46,7 @@ function updateHints() {
widgets.length = 0;
JSHINT(editor.getValue());
for (var i = 0; i < JSHINT.errors.length; ++i) {
for(var i = 0; i < JSHINT.errors.length; ++i) {
var err = JSHINT.errors[i];
if (!err) continue;
var msg = document.createElement("div");
......@@ -100,7 +102,7 @@ function updateHints() {
/*Warning: empty ruleset */
.foo {
}
}
h1 {
font-weight: bold;
......@@ -145,7 +147,7 @@ li.last {
}
}
</textarea></p>
<script>
<script>
var editor = CodeMirror.fromTextArea(document.getElementById("code-js"), {
lineNumbers: true,
mode: "javascript",
......@@ -166,6 +168,6 @@ li.last {
gutters: ["CodeMirror-lint-markers"],
lint: true
});
</script>
</script>
</article>
</article>
......@@ -2,15 +2,18 @@
<title>CodeMirror: Lazy Mode Loading Demo</title>
<meta charset="utf-8"/>
<link rel=stylesheet href="../doc/docs.css">
<link href="../doc/docs.css" rel=stylesheet>
<link rel="stylesheet" href="../lib/codemirror.css">
<link href="../lib/codemirror.css" rel="stylesheet">
<script src="../lib/codemirror.js"></script>
<script src="../addon/mode/loadmode.js"></script>
<script src="../mode/meta.js"></script>
<style>
.CodeMirror {border-top: 1px solid black; border-bottom: 1px solid black;}
</style>
.CodeMirror {
border-top: 1px solid black;
border-bottom: 1px solid black;
}
</style>
<div id=nav>
<a href="https://codemirror.net"><h1>CodeMirror</h1><img id=logo src="../doc/logo.png"></a>
......@@ -25,25 +28,28 @@
</div>
<article>
<h2>Lazy Mode Loading Demo</h2>
<p style="color: gray">Current mode: <span id="modeinfo">text/plain</span></p>
<form><textarea id="code" name="code">This is the editor.
<h2>Lazy Mode Loading Demo</h2>
<p style="color: gray">Current mode: <span id="modeinfo">text/plain</span></p>
<form><textarea id="code" name="code">This is the editor.
// It starts out in plain text mode,
# use the control below to load and apply a mode
"you'll see the highlighting of" this text /*change*/.
</textarea></form>
<p>Filename, mime, or mode name: <input type=text value=foo.js id=mode> <button type=button onclick="change()">change mode</button></p>
<p>Filename, mime, or mode name: <input id=mode type=text value=foo.js>
<button onclick="change()" type=button>change mode</button>
</p>
<script>
CodeMirror.modeURL = "../mode/%N/%N.js";
var editor = CodeMirror.fromTextArea(document.getElementById("code"), {
CodeMirror.modeURL = "../mode/%N/%N.js";
var editor = CodeMirror.fromTextArea(document.getElementById("code"), {
lineNumbers: true
});
var modeInput = document.getElementById("mode");
CodeMirror.on(modeInput, "keypress", function(e) {
});
var modeInput = document.getElementById("mode");
CodeMirror.on(modeInput, "keypress", function (e) {
if (e.keyCode == 13) change();
});
function change() {
});
function change() {
var val = modeInput.value, m, mode, spec;
if (m = /.+\.([^.]+)$/.exec(val)) {
var info = CodeMirror.findModeByExtension(m[1]);
......@@ -67,6 +73,6 @@ function change() {
} else {
alert("Could not find a mode corresponding to " + val);
}
}
</script>
</article>
}
</script>
</article>
......@@ -2,16 +2,24 @@
<title>CodeMirror: Breakpoint Demo</title>
<meta charset="utf-8"/>
<link rel=stylesheet href="../doc/docs.css">
<link href="../doc/docs.css" rel=stylesheet>
<link rel="stylesheet" href="../lib/codemirror.css">
<link href="../lib/codemirror.css" rel="stylesheet">
<script src="../lib/codemirror.js"></script>
<script src="../mode/javascript/javascript.js"></script>
<style>
.breakpoints {width: .8em;}
.breakpoint { color: #822; }
.CodeMirror {border: 1px solid #aaa;}
</style>
.breakpoints {
width: .8em;
}
.breakpoint {
color: #822;
}
.CodeMirror {
border: 1px solid #aaa;
}
</style>
<div id=nav>
<a href="https://codemirror.net"><h1>CodeMirror</h1><img id=logo src="../doc/logo.png"></a>
......@@ -26,8 +34,8 @@
</div>
<article>
<h2>Breakpoint Demo</h2>
<form><textarea id="code" name="code">
<h2>Breakpoint Demo</h2>
<form><textarea id="code" name="code">
var editor = CodeMirror.fromTextArea(document.getElementById("code"), {
lineNumbers: true,
gutters: ["CodeMirror-linenumbers", "breakpoints"]
......@@ -45,8 +53,8 @@ function makeMarker() {
}
</textarea></form>
<p>Click the line-number gutter to add or remove 'breakpoints'.</p>
<p>Click the line-number gutter to add or remove 'breakpoints'.</p>
<script>eval(document.getElementById("code").value);</script>
</article>
</article>
......@@ -2,18 +2,30 @@
<title>CodeMirror: Selection Marking Demo</title>
<meta charset="utf-8"/>
<link rel=stylesheet href="../doc/docs.css">
<link href="../doc/docs.css" rel=stylesheet>
<link rel="stylesheet" href="../lib/codemirror.css">
<link href="../lib/codemirror.css" rel="stylesheet">
<script src="../lib/codemirror.js"></script>
<script src="../addon/search/searchcursor.js"></script>
<script src="../addon/selection/mark-selection.js"></script>
<style>
.CodeMirror {border-top: 1px solid black; border-bottom: 1px solid black;}
.CodeMirror-selected { background-color: blue !important; }
.CodeMirror-selectedtext { color: white; }
.styled-background { background-color: #ff7; }
</style>
.CodeMirror {
border-top: 1px solid black;
border-bottom: 1px solid black;
}
.CodeMirror-selected {
background-color: blue !important;
}
.CodeMirror-selectedtext {
color: white;
}
.styled-background {
background-color: #ff7;
}
</style>
<div id=nav>
<a href="https://codemirror.net"><h1>CodeMirror</h1><img id=logo src="../doc/logo.png"></a>
......@@ -28,8 +40,8 @@
</div>
<article>
<h2>Selection Marking Demo</h2>
<form><textarea id="code" name="code">
<h2>Selection Marking Demo</h2>
<form><textarea id="code" name="code">
Select something from here. You'll see that the selection's foreground
color changes to white! Since, by default, CodeMirror only puts an
independent "marker" layer behind the text, you'll need something like
......@@ -40,13 +52,14 @@ you to safely give text a background color without screwing up the
visibility of the selection.</textarea></form>
<script>
var editor = CodeMirror.fromTextArea(document.getElementById("code"), {
var editor = CodeMirror.fromTextArea(document.getElementById("code"), {
lineNumbers: true,
styleSelectedText: true
});
editor.markText({line: 6, ch: 26}, {line: 6, ch: 42}, {className: "styled-background"});
</script>
});
editor.markText({line: 6, ch: 26}, {line: 6, ch: 42}, {className: "styled-background"});
</script>
<p>Simple addon to easily mark (and style) selected text. <a href="../doc/manual.html#addon_mark-selection">Docs</a>.</p>
<p>Simple addon to easily mark (and style) selected text. <a href="../doc/manual.html#addon_mark-selection">Docs</a>.
</p>
</article>
</article>
......@@ -2,24 +2,34 @@
<title>CodeMirror: Match Highlighter Demo</title>
<meta charset="utf-8"/>
<link rel=stylesheet href="../doc/docs.css">
<link href="../doc/docs.css" rel=stylesheet>
<link rel="stylesheet" href="../lib/codemirror.css">
<link href="../lib/codemirror.css" rel="stylesheet">
<script src="../lib/codemirror.js"></script>
<script src="../addon/scroll/annotatescrollbar.js"></script>
<script src="../addon/search/matchesonscrollbar.js"></script>
<script src="../addon/search/searchcursor.js"></script>
<script src="../addon/search/match-highlighter.js"></script>
<style>
.CodeMirror {border-top: 1px solid black; border-bottom: 1px solid black;}
.CodeMirror {
border-top: 1px solid black;
border-bottom: 1px solid black;
}
.CodeMirror-focused .cm-matchhighlight {
background-image: url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAIAAAACCAYAAABytg0kAAAAFklEQVQI12NgYGBgkKzc8x9CMDAwAAAmhwSbidEoSQAAAABJRU5ErkJggg==);
background-position: bottom;
background-repeat: repeat-x;
}
.cm-matchhighlight {background-color: lightgreen}
.CodeMirror-selection-highlight-scrollbar {background-color: green}
</style>
.cm-matchhighlight {
background-color: lightgreen
}
.CodeMirror-selection-highlight-scrollbar {
background-color: green
}
</style>
<div id=nav>
<a href="https://codemirror.net"><h1>CodeMirror</h1><img id=logo src="../doc/logo.png"></a>
......@@ -34,8 +44,8 @@
</div>
<article>
<h2>Match Highlighter Demo</h2>
<form><textarea id="code" name="code">Select this text: hardtospot
<h2>Match Highlighter Demo</h2>
<form><textarea id="code" name="code">Select this text: hardtospot
And everywhere else in your code where hardtospot appears will
automatically illuminate. Give it a try! No more hard to spot
variables - stay in context of your code all the time.
......@@ -90,14 +100,14 @@ ornare accumsan. In hac habitasse hardtospot platea dictumst.
</textarea></form>
<script>
var editor = CodeMirror.fromTextArea(document.getElementById("code"), {
var editor = CodeMirror.fromTextArea(document.getElementById("code"), {
lineNumbers: true,
// To highlight on scrollbars as well, pass annotateScrollbar in options
// as below.
highlightSelectionMatches: {showToken: /\w/, annotateScrollbar: true}
});
</script>
});
</script>
<p>Search and highlight occurences of the selected text.</p>
</article>
</article>
......@@ -2,16 +2,19 @@
<title>CodeMirror: Tag Matcher Demo</title>
<meta charset="utf-8"/>
<link rel=stylesheet href="../doc/docs.css">
<link href="../doc/docs.css" rel=stylesheet>
<link rel="stylesheet" href="../lib/codemirror.css">
<link href="../lib/codemirror.css" rel="stylesheet">
<script src="../lib/codemirror.js"></script>
<script src="../addon/fold/xml-fold.js"></script>
<script src="../addon/edit/matchtags.js"></script>
<script src="../mode/xml/xml.js"></script>
<style>
.CodeMirror {border-top: 1px solid black; border-bottom: 1px solid black;}
</style>
.CodeMirror {
border-top: 1px solid black;
border-bottom: 1px solid black;
}
</style>
<div id=nav>
<a href="https://codemirror.net"><h1>CodeMirror</h1><img id=logo src="../doc/logo.png"></a>
......@@ -26,23 +29,23 @@
</div>
<article>
<h2>Tag Matcher Demo</h2>
<h2>Tag Matcher Demo</h2>
<div id="editor"></div>
<script>
window.onload = function() {
window.onload = function () {
editor = CodeMirror(document.getElementById("editor"), {
value: "<html>\n " + document.documentElement.innerHTML + "\n</html>",
mode: "text/html",
matchTags: {bothTags: true},
extraKeys: {"Ctrl-J": "toMatchingTag"}
});
};
};
</script>
<p>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.</p>
</article>
</article>
......@@ -2,10 +2,10 @@
<title>CodeMirror: merge view demo</title>
<meta charset="utf-8"/>
<link rel=stylesheet href="../doc/docs.css">
<link href="../doc/docs.css" rel=stylesheet>
<link rel=stylesheet href="../lib/codemirror.css">
<link rel=stylesheet href="../addon/merge/merge.css">
<link href="../lib/codemirror.css" rel=stylesheet>
<link href="../addon/merge/merge.css" rel=stylesheet>
<script src="../lib/codemirror.js"></script>
<script src="../mode/xml/xml.js"></script>
<script src="../mode/css/css.js"></script>
......@@ -14,11 +14,20 @@
<script src="https://cdnjs.cloudflare.com/ajax/libs/diff_match_patch/20121119/diff_match_patch.js"></script>
<script src="../addon/merge/merge.js"></script>
<style>
.CodeMirror { line-height: 1.2; }
.CodeMirror {
line-height: 1.2;
}
@media screen and (min-width: 1300px) {
article { max-width: 1000px; }
#nav { border-right: 499px solid transparent; }
article {
max-width: 1000px;
}
#nav {
border-right: 499px solid transparent;
}
}
span.clicky {
cursor: pointer;
background: #d70;
......@@ -26,7 +35,7 @@
padding: 0 3px;
border-radius: 3px;
}
</style>
</style>
<div id=nav>
<a href="https://codemirror.net"><h1>CodeMirror</h1><img id=logo src="../doc/logo.png"></a>
......@@ -41,31 +50,32 @@
</div>
<article>
<h2>merge view demo</h2>
<h2>merge view demo</h2>
<div id=view></div>
<div id=view></div>
<p>The <a href="../doc/manual.html#addon_merge"><code>merge</code></a>
addon provides an interface for displaying and merging diffs,
either <span class=clicky onclick="panes = 2; initUI()">two-way</span>
or <span class=clicky onclick="panes = 3; initUI()">three-way</span>.
The left (or center) pane is editable, and the differences with the
other pane(s) are <span class=clicky
onclick="toggleDifferences()">optionally</span> shown live as you edit
it. In the two-way configuration, there are also options to pad changed
sections to <span class=clicky onclick="connect = connect ? null :
<p>The <a href="../doc/manual.html#addon_merge"><code>merge</code></a>
addon provides an interface for displaying and merging diffs,
either <span class=clicky onclick="panes = 2; initUI()">two-way</span>
or <span class=clicky onclick="panes = 3; initUI()">three-way</span>.
The left (or center) pane is editable, and the differences with the
other pane(s) are <span class=clicky
onclick="toggleDifferences()">optionally</span> shown live as you edit
it. In the two-way configuration, there are also options to pad changed
sections to <span class=clicky onclick="connect = connect ? null :
'align'; initUI()">align</span> them, and to <span class=clicky
onclick="collapse = !collapse; initUI()">collapse</span> unchanged
stretches of text.</p>
onclick="collapse = !collapse; initUI()">collapse</span> unchanged
stretches of text.</p>
<p>This addon depends on
the <a href="https://code.google.com/p/google-diff-match-patch/">google-diff-match-patch</a>
library to compute the diffs.</p>
<p>This addon depends on
the <a href="https://code.google.com/p/google-diff-match-patch/">google-diff-match-patch</a>
library to compute the diffs.</p>
<script>
var value, orig1, orig2, dv, panes = 2, highlight = true, connect = "align", collapse = false;
function initUI() {
<script>
var value, orig1, orig2, dv, panes = 2, highlight = true, connect = "align", collapse = false;
function initUI() {
if (value == null) return;
var target = document.getElementById("view");
target.innerHTML = "";
......@@ -79,34 +89,37 @@ function initUI() {
connect: connect,
collapseIdentical: collapse
});
}
}
function toggleDifferences() {
function toggleDifferences() {
dv.setShowDifferences(highlight = !highlight);
}
}
window.onload = function() {
window.onload = function () {
value = document.documentElement.innerHTML;
orig1 = "<!doctype html>\n\n" + value.replace(/\.\.\//g, "codemirror/").replace("yellow", "orange");
orig2 = value.replace(/\u003cscript/g, "\u003cscript type=text/javascript ")
.replace("white", "purple;\n font: comic sans;\n text-decoration: underline;\n height: 15em");
initUI();
let d = document.createElement("div"); d.style.cssText = "width: 50px; margin: 7px; height: 14px"; dv.editor().addLineWidget(57, d)
};
let d = document.createElement("div");
d.style.cssText = "width: 50px; margin: 7px; height: 14px";
dv.editor().addLineWidget(57, d)
};
function mergeViewHeight(mergeView) {
function mergeViewHeight(mergeView) {
function editorHeight(editor) {
if (!editor) return 0;
return editor.getScrollInfo().height;
}
return Math.max(editorHeight(mergeView.leftOriginal()),
editorHeight(mergeView.editor()),
editorHeight(mergeView.rightOriginal()));
}
}
function resize(mergeView) {
function resize(mergeView) {
var height = mergeViewHeight(mergeView);
for(;;) {
for (; ;) {
if (mergeView.leftOriginal())
mergeView.leftOriginal().setSize(null, height);
mergeView.editor().setSize(null, height);
......@@ -118,6 +131,6 @@ function resize(mergeView) {
else height = newHeight;
}
mergeView.wrap.style.height = height + "px";
}
</script>
}
</script>
</article>
......@@ -2,16 +2,21 @@
<title>CodeMirror: Multiplexing Parser Demo</title>
<meta charset="utf-8"/>
<link rel=stylesheet href="../doc/docs.css">
<link href="../doc/docs.css" rel=stylesheet>
<link rel="stylesheet" href="../lib/codemirror.css">
<link href="../lib/codemirror.css" rel="stylesheet">
<script src="../lib/codemirror.js"></script>
<script src="../addon/mode/multiplex.js"></script>
<script src="../mode/xml/xml.js"></script>
<style>
.CodeMirror {border: 1px solid black;}
.cm-delimit {color: #fa4;}
</style>
.CodeMirror {
border: 1px solid black;
}
.cm-delimit {
color: #fa4;
}
</style>
<div id=nav>
<a href="https://codemirror.net"><h1>CodeMirror</h1><img id=logo src="../doc/logo.png"></a>
......@@ -26,11 +31,11 @@
</div>
<article>
<h2>Multiplexing Parser Demo</h2>
<form><textarea id="code" name="code">
<h2>Multiplexing Parser Demo</h2>
<form><textarea id="code" name="code">
<html>
<body style="<<magic>>">
<h1><< this is not <html >></h1>
<h1><< this is not <html>></h1>
<<
multiline
not html
......@@ -42,21 +47,23 @@
</textarea></form>
<script>
CodeMirror.defineMode("demo", function(config) {
CodeMirror.defineMode("demo", function (config) {
return CodeMirror.multiplexingMode(
CodeMirror.getMode(config, "text/html"),
{open: "<<", close: ">>",
{
open: "<<", close: ">>",
mode: CodeMirror.getMode(config, "text/plain"),
delimStyle: "delimit"}
delimStyle: "delimit"
}
// .. more multiplexed styles can follow here
);
});
var editor = CodeMirror.fromTextArea(document.getElementById("code"), {
});
var editor = CodeMirror.fromTextArea(document.getElementById("code"), {
mode: "demo",
lineNumbers: true,
lineWrapping: true
});
</script>
});
</script>
<p>Demonstration of a multiplexing mode, which, at certain
boundary strings, switches to one or more inner modes. The out
......@@ -72,4 +79,4 @@ var editor = CodeMirror.fromTextArea(document.getElementById("code"), {
<a href="../test/index.html#verbose,multiplexing_*">verbose</a>.
</p>
</article>
</article>
......@@ -2,15 +2,20 @@
<title>CodeMirror: Overlay Parser Demo</title>
<meta charset="utf-8"/>
<link rel=stylesheet href="../doc/docs.css">
<link href="../doc/docs.css" rel=stylesheet>
<link rel="stylesheet" href="../lib/codemirror.css">
<link href="../lib/codemirror.css" rel="stylesheet">
<script src="../lib/codemirror.js"></script>
<script src="../addon/mode/overlay.js"></script>
<script src="../mode/xml/xml.js"></script>
<style>
.CodeMirror {border: 1px solid black;}
.cm-mustache {color: #0ca;}
.CodeMirror {
border: 1px solid black;
}
.cm-mustache {
color: #0ca;
}
</style>
<div id=nav>
<a href="https://codemirror.net"><h1>CodeMirror</h1><img id=logo src="../doc/logo.png"></a>
......@@ -26,8 +31,8 @@
</div>
<article>
<h2>Overlay Parser Demo</h2>
<form><textarea id="code" name="code">
<h2>Overlay Parser Demo</h2>
<form><textarea id="code" name="code">
<html>
<body>
<h1>{{title}}</h1>
......@@ -40,9 +45,9 @@
</textarea></form>
<script>
CodeMirror.defineMode("mustache", function(config, parserConfig) {
CodeMirror.defineMode("mustache", function (config, parserConfig) {
var mustacheOverlay = {
token: function(stream, state) {
token: function (stream, state) {
var ch;
if (stream.match("{{")) {
while ((ch = stream.next()) != null)
......@@ -51,14 +56,15 @@ CodeMirror.defineMode("mustache", function(config, parserConfig) {
return "mustache";
}
}
while (stream.next() != null && !stream.match("{{", false)) {}
while (stream.next() != null && !stream.match("{{", false)) {
}
return null;
}
};
return CodeMirror.overlayMode(CodeMirror.getMode(config, parserConfig.backdrop || "text/html"), mustacheOverlay);
});
var editor = CodeMirror.fromTextArea(document.getElementById("code"), {mode: "mustache"});
</script>
});
var editor = CodeMirror.fromTextArea(document.getElementById("code"), {mode: "mustache"});
</script>
<p>Demonstration of a mode that parses HTML, highlighting
the <a href="http://mustache.github.com/">Mustache</a> templating
......@@ -66,4 +72,4 @@ var editor = CodeMirror.fromTextArea(document.getElementById("code"), {mode: "mu
in <a href="../addon/mode/overlay.js"><code>overlay.js</code></a>. View
source to see the 15 lines of code needed to accomplish this.</p>
</article>
</article>
......@@ -2,9 +2,9 @@
<title>CodeMirror: Panel Demo</title>
<meta charset="utf-8"/>
<link rel=stylesheet href="../doc/docs.css">
<link href="../doc/docs.css" rel=stylesheet>
<link rel="stylesheet" href="../lib/codemirror.css">
<link href="../lib/codemirror.css" rel="stylesheet">
<script src="../lib/codemirror.js"></script>
<script src="../mode/javascript/javascript.js"></script>
<script src="../mode/xml/xml.js"></script>
......@@ -14,26 +14,32 @@
.border {
border: 1px solid #f7f7f7;
}
.add-panel {
background: orange;
padding: 3px 6px;
color: white !important;
border-radius: 3px;
}
.add-panel, .remove-panel {
cursor: pointer;
}
.remove-panel {
float: right;
}
.panel {
background: #f7f7f7;
padding: 3px 7px;
font-size: 0.85em;
}
.panel.top, .panel.after-top {
border-bottom: 1px solid #ddd;
}
.panel.bottom, .panel.before-bottom {
border-top: 1px solid #ddd;
}
......@@ -54,48 +60,48 @@
<article>
<h2>Panel Demo</h2>
<h2>Panel Demo</h2>
<div class="border">
<div class="border">
<textarea id="code" name="code"></textarea>
</div>
</div>
<p>
<p>
The <a href="../doc/manual.html#addon_panel"><code>panel</code></a>
addon allows you to display panels above or below an editor.
<br>
Click the links below to add panels at the given position:
</p>
</p>
<div id="demo">
<p>
<div id="demo">
<p>
<a class="add-panel" onclick="addPanel('top')">top</a>
<a class="add-panel" onclick="addPanel('after-top')">after-top</a>
<a class="add-panel" onclick="addPanel('before-bottom')">before-bottom</a>
<a class="add-panel" onclick="addPanel('bottom')">bottom</a>
</p>
<p>
</p>
<p>
You can also replace an existing panel:
</p>
<form onsubmit="return replacePanel(this);" name="replace_panel">
<input type="submit" value="Replace panel n°" />
<input type="number" name="panel_id" min="1" value="1" />
</form>
<script>
var textarea = document.getElementById("code");
var demo = document.getElementById("demo");
var numPanels = 0;
var panels = {};
var editor;
textarea.value = demo.innerHTML.trim();
editor = CodeMirror.fromTextArea(textarea, {
</p>
<form name="replace_panel" onsubmit="return replacePanel(this);">
<input type="submit" value="Replace panel n°"/>
<input min="1" name="panel_id" type="number" value="1"/>
</form>
<script>
var textarea = document.getElementById("code");
var demo = document.getElementById("demo");
var numPanels = 0;
var panels = {};
var editor;
textarea.value = demo.innerHTML.trim();
editor = CodeMirror.fromTextArea(textarea, {
lineNumbers: true,
mode: "htmlmixed"
});
});
function makePanel(where) {
function makePanel(where) {
var node = document.createElement("div");
var id = ++numPanels;
var widget, close, label;
......@@ -106,32 +112,33 @@ function makePanel(where) {
close.setAttribute("title", "Remove me!");
close.setAttribute("class", "remove-panel");
close.textContent = "";
CodeMirror.on(close, "mousedown", function(e) {
CodeMirror.on(close, "mousedown", function (e) {
e.preventDefault()
panels[node.id].clear();
});
label = node.appendChild(document.createElement("span"));
label.textContent = "I'm panel n°" + id;
return node;
}
function addPanel(where) {
}
function addPanel(where) {
var node = makePanel(where);
panels[node.id] = editor.addPanel(node, {position: where, stable: true});
}
}
addPanel("top");
addPanel("bottom");
addPanel("top");
addPanel("bottom");
function replacePanel(form) {
function replacePanel(form) {
var id = form.elements.panel_id.value;
var panel = panels["panel-" + id];
var node = makePanel("");
panels[node.id] = editor.addPanel(node, {replace: panel, position: "after-top", stable: true});
return false;
}
</script>
}
</script>
</div>
</div>
</article>
......@@ -2,17 +2,28 @@
<title>CodeMirror: Placeholder demo</title>
<meta charset="utf-8"/>
<link rel=stylesheet href="../doc/docs.css">
<link href="../doc/docs.css" rel=stylesheet>
<link rel="stylesheet" href="../lib/codemirror.css">
<link href="../lib/codemirror.css" rel="stylesheet">
<script src="../lib/codemirror.js"></script>
<script src="../addon/display/placeholder.js"></script>
<style>
.CodeMirror { border: 1px solid silver; }
.CodeMirror-empty { outline: 1px solid #c22; }
.CodeMirror-empty.CodeMirror-focused { outline: none; }
.CodeMirror pre.CodeMirror-placeholder { color: #999; }
</style>
.CodeMirror {
border: 1px solid silver;
}
.CodeMirror-empty {
outline: 1px solid #c22;
}
.CodeMirror-empty.CodeMirror-focused {
outline: none;
}
.CodeMirror pre.CodeMirror-placeholder {
color: #999;
}
</style>
<div id=nav>
<a href="https://codemirror.net"><h1>CodeMirror</h1><img id=logo src="../doc/logo.png"></a>
......@@ -27,8 +38,8 @@
</div>
<article>
<h2>Placeholder demo</h2>
<form><textarea id="code" name="code" placeholder="Code goes here..."></textarea></form>
<h2>Placeholder demo</h2>
<form><textarea id="code" name="code" placeholder="Code goes here..."></textarea></form>
<p>The <a href="../doc/manual.html#addon_placeholder">placeholder</a>
plug-in adds an option <code>placeholder</code> that can be set to
......@@ -42,4 +53,4 @@
});
</script>
</article>
</article>
......@@ -2,9 +2,9 @@
<title>CodeMirror: HTML5 preview</title>
<meta charset="utf-8"/>
<link rel=stylesheet href="../doc/docs.css">
<link href="../doc/docs.css" rel=stylesheet>
<link rel=stylesheet href=../lib/codemirror.css>
<link href=../lib/codemirror.css rel=stylesheet>
<script src=../lib/codemirror.js></script>
<script src=../mode/xml/xml.js></script>
<script src=../mode/javascript/javascript.js></script>
......@@ -16,6 +16,7 @@
width: 50%;
border: 1px solid black;
}
iframe {
width: 49%;
float: left;
......@@ -23,7 +24,7 @@
border: 1px solid black;
border-left: 0px;
}
</style>
</style>
<div id=nav>
<a href="https://codemirror.net"><h1>CodeMirror</h1><img id=logo src="../doc/logo.png"></a>
......@@ -38,7 +39,7 @@
</div>
<article>
<h2>HTML5 preview</h2>
<h2>HTML5 preview</h2>
<textarea id=code name=code>
<!doctype html>
......@@ -46,11 +47,13 @@
<head>
<meta charset=utf-8>
<title>HTML5 canvas demo</title>
<style>p {font-family: monospace;}</style>
<style>p {
font-family: monospace;
}</style>
</head>
<body>
<p>Canvas pane goes here:</p>
<canvas id=pane width=300 height=200></canvas>
<canvas height=200 id=pane width=300></canvas>
<script>
var canvas = document.getElementById('pane');
var context = canvas.getContext('2d');
......@@ -70,7 +73,7 @@
var editor = CodeMirror.fromTextArea(document.getElementById('code'), {
mode: 'text/html'
});
editor.on("change", function() {
editor.on("change", function () {
clearTimeout(delay);
delay = setTimeout(updatePreview, 300);
});
......@@ -82,6 +85,7 @@
preview.write(editor.getValue());
preview.close();
}
setTimeout(updatePreview, 300);
</script>
</article>
</article>
......@@ -3,18 +3,21 @@
<head>
<title>CodeMirror: HTML completion demo</title>
<meta charset="utf-8"/>
<link rel=stylesheet href="../doc/docs.css">
<link href="../doc/docs.css" rel=stylesheet>
<link rel="stylesheet" href="../lib/codemirror.css">
<link rel="stylesheet" href="../addon/hint/show-hint.css">
<link href="../lib/codemirror.css" rel="stylesheet">
<link href="../addon/hint/show-hint.css" rel="stylesheet">
<script src="//cdnjs.cloudflare.com/ajax/libs/require.js/2.1.14/require.min.js"></script>
<style>
.CodeMirror {border-top: 1px solid #888; border-bottom: 1px solid #888;}
.CodeMirror {
border-top: 1px solid #888;
border-bottom: 1px solid #888;
}
</style>
</head>
<body>
<div id=nav>
<div id=nav>
<a href="https://codemirror.net"><h1>CodeMirror</h1><img id=logo src="../doc/logo.png"></a>
<ul>
<li><a href="../index.html">Home</a>
......@@ -24,9 +27,9 @@
<ul>
<li><a class=active href="#">HTML completion</a>
</ul>
</div>
</div>
<article>
<article>
<h2>RequireJS module loading demo</h2>
<p>This demo does the same thing as
......@@ -50,7 +53,7 @@
});
require(["codemirror", "codemirror/mode/htmlmixed/htmlmixed",
"codemirror/addon/hint/show-hint", "codemirror/addon/hint/html-hint",
"codemirror/addon/mode/loadmode"], function(CodeMirror) {
"codemirror/addon/mode/loadmode"], function (CodeMirror) {
editor = CodeMirror(document.getElementById("code"), {
mode: "text/html",
extraKeys: {"Ctrl-Space": "autocomplete"},
......@@ -58,13 +61,13 @@
});
CodeMirror.modeURL = "codemirror/mode/%N/%N";
document.getElementById("markdown").addEventListener("click", function() {
CodeMirror.requireMode("markdown", function() {
document.getElementById("markdown").addEventListener("click", function () {
CodeMirror.requireMode("markdown", function () {
editor.replaceRange("This is **Markdown**.\n\n", {line: 0, ch: 0});
editor.setOption("mode", "markdown");
});
});
});
</script>
</article>
</article>
</body>
......@@ -2,9 +2,9 @@
<title>CodeMirror: Autoresize Demo</title>
<meta charset="utf-8"/>
<link rel=stylesheet href="../doc/docs.css">
<link href="../doc/docs.css" rel=stylesheet>
<link rel="stylesheet" href="../lib/codemirror.css">
<link href="../lib/codemirror.css" rel="stylesheet">
<script src="../lib/codemirror.js"></script>
<script src="../mode/css/css.js"></script>
<style>
......@@ -12,7 +12,7 @@
border: 1px solid #eee;
height: auto;
}
</style>
</style>
<div id=nav>
<a href="https://codemirror.net"><h1>CodeMirror</h1><img id=logo src="../doc/logo.png"></a>
......@@ -27,19 +27,19 @@
</div>
<article>
<h2>Autoresize Demo</h2>
<form><textarea id="code" name="code">
<h2>Autoresize Demo</h2>
<form><textarea id="code" name="code">
.CodeMirror {
border: 1px solid #eee;
height: auto;
}
</textarea></form>
<p>By setting an editor's <code>height</code> style
to <code>auto</code> and giving
the <a href="../doc/manual.html#option_viewportMargin"><code>viewportMargin</code></a>
a value of <code>Infinity</code>, CodeMirror can be made to
automatically resize to fit its content.</p>
<p>By setting an editor's <code>height</code> style
to <code>auto</code> and giving
the <a href="../doc/manual.html#option_viewportMargin"><code>viewportMargin</code></a>
a value of <code>Infinity</code>, CodeMirror can be made to
automatically resize to fit its content.</p>
<script>
var editor = CodeMirror.fromTextArea(document.getElementById("code"), {
......@@ -48,4 +48,4 @@ automatically resize to fit its content.</p>
});
</script>
</article>
</article>
......@@ -2,13 +2,16 @@
<title>CodeMirror: Ruler Demo</title>
<meta charset="utf-8"/>
<link rel=stylesheet href="../doc/docs.css">
<link href="../doc/docs.css" rel=stylesheet>
<link rel="stylesheet" href="../lib/codemirror.css">
<link href="../lib/codemirror.css" rel="stylesheet">
<script src="../lib/codemirror.js"></script>
<script src="../addon/display/rulers.js"></script>
<style>
.CodeMirror {border-top: 1px solid #888; border-bottom: 1px solid #888;}
.CodeMirror {
border-top: 1px solid #888;
border-bottom: 1px solid #888;
}
</style>
<div id=nav>
<a href="https://codemirror.net"><h1>CodeMirror</h1><img id=logo src="../doc/logo.png"></a>
......@@ -24,9 +27,9 @@
</div>
<article>
<h2>Ruler Demo</h2>
<h2>Ruler Demo</h2>
<script>
<script>
var nums = "0123456789", space = " ";
var colors = ["#fcc", "#f5f577", "#cfc", "#aff", "#ccf", "#fcf"];
var rulers = [], value = "";
......@@ -39,11 +42,11 @@
rulers: rulers,
value: value + value + value,
lineNumbers: true
});
</script>
});
</script>
<p>Demonstration of
the <a href="../doc/manual.html#addon_rulers">rulers</a> addon, which
displays vertical lines at given column offsets.</p>
<p>Demonstration of
the <a href="../doc/manual.html#addon_rulers">rulers</a> addon, which
displays vertical lines at given column offsets.</p>
</article>
......@@ -2,9 +2,9 @@
<title>CodeMirror: Mode Runner Demo</title>
<meta charset="utf-8"/>
<link rel=stylesheet href="../doc/docs.css">
<link href="../doc/docs.css" rel=stylesheet>
<link rel="stylesheet" href="../lib/codemirror.css">
<link href="../lib/codemirror.css" rel="stylesheet">
<script src="../addon/runmode/runmode-standalone.js"></script>
<script src="../mode/xml/xml.js"></script>
<div id=nav>
......@@ -21,28 +21,29 @@
</div>
<article>
<h2>Mode Runner Demo</h2>
<h2>Mode Runner Demo</h2>
<textarea id="code" style="width: 90%; height: 7em; border: 1px solid black; padding: .2em .4em;">
<foobar>
<blah>Enter your xml here and press the button below to display
it as highlighted by the CodeMirror XML mode</blah>
<tag2 foo="2" bar="&amp;quot;bar&amp;quot;"/>
<tag2 bar="&amp;quot;bar&amp;quot;" foo="2"/>
</foobar></textarea><br>
<button onclick="doHighlight();">Highlight!</button>
<pre id="output" class="cm-s-default"></pre>
<pre class="cm-s-default" id="output"></pre>
<script>
function doHighlight() {
function doHighlight() {
CodeMirror.runMode(document.getElementById("code").value, "application/xml",
document.getElementById("output"));
}
</script>
}
</script>
<p>Running a CodeMirror mode outside of the editor.
The <code>CodeMirror.runMode</code> function, defined
in <code><a href="../addon/runmode/runmode.js">addon/runmode/runmode.js</a></code> takes the following arguments:</p>
in <code><a href="../addon/runmode/runmode.js">addon/runmode/runmode.js</a></code> takes the following arguments:
</p>
<dl>
<dt><code>text (string)</code></dt>
......@@ -55,7 +56,8 @@ function doHighlight() {
be <code>null</code> for unstyled tokens). If it is a DOM node,
the tokens will be converted to <code>span</code> elements as in
an editor, and inserted into the node
(through <code>innerHTML</code>).</dd>
(through <code>innerHTML</code>).
</dd>
</dl>
</article>
</article>
......@@ -2,9 +2,9 @@
<title>CodeMirror: Mode Runner Demo</title>
<meta charset="utf-8"/>
<link rel=stylesheet href="../doc/docs.css">
<link href="../doc/docs.css" rel=stylesheet>
<link rel="stylesheet" href="../lib/codemirror.css">
<link href="../lib/codemirror.css" rel="stylesheet">
<script src="../lib/codemirror.js"></script>
<script src="../addon/runmode/runmode.js"></script>
<script src="../mode/xml/xml.js"></script>
......@@ -22,28 +22,29 @@
</div>
<article>
<h2>Mode Runner Demo</h2>
<h2>Mode Runner Demo</h2>
<textarea id="code" style="width: 90%; height: 7em; border: 1px solid black; padding: .2em .4em;">
<foobar>
<blah>Enter your xml here and press the button below to display
it as highlighted by the CodeMirror XML mode</blah>
<tag2 foo="2" bar="&amp;quot;bar&amp;quot;"/>
<tag2 bar="&amp;quot;bar&amp;quot;" foo="2"/>
</foobar></textarea><br>
<button onclick="doHighlight();">Highlight!</button>
<pre id="output" class="cm-s-default"></pre>
<pre class="cm-s-default" id="output"></pre>
<script>
function doHighlight() {
function doHighlight() {
CodeMirror.runMode(document.getElementById("code").value, "application/xml",
document.getElementById("output"));
}
</script>
}
</script>
<p>Running a CodeMirror mode outside of the editor.
The <code>CodeMirror.runMode</code> function, defined
in <code><a href="../addon/runmode/runmode.js">addon/runmode/runmode.js</a></code> takes the following arguments:</p>
in <code><a href="../addon/runmode/runmode.js">addon/runmode/runmode.js</a></code> takes the following arguments:
</p>
<dl>
<dt><code>text (string)</code></dt>
......@@ -56,7 +57,8 @@ function doHighlight() {
be <code>null</code> for unstyled tokens). If it is a DOM node,
the tokens will be converted to <code>span</code> elements as in
an editor, and inserted into the node
(through <code>innerHTML</code>).</dd>
(through <code>innerHTML</code>).
</dd>
</dl>
</article>
</article>
......@@ -2,11 +2,11 @@
<title>CodeMirror: Search/Replace Demo</title>
<meta charset="utf-8"/>
<link rel=stylesheet href="../doc/docs.css">
<link href="../doc/docs.css" rel=stylesheet>
<link rel="stylesheet" href="../lib/codemirror.css">
<link rel="stylesheet" href="../addon/dialog/dialog.css">
<link rel="stylesheet" href="../addon/search/matchesonscrollbar.css">
<link href="../lib/codemirror.css" rel="stylesheet">
<link href="../addon/dialog/dialog.css" rel="stylesheet">
<link href="../addon/search/matchesonscrollbar.css" rel="stylesheet">
<script src="../lib/codemirror.js"></script>
<script src="../mode/xml/xml.js"></script>
<script src="../addon/dialog/dialog.js"></script>
......@@ -16,9 +16,16 @@
<script src="../addon/search/matchesonscrollbar.js"></script>
<script src="../addon/search/jump-to-line.js"></script>
<style>
.CodeMirror {border-top: 1px solid black; border-bottom: 1px solid black;}
dt {font-family: monospace; color: #666;}
</style>
.CodeMirror {
border-top: 1px solid black;
border-bottom: 1px solid black;
}
dt {
font-family: monospace;
color: #666;
}
</style>
<div id=nav>
<a href="https://codemirror.net"><h1>CodeMirror</h1><img id=logo src="../doc/logo.png"></a>
......@@ -33,8 +40,8 @@
</div>
<article>
<h2>Search/Replace Demo</h2>
<form><textarea id="code" name="code">
<h2>Search/Replace Demo</h2>
<form><textarea id="code" name="code">
<dl>
<dt id="option_indentWithTabs"><code><strong>indentWithTabs</strong>: boolean</code></dt>
<dd>Whether, when indenting, the first N*<code>tabSize</code>
......@@ -70,24 +77,32 @@
</textarea></form>
<script>
var editor = CodeMirror.fromTextArea(document.getElementById("code"), {
var editor = CodeMirror.fromTextArea(document.getElementById("code"), {
mode: "text/html",
lineNumbers: true,
extraKeys: {"Alt-F": "findPersistent"}
});
</script>
});
</script>
<p>Demonstration of primitive search/replace functionality. The
keybindings (which can be configured with custom keymaps) are:</p>
<dl>
<dt>Ctrl-F / Cmd-F</dt><dd>Start searching</dd>
<dt>Ctrl-G / Cmd-G</dt><dd>Find next</dd>
<dt>Shift-Ctrl-G / Shift-Cmd-G</dt><dd>Find previous</dd>
<dt>Shift-Ctrl-F / Cmd-Option-F</dt><dd>Replace</dd>
<dt>Shift-Ctrl-R / Shift-Cmd-Option-F</dt><dd>Replace all</dd>
<dt>Alt-F</dt><dd>Persistent search (dialog doesn't autoclose,
enter to find next, Shift-Enter to find previous)</dd>
<dt>Alt-G</dt><dd>Jump to line</dd>
<dt>Ctrl-F / Cmd-F</dt>
<dd>Start searching</dd>
<dt>Ctrl-G / Cmd-G</dt>
<dd>Find next</dd>
<dt>Shift-Ctrl-G / Shift-Cmd-G</dt>
<dd>Find previous</dd>
<dt>Shift-Ctrl-F / Cmd-Option-F</dt>
<dd>Replace</dd>
<dt>Shift-Ctrl-R / Shift-Cmd-Option-F</dt>
<dd>Replace all</dd>
<dt>Alt-F</dt>
<dd>Persistent search (dialog doesn't autoclose,
enter to find next, Shift-Enter to find previous)
</dd>
<dt>Alt-G</dt>
<dd>Jump to line</dd>
</dl>
<p>Searching is enabled by
including <a href="../addon/search/search.js">addon/search/search.js</a>
......@@ -96,4 +111,4 @@ var editor = CodeMirror.fromTextArea(document.getElementById("code"), {
<p>For good-looking input dialogs, you also want to include
<a href="../addon/dialog/dialog.js">addon/dialog/dialog.js</a>
and <a href="../addon/dialog/dialog.css">addon/dialog/dialog.css</a>.</p>
</article>
</article>
......@@ -2,17 +2,32 @@
<title>CodeMirror: Simple Mode Demo</title>
<meta charset="utf-8"/>
<link rel=stylesheet href="../doc/docs.css">
<link href="../doc/docs.css" rel=stylesheet>
<link rel="stylesheet" href="../lib/codemirror.css">
<link href="../lib/codemirror.css" rel="stylesheet">
<script src="../lib/codemirror.js"></script>
<script src="../addon/mode/simple.js"></script>
<script src="../mode/xml/xml.js"></script>
<style>
.CodeMirror {border: 1px solid silver; margin-bottom: 1em; }
dt { text-indent: -2em; padding-left: 2em; margin-top: 1em; }
dd { margin-left: 1.5em; margin-bottom: 1em; }
dt {margin-top: 1em;}
.CodeMirror {
border: 1px solid silver;
margin-bottom: 1em;
}
dt {
text-indent: -2em;
padding-left: 2em;
margin-top: 1em;
}
dd {
margin-left: 1.5em;
margin-bottom: 1em;
}
dt {
margin-top: 1em;
}
</style>
<div id=nav>
......@@ -29,38 +44,38 @@
</div>
<article>
<h2>Simple Mode Demo</h2>
<p>The <a href="../addon/mode/simple.js"><code>mode/simple</code></a>
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 <a href="../doc/manual.html#modeapi">mode
interface</a>, but is a lot easier to get started with, and
sufficiently expressive for many simple language modes.</p>
<p>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.</p>
<p>Simple modes (loosely based on
the <a href="https://github.com/mozilla/skywriter/wiki/Common-JavaScript-Syntax-Highlighting-Specification">Common
JavaScript Syntax Highlighting Specification</a>, 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.</p>
<p>The <code>CodeMirror.defineSimpleMode(name, states)</code> 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).</p>
<div id="code"></div>
<p>Each state is an array of rules. A rule may have the following properties:</p>
<dl>
<h2>Simple Mode Demo</h2>
<p>The <a href="../addon/mode/simple.js"><code>mode/simple</code></a>
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 <a href="../doc/manual.html#modeapi">mode
interface</a>, but is a lot easier to get started with, and
sufficiently expressive for many simple language modes.</p>
<p>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.</p>
<p>Simple modes (loosely based on
the <a href="https://github.com/mozilla/skywriter/wiki/Common-JavaScript-Syntax-Highlighting-Specification">Common
JavaScript Syntax Highlighting Specification</a>, 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.</p>
<p>The <code>CodeMirror.defineSimpleMode(name, states)</code> 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).</p>
<div id="code"></div>
<p>Each state is an array of rules. A rule may have the following properties:</p>
<dl>
<dt><code><strong>regex</strong>: string | RegExp</code></dt>
<dd>The regular expression that matches the token. May be a string
or a regex object. When a regex, the <code>ignoreCase</code> flag
......@@ -68,7 +83,8 @@ highlighted by the mode shown in it).</p>
has to capture groups when the <code>token</code> property is
an array. If it captures groups, it must capture <em>all</em> of the string
(since JS provides no way to find out where a group matched).
Currently negative lookbehind assertion for regex is not supported, regardless of browser support.</dd>
Currently negative lookbehind assertion for regex is not supported, regardless of browser support.
</dd>
<dt><code><strong>token</strong></code>: string | array&lt;string&gt; | null</dt>
<dd>An optional token style. Multiple styles can be specified by
separating them with dots or spaces. When this property holds an array of token styles,
......@@ -78,72 +94,87 @@ highlighted by the mode shown in it).</p>
<dd>When true, this token will only match at the start of the line.
(The <code>^</code> regexp marker doesn't work as you'd expect in
this context because of limitations in JavaScript's RegExp
API.)</dd>
API.)
</dd>
<dt><code><strong>next</strong>: string</code></dt>
<dd>When a <code>next</code> property is present, the mode will
transfer to the state named by the property when the token is
encountered.</dd>
encountered.
</dd>
<dt><code><strong>push</strong>: string</code></dt>
<dd>Like <code>next</code>, 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 <code>pop</code> directive.</dd>
returned to with the <code>pop</code> directive.
</dd>
<dt><code><strong>pop</strong>: bool</code></dt>
<dd>When true, and there is another state on the state stack, will
cause the mode to pop that state off the stack and transition to
it.</dd>
it.
</dd>
<dt><code><strong>mode</strong>: {spec, end, persistent}</code></dt>
<dd>Can be used to embed another mode inside a mode. When present,
must hold an object with a <code>spec</code> property that describes
the embedded mode, and an optional <code>end</code> end property
that specifies the regexp that will end the extent of the mode. When
a <code>persistent</code> property is set (and true), the nested
mode's state will be preserved between occurrences of the mode.</dd>
mode's state will be preserved between occurrences of the mode.
</dd>
<dt><code><strong>indent</strong>: bool</code></dt>
<dd>When true, this token changes the indentation to be one unit
more than the current line's indentation.</dd>
more than the current line's indentation.
</dd>
<dt><code><strong>dedent</strong>: bool</code></dt>
<dd>When true, this token will pop one scope off the indentation
stack.</dd>
stack.
</dd>
<dt><code><strong>dedentIfLineStart</strong>: bool</code></dt>
<dd>If a token has its <code>dedent</code> 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.</dd>
</dl>
Set this property to false to prevent that behavior.
</dd>
</dl>
<p>The <code>meta</code> 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 <a href="../doc/manual.html#addon_comment"><code>lineComment</code></a>,
which sets the comment style for a mode. The simple mode addon also
recognizes a few such properties:</p>
<p>The <code>meta</code> 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 <a href="../doc/manual.html#addon_comment"><code>lineComment</code></a>,
which sets the comment style for a mode. The simple mode addon also
recognizes a few such properties:</p>
<dl>
<dl>
<dt><code><strong>dontIndentStates</strong>: array&lt;string&gt;</code></dt>
<dd>An array of states in which the mode's auto-indentation should
not take effect. Usually used for multi-line comment and string
states.</dd>
</dl>
states.
</dd>
</dl>
<script id="modecode">/* Example definition of a simple mode that understands a subset of
<script id="modecode">/* Example definition of a simple mode that understands a subset of
* JavaScript:
*/
CodeMirror.defineSimpleMode("simplemode", {
CodeMirror.defineSimpleMode("simplemode", {
// The start state contains the rules that are intially used
start: [
// The regex matches the token, the token property contains the type
{regex: /"(?:[^\\]|\\.)*?(?:"|$)/, token: "string"},
// You can match multiple tokens at once. Note that the captured
// groups must span the whole string in this case
{regex: /(function)(\s+)([a-z$][\w$]*)/,
token: ["keyword", null, "variable-2"]},
{
regex: /(function)(\s+)([a-z$][\w$]*)/,
token: ["keyword", null, "variable-2"]
},
// Rules are matched in the order in which they appear, so there is
// no ambiguity between this one and the one above
{regex: /(?:function|var|return|if|for|while|else|do|this)\b/,
token: "keyword"},
{
regex: /(?:function|var|return|if|for|while|else|do|this)\b/,
token: "keyword"
},
{regex: /true|false|null|undefined/, token: "atom"},
{regex: /0x[a-f\d]+|[-+]?(?:\.\d+|\d+\.?\d*)(?:e[-+]?\d+)?/i,
token: "number"},
{
regex: /0x[a-f\d]+|[-+]?(?:\.\d+|\d+\.?\d*)(?:e[-+]?\d+)?/i,
token: "number"
},
{regex: /\/\/.*/, token: "comment"},
{regex: /\/(?:[^\\]|\\.)*?\//, token: "variable-3"},
// A next property will cause the mode to move to a different state
......@@ -171,16 +202,16 @@ CodeMirror.defineSimpleMode("simplemode", {
dontIndentStates: ["comment"],
lineComment: "//"
}
});
</script>
});
</script>
<script>
var sc = document.getElementById("modecode");
var code = document.getElementById("code");
var editor = CodeMirror(code, {
<script>
var sc = document.getElementById("modecode");
var code = document.getElementById("code");
var editor = CodeMirror(code, {
value: (sc.textContent || sc.innerText || sc.innerHTML),
mode: "simplemode"
});
</script>
});
</script>
</article>
......@@ -2,10 +2,10 @@
<title>CodeMirror: Simple Scrollbar Demo</title>
<meta charset="utf-8"/>
<link rel=stylesheet href="../doc/docs.css">
<link href="../doc/docs.css" rel=stylesheet>
<link rel="stylesheet" href="../lib/codemirror.css">
<link rel="stylesheet" href="../addon/scroll/simplescrollbars.css">
<link href="../lib/codemirror.css" rel="stylesheet">
<link href="../addon/scroll/simplescrollbars.css" rel="stylesheet">
<script src="../lib/codemirror.js"></script>
<script src="../mode/markdown/markdown.js"></script>
<script src="../mode/xml/xml.js"></script>
......@@ -25,8 +25,8 @@
</div>
<article>
<h2>Simple Scrollbar Demo</h2>
<form><textarea id="code" name="code"># Custom Scrollbars
<h2>Simple Scrollbar Demo</h2>
<form><textarea id="code" name="code"># Custom Scrollbars
This is a piece of text that creates scrollbars
......@@ -67,11 +67,14 @@ maxime sem dapibus et eget, mi enim dignissim nec pretium, augue vehicula, volut
lobortis viverra, cum in sed, vivamus tellus. Libero at malesuada est vivamus leo tortor.
</textarea></form>
<p>The <a href="../doc/manual.html#addon_simplescrollbars"><code>simplescrollbars</code></a> addon defines two
styles of non-native scrollbars: <a href="javascript:editor.setOption('scrollbarStyle', 'simple')"><code>"simple"</code></a> and <a href="javascript:editor.setOption('scrollbarStyle', 'overlay')"><code>"overlay"</code></a> (click to try), which can be passed to
the <a href="../doc/manual.html#option_scrollbarStyle"><code>scrollbarStyle</code></a> option. These implement
the scrollbar using DOM elements, allowing more control over
its <a href="../addon/scroll/simplescrollbars.css">appearance</a>.</p>
<p>The <a href="../doc/manual.html#addon_simplescrollbars"><code>simplescrollbars</code></a> addon defines two
styles of non-native scrollbars: <a
href="javascript:editor.setOption('scrollbarStyle', 'simple')"><code>"simple"</code></a> and <a
href="javascript:editor.setOption('scrollbarStyle', 'overlay')"><code>"overlay"</code></a> (click to try), which
can be passed to
the <a href="../doc/manual.html#option_scrollbarStyle"><code>scrollbarStyle</code></a> option. These implement
the scrollbar using DOM elements, allowing more control over
its <a href="../addon/scroll/simplescrollbars.css">appearance</a>.</p>
<script>
var editor = CodeMirror.fromTextArea(document.getElementById("code"), {
......
......@@ -2,7 +2,7 @@
<title>CodeMirror: Automatically derive odd wrapping behavior for your browser</title>
<meta charset="utf-8"/>
<link rel=stylesheet href="../doc/docs.css">
<link href="../doc/docs.css" rel=stylesheet>
<div id=nav>
<a href="https://codemirror.net"><h1>CodeMirror</h1><img id=logo src="../doc/logo.png"></a>
......@@ -18,7 +18,7 @@
</div>
<article>
<h2>Automatically derive odd wrapping behavior for your browser</h2>
<h2>Automatically derive odd wrapping behavior for your browser</h2>
<p>This is a hack to automatically derive
......@@ -27,7 +27,7 @@
in <a href="../lib/codemirror.js"><code>lib/codemirror.js</code></a>
for some more details.</p>
<div style="white-space: pre-wrap; width: 50px;" id="area"></div>
<div id="area" style="white-space: pre-wrap; width: 50px;"></div>
<pre id="output"></pre>
<script id="script">
......@@ -46,6 +46,7 @@
}
var re = "";
function toREElt(str) {
if (str.length > 1) {
var invert = false;
......@@ -55,7 +56,9 @@
for (var i = 0; i < l; ++i) if (str.indexOf(chars.charAt(i)) == -1) newStr += chars.charAt(i);
str = newStr;
}
str = str.replace(/[\-\.\]\"\'\\\/\^a]/g, function(orig) { return orig == "a" ? "\\w" : "\\" + orig; });
str = str.replace(/[\-\.\]\"\'\\\/\^a]/g, function (orig) {
return orig == "a" ? "\\w" : "\\" + orig;
});
return "[" + (invert ? "^" : "") + str + "]";
} else if (str == "a") {
return "\\w";
......@@ -67,7 +70,7 @@
}
var newRE = "";
for (;;) {
for (; ;) {
var left = null;
for (var left in bad) break;
if (left == null) break;
......@@ -81,5 +84,6 @@
}
document.getElementById("output").appendChild(document.createTextNode("Your regexp is: " + (newRE || "^$")));
</script>
</article>
<
/script>
< /article>
......@@ -2,12 +2,12 @@
<title>CodeMirror: Sublime Text bindings demo</title>
<meta charset="utf-8"/>
<link rel=stylesheet href="../doc/docs.css">
<link href="../doc/docs.css" rel=stylesheet>
<link rel="stylesheet" href="../lib/codemirror.css">
<link rel="stylesheet" href="../addon/fold/foldgutter.css">
<link rel="stylesheet" href="../addon/dialog/dialog.css">
<link rel="stylesheet" href="../theme/monokai.css">
<link href="../lib/codemirror.css" rel="stylesheet">
<link href="../addon/fold/foldgutter.css" rel="stylesheet">
<link href="../addon/dialog/dialog.css" rel="stylesheet">
<link href="../theme/monokai.css" rel="stylesheet">
<script src="../lib/codemirror.js"></script>
<script src="../addon/search/searchcursor.js"></script>
<script src="../addon/search/search.js"></script>
......@@ -21,8 +21,16 @@
<script src="../mode/javascript/javascript.js"></script>
<script src="../keymap/sublime.js"></script>
<style>
.CodeMirror {border-top: 1px solid #eee; border-bottom: 1px solid #eee; line-height: 1.3; height: 500px}
.CodeMirror-linenumbers { padding: 0 8px; }
.CodeMirror {
border-top: 1px solid #eee;
border-bottom: 1px solid #eee;
line-height: 1.3;
height: 500px
}
.CodeMirror-linenumbers {
padding: 0 8px;
}
</style>
<div id=nav>
<a href="https://codemirror.net"><h1>CodeMirror</h1><img id=logo src="../doc/logo.png"></a>
......@@ -38,20 +46,20 @@
</div>
<article>
<h2>Sublime Text bindings demo</h2>
<h2>Sublime Text bindings demo</h2>
<p>The <code>sublime</code> keymap defines many Sublime Text-specific
bindings for CodeMirror. See the code below for an overview.</p>
<p>The <code>sublime</code> keymap defines many Sublime Text-specific
bindings for CodeMirror. See the code below for an overview.</p>
<p>Enable the keymap by
loading <a href="../keymap/sublime.js"><code>keymap/sublime.js</code></a>
and setting
the <a href="../doc/manual.html#option_keyMap"><code>keyMap</code></a>
option to <code>"sublime"</code>.</p>
<p>Enable the keymap by
loading <a href="../keymap/sublime.js"><code>keymap/sublime.js</code></a>
and setting
the <a href="../doc/manual.html#option_keyMap"><code>keyMap</code></a>
option to <code>"sublime"</code>.</p>
<p>(A lot of the search functionality is still missing.)
<p>(A lot of the search functionality is still missing.)
<script>
<script>
var value = "// The bindings defined specifically in the Sublime Text mode\nvar bindings = {\n";
var map = CodeMirror.keyMap.sublime;
for (var key in map) {
......@@ -72,6 +80,6 @@ option to <code>"sublime"</code>.</p>
theme: "monokai",
tabSize: 2
});
</script>
</script>
</article>
......@@ -2,12 +2,12 @@
<title>CodeMirror: Tern Demo</title>
<meta charset="utf-8"/>
<link rel=stylesheet href="../doc/docs.css">
<link href="../doc/docs.css" rel=stylesheet>
<link rel="stylesheet" href="../lib/codemirror.css">
<link rel="stylesheet" href="../addon/dialog/dialog.css">
<link rel="stylesheet" href="../addon/hint/show-hint.css">
<link rel="stylesheet" href="../addon/tern/tern.css">
<link href="../lib/codemirror.css" rel="stylesheet">
<link href="../addon/dialog/dialog.css" rel="stylesheet">
<link href="../addon/hint/show-hint.css" rel="stylesheet">
<link href="../addon/tern/tern.css" rel="stylesheet">
<script src="../lib/codemirror.js"></script>
<script src="../mode/javascript/javascript.js"></script>
<script src="../addon/dialog/dialog.js"></script>
......@@ -24,8 +24,10 @@
<script src="https://unpkg.com/tern/lib/infer.js"></script>
<script src="https://unpkg.com/tern/plugin/doc_comment.js"></script>
<style>
.CodeMirror {border: 1px solid #ddd;}
</style>
.CodeMirror {
border: 1px solid #ddd;
}
</style>
<div id=nav>
<a href="https://codemirror.net"><h1>CodeMirror</h1><img id=logo src="../doc/logo.png"></a>
......@@ -40,8 +42,8 @@
</div>
<article>
<h2>Tern Demo</h2>
<form><textarea id="code" name="code">// Use ctrl-space to complete something
<h2>Tern Demo</h2>
<form><textarea id="code" name="code">// Use ctrl-space to complete something
// Put the cursor in or after an expression, press ctrl-o to
// find its type
......@@ -78,28 +80,34 @@ var randomStr = myMod.randomElt(myMod.strList);
var randomInt = myMod.randomElt(myMod.intList);
</textarea></form>
<p>Demonstrates integration of <a href="http://ternjs.net/">Tern</a>
and CodeMirror. The following keys are bound:</p>
<dl>
<dt>Ctrl-Space</dt><dd>Autocomplete</dd>
<dt>Ctrl-O</dt><dd>Find docs for the expression at the cursor</dd>
<dt>Ctrl-I</dt><dd>Find type at cursor</dd>
<dt>Alt-.</dt><dd>Jump to definition (Alt-, to jump back)</dd>
<dt>Ctrl-Q</dt><dd>Rename variable</dd>
<dt>Ctrl-.</dt><dd>Select all occurrences of a variable</dd>
</dl>
<p>Documentation is sparse for now. See the top of
the <a href="../addon/tern/tern.js">script</a> for a rough API
overview.</p>
<script>
<p>Demonstrates integration of <a href="http://ternjs.net/">Tern</a>
and CodeMirror. The following keys are bound:</p>
<dl>
<dt>Ctrl-Space</dt>
<dd>Autocomplete</dd>
<dt>Ctrl-O</dt>
<dd>Find docs for the expression at the cursor</dd>
<dt>Ctrl-I</dt>
<dd>Find type at cursor</dd>
<dt>Alt-.</dt>
<dd>Jump to definition (Alt-, to jump back)</dd>
<dt>Ctrl-Q</dt>
<dd>Rename variable</dd>
<dt>Ctrl-.</dt>
<dd>Select all occurrences of a variable</dd>
</dl>
<p>Documentation is sparse for now. See the top of
the <a href="../addon/tern/tern.js">script</a> for a rough API
overview.</p>
<script>
function getURL(url, c) {
var xhr = new XMLHttpRequest();
xhr.open("get", url, true);
xhr.send();
xhr.onreadystatechange = function() {
xhr.onreadystatechange = function () {
if (xhr.readyState != 4) return;
if (xhr.status < 400) return c(null, xhr.responseText);
var e = new Error(xhr.responseText || "No response");
......@@ -109,25 +117,41 @@ overview.</p>
}
var server;
getURL("https://unpkg.com/tern/defs/ecmascript.json", function(err, code) {
getURL("https://unpkg.com/tern/defs/ecmascript.json", function (err, code) {
if (err) throw new Error("Request for ecmascript.json: " + err);
server = new CodeMirror.TernServer({defs: [JSON.parse(code)]});
editor.setOption("extraKeys", {
"Ctrl-Space": function(cm) { server.complete(cm); },
"Ctrl-I": function(cm) { server.showType(cm); },
"Ctrl-O": function(cm) { server.showDocs(cm); },
"Alt-.": function(cm) { server.jumpToDef(cm); },
"Alt-,": function(cm) { server.jumpBack(cm); },
"Ctrl-Q": function(cm) { server.rename(cm); },
"Ctrl-.": function(cm) { server.selectName(cm); }
"Ctrl-Space": function (cm) {
server.complete(cm);
},
"Ctrl-I": function (cm) {
server.showType(cm);
},
"Ctrl-O": function (cm) {
server.showDocs(cm);
},
"Alt-.": function (cm) {
server.jumpToDef(cm);
},
"Alt-,": function (cm) {
server.jumpBack(cm);
},
"Ctrl-Q": function (cm) {
server.rename(cm);
},
"Ctrl-.": function (cm) {
server.selectName(cm);
}
})
editor.on("cursorActivity", function(cm) { server.updateArgHints(cm); });
editor.on("cursorActivity", function (cm) {
server.updateArgHints(cm);
});
});
var editor = CodeMirror.fromTextArea(document.getElementById("code"), {
lineNumbers: true,
mode: "javascript"
});
</script>
</script>
</article>
</article>
......@@ -2,77 +2,80 @@
<title>CodeMirror: Theme Demo</title>
<meta charset="utf-8"/>
<link rel=stylesheet href="../doc/docs.css">
<link href="../doc/docs.css" rel=stylesheet>
<link rel="stylesheet" href="../lib/codemirror.css">
<link rel="stylesheet" href="../theme/3024-day.css">
<link rel="stylesheet" href="../theme/3024-night.css">
<link rel="stylesheet" href="../theme/abcdef.css">
<link rel="stylesheet" href="../theme/ambiance.css">
<link rel="stylesheet" href="../theme/ayu-dark.css">
<link rel="stylesheet" href="../theme/ayu-mirage.css">
<link rel="stylesheet" href="../theme/base16-dark.css">
<link rel="stylesheet" href="../theme/bespin.css">
<link rel="stylesheet" href="../theme/base16-light.css">
<link rel="stylesheet" href="../theme/blackboard.css">
<link rel="stylesheet" href="../theme/cobalt.css">
<link rel="stylesheet" href="../theme/colorforth.css">
<link rel="stylesheet" href="../theme/dracula.css">
<link rel="stylesheet" href="../theme/duotone-dark.css">
<link rel="stylesheet" href="../theme/duotone-light.css">
<link rel="stylesheet" href="../theme/eclipse.css">
<link rel="stylesheet" href="../theme/elegant.css">
<link rel="stylesheet" href="../theme/erlang-dark.css">
<link rel="stylesheet" href="../theme/gruvbox-dark.css">
<link rel="stylesheet" href="../theme/hopscotch.css">
<link rel="stylesheet" href="../theme/icecoder.css">
<link rel="stylesheet" href="../theme/isotope.css">
<link rel="stylesheet" href="../theme/lesser-dark.css">
<link rel="stylesheet" href="../theme/liquibyte.css">
<link rel="stylesheet" href="../theme/lucario.css">
<link rel="stylesheet" href="../theme/material.css">
<link rel="stylesheet" href="../theme/material-darker.css">
<link rel="stylesheet" href="../theme/material-palenight.css">
<link rel="stylesheet" href="../theme/material-ocean.css">
<link rel="stylesheet" href="../theme/mbo.css">
<link rel="stylesheet" href="../theme/mdn-like.css">
<link rel="stylesheet" href="../theme/midnight.css">
<link rel="stylesheet" href="../theme/monokai.css">
<link rel="stylesheet" href="../theme/moxer.css">
<link rel="stylesheet" href="../theme/neat.css">
<link rel="stylesheet" href="../theme/neo.css">
<link rel="stylesheet" href="../theme/night.css">
<link rel="stylesheet" href="../theme/nord.css">
<link rel="stylesheet" href="../theme/oceanic-next.css">
<link rel="stylesheet" href="../theme/panda-syntax.css">
<link rel="stylesheet" href="../theme/paraiso-dark.css">
<link rel="stylesheet" href="../theme/paraiso-light.css">
<link rel="stylesheet" href="../theme/pastel-on-dark.css">
<link rel="stylesheet" href="../theme/railscasts.css">
<link rel="stylesheet" href="../theme/rubyblue.css">
<link rel="stylesheet" href="../theme/seti.css">
<link rel="stylesheet" href="../theme/shadowfox.css">
<link rel="stylesheet" href="../theme/solarized.css">
<link rel="stylesheet" href="../theme/the-matrix.css">
<link rel="stylesheet" href="../theme/tomorrow-night-bright.css">
<link rel="stylesheet" href="../theme/tomorrow-night-eighties.css">
<link rel="stylesheet" href="../theme/ttcn.css">
<link rel="stylesheet" href="../theme/twilight.css">
<link rel="stylesheet" href="../theme/vibrant-ink.css">
<link rel="stylesheet" href="../theme/xq-dark.css">
<link rel="stylesheet" href="../theme/xq-light.css">
<link rel="stylesheet" href="../theme/yeti.css">
<link rel="stylesheet" href="../theme/idea.css">
<link rel="stylesheet" href="../theme/darcula.css">
<link rel="stylesheet" href="../theme/yonce.css">
<link rel="stylesheet" href="../theme/zenburn.css">
<link href="../lib/codemirror.css" rel="stylesheet">
<link href="../theme/3024-day.css" rel="stylesheet">
<link href="../theme/3024-night.css" rel="stylesheet">
<link href="../theme/abcdef.css" rel="stylesheet">
<link href="../theme/ambiance.css" rel="stylesheet">
<link href="../theme/ayu-dark.css" rel="stylesheet">
<link href="../theme/ayu-mirage.css" rel="stylesheet">
<link href="../theme/base16-dark.css" rel="stylesheet">
<link href="../theme/bespin.css" rel="stylesheet">
<link href="../theme/base16-light.css" rel="stylesheet">
<link href="../theme/blackboard.css" rel="stylesheet">
<link href="../theme/cobalt.css" rel="stylesheet">
<link href="../theme/colorforth.css" rel="stylesheet">
<link href="../theme/dracula.css" rel="stylesheet">
<link href="../theme/duotone-dark.css" rel="stylesheet">
<link href="../theme/duotone-light.css" rel="stylesheet">
<link href="../theme/eclipse.css" rel="stylesheet">
<link href="../theme/elegant.css" rel="stylesheet">
<link href="../theme/erlang-dark.css" rel="stylesheet">
<link href="../theme/gruvbox-dark.css" rel="stylesheet">
<link href="../theme/hopscotch.css" rel="stylesheet">
<link href="../theme/icecoder.css" rel="stylesheet">
<link href="../theme/isotope.css" rel="stylesheet">
<link href="../theme/lesser-dark.css" rel="stylesheet">
<link href="../theme/liquibyte.css" rel="stylesheet">
<link href="../theme/lucario.css" rel="stylesheet">
<link href="../theme/material.css" rel="stylesheet">
<link href="../theme/material-darker.css" rel="stylesheet">
<link href="../theme/material-palenight.css" rel="stylesheet">
<link href="../theme/material-ocean.css" rel="stylesheet">
<link href="../theme/mbo.css" rel="stylesheet">
<link href="../theme/mdn-like.css" rel="stylesheet">
<link href="../theme/midnight.css" rel="stylesheet">
<link href="../theme/monokai.css" rel="stylesheet">
<link href="../theme/moxer.css" rel="stylesheet">
<link href="../theme/neat.css" rel="stylesheet">
<link href="../theme/neo.css" rel="stylesheet">
<link href="../theme/night.css" rel="stylesheet">
<link href="../theme/nord.css" rel="stylesheet">
<link href="../theme/oceanic-next.css" rel="stylesheet">
<link href="../theme/panda-syntax.css" rel="stylesheet">
<link href="../theme/paraiso-dark.css" rel="stylesheet">
<link href="../theme/paraiso-light.css" rel="stylesheet">
<link href="../theme/pastel-on-dark.css" rel="stylesheet">
<link href="../theme/railscasts.css" rel="stylesheet">
<link href="../theme/rubyblue.css" rel="stylesheet">
<link href="../theme/seti.css" rel="stylesheet">
<link href="../theme/shadowfox.css" rel="stylesheet">
<link href="../theme/solarized.css" rel="stylesheet">
<link href="../theme/the-matrix.css" rel="stylesheet">
<link href="../theme/tomorrow-night-bright.css" rel="stylesheet">
<link href="../theme/tomorrow-night-eighties.css" rel="stylesheet">
<link href="../theme/ttcn.css" rel="stylesheet">
<link href="../theme/twilight.css" rel="stylesheet">
<link href="../theme/vibrant-ink.css" rel="stylesheet">
<link href="../theme/xq-dark.css" rel="stylesheet">
<link href="../theme/xq-light.css" rel="stylesheet">
<link href="../theme/yeti.css" rel="stylesheet">
<link href="../theme/idea.css" rel="stylesheet">
<link href="../theme/darcula.css" rel="stylesheet">
<link href="../theme/yonce.css" rel="stylesheet">
<link href="../theme/zenburn.css" rel="stylesheet">
<script src="../lib/codemirror.js"></script>
<script src="../mode/javascript/javascript.js"></script>
<script src="../addon/selection/active-line.js"></script>
<script src="../addon/edit/matchbrackets.js"></script>
<style>
.CodeMirror {border: 1px solid black; font-size:13px}
</style>
.CodeMirror {
border: 1px solid black;
font-size: 13px
}
</style>
<div id=nav>
<a href="https://codemirror.net"><h1>CodeMirror</h1><img id=logo src="../doc/logo.png"></a>
......@@ -87,8 +90,8 @@
</div>
<article>
<h2>Theme Demo</h2>
<form><textarea id="code" name="code">
<h2>Theme Demo</h2>
<form><textarea id="code" name="code">
function findSequence(goal) {
function find(start, history) {
if (start == goal)
......@@ -102,7 +105,7 @@ function findSequence(goal) {
return find(1, "1");
}</textarea></form>
<p>Select a theme: <select onchange="selectTheme()" id=select>
<p>Select a theme: <select id=select onchange="selectTheme()">
<option selected>default</option>
<option>3024-day</option>
<option>3024-night</option>
......@@ -166,21 +169,23 @@ function findSequence(goal) {
<option>yeti</option>
<option>yonce</option>
<option>zenburn</option>
</select>
</p>
</select>
</p>
<script>
<script>
var editor = CodeMirror.fromTextArea(document.getElementById("code"), {
lineNumbers: true,
styleActiveLine: true,
matchBrackets: true
});
var input = document.getElementById("select");
function selectTheme() {
var theme = input.options[input.selectedIndex].textContent;
editor.setOption("theme", theme);
location.hash = "#" + theme;
}
var choice = (location.hash && location.hash.slice(1)) ||
(document.location.search &&
decodeURIComponent(document.location.search.slice(1)));
......@@ -188,9 +193,12 @@ function findSequence(goal) {
input.value = choice;
editor.setOption("theme", choice);
}
CodeMirror.on(window, "hashchange", function() {
CodeMirror.on(window, "hashchange", function () {
var theme = location.hash.slice(1);
if (theme) { input.value = theme; selectTheme(); }
if (theme) {
input.value = theme;
selectTheme();
}
});
</script>
</script>
</article>
......@@ -2,19 +2,23 @@
<title>CodeMirror: Trailing Whitespace Demo</title>
<meta charset="utf-8"/>
<link rel=stylesheet href="../doc/docs.css">
<link href="../doc/docs.css" rel=stylesheet>
<link rel="stylesheet" href="../lib/codemirror.css">
<link href="../lib/codemirror.css" rel="stylesheet">
<script src="../lib/codemirror.js"></script>
<script src="../addon/edit/trailingspace.js"></script>
<style>
.CodeMirror {border-top: 1px solid black; border-bottom: 1px solid black;}
.CodeMirror {
border-top: 1px solid black;
border-bottom: 1px solid black;
}
.cm-trailingspace {
background-image: url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAQAAAACCAYAAAB/qH1jAAAABmJLR0QA/wD/AP+gvaeTAAAACXBIWXMAAAsTAAALEwEAmpwYAAAAB3RJTUUH3QUXCToH00Y1UgAAACFJREFUCNdjPMDBUc/AwNDAAAFMTAwMDA0OP34wQgX/AQBYgwYEx4f9lQAAAABJRU5ErkJggg==);
background-position: bottom left;
background-repeat: repeat-x;
}
</style>
</style>
<div id=nav>
<a href="https://codemirror.net"><h1>CodeMirror</h1><img id=logo src="../doc/logo.png"></a>
......@@ -29,20 +33,20 @@
</div>
<article>
<h2>Trailing Whitespace Demo</h2>
<form><textarea id="code" name="code">This text
<h2>Trailing Whitespace Demo</h2>
<form><textarea id="code" name="code">This text
has some
trailing whitespace!</textarea></form>
<script>
var editor = CodeMirror.fromTextArea(document.getElementById("code"), {
var editor = CodeMirror.fromTextArea(document.getElementById("code"), {
lineNumbers: true,
showTrailingSpace: true
});
</script>
});
</script>
<p>Uses
the <a href="../doc/manual.html#addon_trailingspace">trailingspace</a>
addon to highlight trailing whitespace.</p>
<p>Uses
the <a href="../doc/manual.html#addon_trailingspace">trailingspace</a>
addon to highlight trailing whitespace.</p>
</article>
</article>
......@@ -2,23 +2,50 @@
<title>CodeMirror: Variable Height Demo</title>
<meta charset="utf-8"/>
<link rel=stylesheet href="../doc/docs.css">
<link href="../doc/docs.css" rel=stylesheet>
<link rel="stylesheet" href="../lib/codemirror.css">
<link href="../lib/codemirror.css" rel="stylesheet">
<script src="../lib/codemirror.js"></script>
<script src="../mode/markdown/markdown.js"></script>
<script src="../mode/xml/xml.js"></script>
<style>
.CodeMirror {border: 1px solid silver; border-width: 1px 2px; }
.cm-header { font-family: arial; }
.cm-header-1 { font-size: 150%; }
.cm-header-2 { font-size: 130%; }
.cm-header-3 { font-size: 120%; }
.cm-header-4 { font-size: 110%; }
.cm-header-5 { font-size: 100%; }
.cm-header-6 { font-size: 90%; }
.cm-strong { font-size: 140%; }
</style>
.CodeMirror {
border: 1px solid silver;
border-width: 1px 2px;
}
.cm-header {
font-family: arial;
}
.cm-header-1 {
font-size: 150%;
}
.cm-header-2 {
font-size: 130%;
}
.cm-header-3 {
font-size: 120%;
}
.cm-header-4 {
font-size: 110%;
}
.cm-header-5 {
font-size: 100%;
}
.cm-header-6 {
font-size: 90%;
}
.cm-strong {
font-size: 140%;
}
</style>
<div id=nav>
<a href="https://codemirror.net"><h1>CodeMirror</h1><img id=logo src="../doc/logo.png"></a>
......@@ -33,8 +60,8 @@
</div>
<article>
<h2>Variable Height Demo</h2>
<form><textarea id="code" name="code"># A First Level Header
<h2>Variable Height Demo</h2>
<form><textarea id="code" name="code"># A First Level Header
**Bold** text in a normal-size paragraph.
......@@ -64,4 +91,4 @@ dog's back.
mode: "markdown"
});
</script>
</article>
</article>
......@@ -2,12 +2,12 @@
<title>CodeMirror: Vim bindings demo</title>
<meta charset="utf-8"/>
<link rel=stylesheet href="../doc/docs.css">
<link href="../doc/docs.css" rel=stylesheet>
<link rel="stylesheet" href="../lib/codemirror.css">
<link rel="stylesheet" href="../addon/dialog/dialog.css">
<link rel="stylesheet" href="../theme/midnight.css">
<link rel="stylesheet" href="../theme/solarized.css">
<link href="../lib/codemirror.css" rel="stylesheet">
<link href="../addon/dialog/dialog.css" rel="stylesheet">
<link href="../theme/midnight.css" rel="stylesheet">
<link href="../theme/solarized.css" rel="stylesheet">
<script src="../lib/codemirror.js"></script>
<script src="../addon/dialog/dialog.js"></script>
<script src="../addon/search/searchcursor.js"></script>
......@@ -15,8 +15,11 @@
<script src="../addon/edit/matchbrackets.js"></script>
<script src="../keymap/vim.js"></script>
<style>
.CodeMirror {border-top: 1px solid #eee; border-bottom: 1px solid #eee;}
</style>
.CodeMirror {
border-top: 1px solid #eee;
border-bottom: 1px solid #eee;
}
</style>
<div id=nav>
<a href="https://codemirror.net"><h1>CodeMirror</h1><img id=logo src="../doc/logo.png"></a>
......@@ -31,20 +34,20 @@
</div>
<article>
<h2>Vim bindings demo</h2>
<h2>Vim bindings demo</h2>
<p><strong style="color: #c33; text-decoration: none">Note:</strong> 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 <a href="https://github.com/codemirror/codemirror/issues?q=is%3Aissue+is%3Aopen+label%3Avim">the
open issues</a> and see which ones you can solve.</p>
<p><strong style="color: #c33; text-decoration: none">Note:</strong> 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 <a href="https://github.com/codemirror/codemirror/issues?q=is%3Aissue+is%3Aopen+label%3Avim">the
open issues</a> and see which ones you can solve.</p>
<form><textarea id="code" name="code">
<form><textarea id="code" name="code">
#include "syscalls.h"
/* getchar: simple buffered version */
int getchar(void)
{
{
static char buf[BUFSIZ];
static char *bufp = buf;
static int n = 0;
......@@ -55,16 +58,16 @@ int getchar(void)
return (--n >= 0) ? (unsigned char) *bufp++ : EOF;
}
</textarea></form>
<div style="font-size: 13px; width: 300px; height: 30px;">Key buffer: <span id="command-display"></span></div>
<div style="font-size: 13px; width: 300px; height: 30px;">Vim mode: <span id="vim-mode"></span></div>
<div style="font-size: 13px; width: 300px; height: 30px;">Key buffer: <span id="command-display"></span></div>
<div style="font-size: 13px; width: 300px; height: 30px;">Vim mode: <span id="vim-mode"></span></div>
<p>The vim keybindings are enabled by including <code><a
href="../keymap/vim.js">keymap/vim.js</a></code> and setting the
<code>keyMap</code> option to <code>vim</code>.</p>
<p>The vim keybindings are enabled by including <code><a
href="../keymap/vim.js">keymap/vim.js</a></code> and setting the
<code>keyMap</code> option to <code>vim</code>.</p>
<p><strong>Features</strong></p>
<p><strong>Features</strong></p>
<ul>
<ul>
<li>All common motions and operators, including text objects</li>
<li>Operator motion orthogonality</li>
<li>Visual mode - characterwise, linewise, blockwise</li>
......@@ -79,18 +82,20 @@ href="../keymap/vim.js">keymap/vim.js</a></code> and setting the
<li>:global</li>
<li>Insert mode behaves identical to base CodeMirror</li>
<li>Cross-buffer yank/paste</li>
</ul>
</ul>
<p>For the full list of key mappings and Ex commands, refer to the
<code>defaultKeymap</code> and <code>defaultExCommandMap</code> at the
top of <code><a href="../keymap/vim.js">keymap/vim.js</a></code>.
<p>For the full list of key mappings and Ex commands, refer to the
<code>defaultKeymap</code> and <code>defaultExCommandMap</code> at the
top of <code><a href="../keymap/vim.js">keymap/vim.js</a></code>.
<p>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</p>
<p>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</p>
<script>
CodeMirror.commands.save = function(){ alert("Saving"); };
CodeMirror.commands.save = function () {
alert("Saving");
};
var editor = CodeMirror.fromTextArea(document.getElementById("code"), {
lineNumbers: true,
mode: "text/x-csrc",
......@@ -100,18 +105,18 @@ become a complete vim implementation</p>
});
var commandDisplay = document.getElementById('command-display');
var keys = '';
CodeMirror.on(editor, 'vim-keypress', function(key) {
CodeMirror.on(editor, 'vim-keypress', function (key) {
keys = keys + key;
commandDisplay.innerText = keys;
});
CodeMirror.on(editor, 'vim-command-done', function(e) {
CodeMirror.on(editor, 'vim-command-done', function (e) {
keys = '';
commandDisplay.innerHTML = keys;
});
var vimMode = document.getElementById('vim-mode');
CodeMirror.on(editor, 'vim-mode-change', function(e) {
CodeMirror.on(editor, 'vim-mode-change', function (e) {
vimMode.innerText = JSON.stringify(e);
});
</script>
</article>
</article>
......@@ -2,19 +2,23 @@
<title>CodeMirror: Visible tabs demo</title>
<meta charset="utf-8"/>
<link rel=stylesheet href="../doc/docs.css">
<link href="../doc/docs.css" rel=stylesheet>
<link rel="stylesheet" href="../lib/codemirror.css">
<link href="../lib/codemirror.css" rel="stylesheet">
<script src="../lib/codemirror.js"></script>
<script src="../mode/clike/clike.js"></script>
<style>
.CodeMirror {border-top: 1px solid #eee; border-bottom: 1px solid #eee;}
.CodeMirror {
border-top: 1px solid #eee;
border-bottom: 1px solid #eee;
}
.cm-tab {
background: url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAADAAAAAMCAYAAAAkuj5RAAAAAXNSR0IArs4c6QAAAGFJREFUSMft1LsRQFAQheHPowAKoACx3IgEKtaEHujDjORSgWTH/ZOdnZOcM/sgk/kFFWY0qV8foQwS4MKBCS3qR6ixBJvElOobYAtivseIE120FaowJPN75GMu8j/LfMwNjh4HUpwg4LUAAAAASUVORK5CYII=);
background-position: right;
background-repeat: no-repeat;
}
</style>
</style>
<div id=nav>
<a href="https://codemirror.net"><h1>CodeMirror</h1><img id=logo src="../doc/logo.png"></a>
......@@ -29,12 +33,12 @@
</div>
<article>
<h2>Visible tabs demo</h2>
<form><textarea id="code" name="code">
<h2>Visible tabs demo</h2>
<form><textarea id="code" name="code">
#include "syscalls.h"
/* getchar: simple buffered version */
int getchar(void)
{
{
static char buf[BUFSIZ];
static char *bufp = buf;
static int n = 0;
......@@ -46,8 +50,8 @@ int getchar(void)
}
</textarea></form>
<p>Tabs inside the editor are spans with the
class <code>cm-tab</code>, and can be styled.</p>
<p>Tabs inside the editor are spans with the
class <code>cm-tab</code>, and can be styled.</p>
<script>
var editor = CodeMirror.fromTextArea(document.getElementById("code"), {
......@@ -59,4 +63,4 @@ class <code>cm-tab</code>, and can be styled.</p>
});
</script>
</article>
</article>
......@@ -2,17 +2,34 @@
<title>CodeMirror: Inline Widget Demo</title>
<meta charset="utf-8"/>
<link rel=stylesheet href="../doc/docs.css">
<link href="../doc/docs.css" rel=stylesheet>
<link rel="stylesheet" href="../lib/codemirror.css">
<link href="../lib/codemirror.css" rel="stylesheet">
<script src="../lib/codemirror.js"></script>
<script src="../mode/javascript/javascript.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/jshint/2.9.5/jshint.min.js"></script>
<style>
.CodeMirror {border: 1px solid black;}
.lint-error {font-family: arial; font-size: 70%; background: #ffa; color: #a00; padding: 2px 5px 3px; }
.lint-error-icon {color: white; background-color: red; font-weight: bold; border-radius: 50%; padding: 0 3px; margin-right: 7px;}
</style>
.CodeMirror {
border: 1px solid black;
}
.lint-error {
font-family: arial;
font-size: 70%;
background: #ffa;
color: #a00;
padding: 2px 5px 3px;
}
.lint-error-icon {
color: white;
background-color: red;
font-weight: bold;
border-radius: 50%;
padding: 0 3px;
margin-right: 7px;
}
</style>
<div id=nav>
<a href="https://codemirror.net"><h1>CodeMirror</h1><img id=logo src="../doc/logo.png"></a>
......@@ -27,13 +44,14 @@
</div>
<article>
<h2>Inline Widget Demo</h2>
<h2>Inline Widget Demo</h2>
<div id=code></div>
<script id="script">var widgets = []
function updateHints() {
editor.operation(function(){
function updateHints() {
editor.operation(function () {
for (var i = 0; i < widgets.length; ++i)
editor.removeLineWidget(widgets[i]);
widgets.length = 0;
......@@ -55,9 +73,9 @@ function updateHints() {
var after = editor.charCoords({line: editor.getCursor().line + 1, ch: 0}, "local").top;
if (info.top + info.clientHeight < after)
editor.scrollTo(null, after - info.clientHeight + 3);
}
}
window.onload = function() {
window.onload = function () {
var sc = document.getElementById("script");
var content = sc.textContent || sc.innerText || sc.innerHTML;
......@@ -68,18 +86,18 @@ window.onload = function() {
});
var waiting;
editor.on("change", function() {
editor.on("change", function () {
clearTimeout(waiting);
waiting = setTimeout(updateHints, 500);
});
setTimeout(updateHints, 100);
};
};
"long line to create a horizontal scrollbar, in order to test whether the (non-inline) widgets stay in place when scrolling to the right";
</script>
<p>This demo runs <a href="http://jshint.com">JSHint</a> over the code
in the editor (which is the script used on this page), and
inserts <a href="../doc/manual.html#addLineWidget">line widgets</a> to
display the warnings that JSHint comes up with.</p>
</article>
"long line to create a horizontal scrollbar, in order to test whether the (non-inline) widgets stay in place when scrolling to the right";
</script>
<p>This demo runs <a href="http://jshint.com">JSHint</a> over the code
in the editor (which is the script used on this page), and
inserts <a href="../doc/manual.html#addLineWidget">line widgets</a> to
display the warnings that JSHint comes up with.</p>
</article>
......@@ -2,17 +2,19 @@
<title>CodeMirror: XML Autocomplete Demo</title>
<meta charset="utf-8"/>
<link rel=stylesheet href="../doc/docs.css">
<link href="../doc/docs.css" rel=stylesheet>
<link rel="stylesheet" href="../lib/codemirror.css">
<link rel="stylesheet" href="../addon/hint/show-hint.css">
<link href="../lib/codemirror.css" rel="stylesheet">
<link href="../addon/hint/show-hint.css" rel="stylesheet">
<script src="../lib/codemirror.js"></script>
<script src="../addon/hint/show-hint.js"></script>
<script src="../addon/hint/xml-hint.js"></script>
<script src="../mode/xml/xml.js"></script>
<style>
.CodeMirror { border: 1px solid #eee; }
</style>
.CodeMirror {
border: 1px solid #eee;
}
</style>
<div id=nav>
<a href="https://codemirror.net"><h1>CodeMirror</h1><img id=logo src="../doc/logo.png"></a>
......@@ -27,8 +29,8 @@
</div>
<article>
<h2>XML Autocomplete Demo</h2>
<form><textarea id="code" name="code"><!-- write some xml below -->
<h2>XML Autocomplete Demo</h2>
<form><textarea id="code" name="code"><!-- write some xml below -->
</textarea></form>
<p>Press <strong>ctrl-space</strong>, or type a '&lt;' character to
......@@ -80,7 +82,7 @@
function completeAfter(cm, pred) {
var cur = cm.getCursor();
if (!pred || pred()) setTimeout(function() {
if (!pred || pred()) setTimeout(function () {
if (!cm.state.completionActive)
cm.showHint({completeSingle: false});
}, 100);
......@@ -88,14 +90,14 @@
}
function completeIfAfterLt(cm) {
return completeAfter(cm, function() {
return completeAfter(cm, function () {
var cur = cm.getCursor();
return cm.getRange(CodeMirror.Pos(cur.line, cur.ch - 1), cur) == "<";
});
}
function completeIfInTag(cm) {
return completeAfter(cm, function() {
return completeAfter(cm, function () {
var tok = cm.getTokenAt(cm.getCursor());
if (tok.type == "string" && (!/['"]/.test(tok.string.charAt(tok.string.length - 1)) || tok.string.length == 1)) return false;
var inner = CodeMirror.innerMode(cm.getMode(), tok.state).state;
......@@ -116,4 +118,4 @@
hintOptions: {schemaInfo: tags}
});
</script>
</article>
</article>
......@@ -2,7 +2,7 @@
document.createElement("section");
document.createElement("article");
(function() {
(function () {
if (!window.addEventListener) return;
var pending = false, prevVal = null;
......@@ -44,12 +44,15 @@ document.createElement("article");
window.addEventListener("scroll", updateSoon);
window.addEventListener("load", updateSoon);
window.addEventListener("hashchange", function() {
setTimeout(function() {
window.addEventListener("hashchange", function () {
setTimeout(function () {
var hash = document.location.hash, found = null, m;
var marks = document.getElementById("nav").getElementsByTagName("a");
for (var i = 0; i < marks.length; i++)
if ((m = marks[i].href.match(/(#.*)/)) && m[1] == hash) { found = i; break; }
if ((m = marks[i].href.match(/(#.*)/)) && m[1] == hash) {
found = i;
break;
}
if (found != null) for (var i = 0; i < marks.length; i++)
marks[i].className = i == found ? "active" : "";
}, 300);
......
......@@ -5,8 +5,16 @@
src: local('Source Sans Pro'), local('SourceSansPro-Regular'), url(//themes.googleusercontent.com/static/fonts/sourcesanspro/v5/ODelI1aHBYDBqgeIAH2zlBM0YzuT7MdOe03otPbuUS0.woff) format('woff');
}
body, html { margin: 0; padding: 0; height: 100%; }
section, article { display: block; padding: 0; }
body, html {
margin: 0;
padding: 0;
height: 100%;
}
section, article {
display: block;
padding: 0;
}
body {
background: #f8f8f8;
......@@ -14,16 +22,30 @@ body {
line-height: 1.5;
}
p { margin-top: 0; }
p {
margin-top: 0;
}
h2, h3, h1 {
font-weight: normal;
margin-bottom: .7em;
}
h1 { font-size: 140%; }
h2 { font-size: 120%; }
h3 { font-size: 110%; }
article > h2:first-child, section:first-child > h2 { margin-top: 0; }
h1 {
font-size: 140%;
}
h2 {
font-size: 120%;
}
h3 {
font-size: 110%;
}
article > h2:first-child, section:first-child > h2 {
margin-top: 0;
}
#nav h1 {
margin-right: 12px;
......@@ -66,7 +88,8 @@ article {
box-sizing: -moz-border-box;
box-sizing: border-box;
overflow-y: auto;
left: 0; right: none;
left: 0;
right: none;
width: 160px;
text-align: right;
z-index: 1;
......@@ -76,6 +99,7 @@ article {
article {
margin: 0 auto;
}
#nav {
right: 50%;
width: auto;
......@@ -85,7 +109,8 @@ article {
#nav ul {
display: block;
margin: 0; padding: 0;
margin: 0;
padding: 0;
margin-bottom: 32px;
}
......@@ -154,7 +179,8 @@ section.first {
.yinyang {
position: absolute;
top: -10px;
left: 0; right: 0;
left: 0;
right: 0;
margin: auto;
display: block;
height: 120px;
......@@ -170,7 +196,9 @@ section.first {
pointer-events: none;
position: absolute;
height: 100px;
top: 0; left: 0; right: 0;
top: 0;
left: 0;
right: 0;
}
.actionlink {
......@@ -179,7 +207,8 @@ section.first {
font-size: 80%;
font-weight: bold;
position: absolute;
top: 0; bottom: 0;
top: 0;
bottom: 0;
line-height: 1;
height: 1em;
margin: auto;
......@@ -219,6 +248,7 @@ section.first {
.actions {
padding-top: 120px;
}
.actionsleft, .actionsright {
float: none;
text-align: left;
......@@ -250,6 +280,7 @@ th {
.rel {
margin-bottom: 0;
}
.rel-note {
margin-top: 0;
color: #555;
......
......@@ -2,8 +2,14 @@
<title>CodeMirror: Internals</title>
<meta charset="utf-8"/>
<link rel=stylesheet href="docs.css">
<style>dl dl {margin: 0;} .update {color: #d40 !important}</style>
<link href="docs.css" rel=stylesheet>
<style>dl dl {
margin: 0;
}
.update {
color: #d40 !important
}</style>
<script src="activebookmark.js"></script>
<div id=nav>
......@@ -29,476 +35,482 @@
<article>
<h2 id=top>(Re-) Implementing A Syntax-Highlighting Editor in JavaScript</h2>
<h2 id=top>(Re-) Implementing A Syntax-Highlighting Editor in JavaScript</h2>
<p style="font-size: 85%" id="intro">
<p id="intro" style="font-size: 85%">
<strong>Topic:</strong> JavaScript, code editor implementation<br>
<strong>Author:</strong> Marijn Haverbeke<br>
<strong>Date:</strong> March 2nd 2011 (updated November 13th 2011)
</p>
<p style="padding: 0 3em 0 2em"><strong>Caution</strong>: 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 <a href="http://marijnhaverbeke.nl/blog/#cm-internals">cm-internals</a>
on my blog.</p>
<p>This is a followup to
my <a href="https://codemirror.net/story.html">Brutal Odyssey to the
Dark Side of the DOM Tree</a> 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.</p>
<p>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 <a href="http://googlecode.blogspot.com/2011/01/make-quick-fixes-quicker-on-google.html">Google
code's project hosting</a>. It works, and it's being used widely.</p>
<p>Still, I did not start replacing it because I was bored. CodeMirror
1 was heavily reliant on <code>designMode</code>
or <code>contentEditable</code> (depending on the browser). Neither of
these are well specified (HTML5 tries
to <a href="http://www.w3.org/TR/html5/editing.html#contenteditable">specify</a>
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.</p>
<p>Also, there is the fact that <code>designMode</code> (which seemed
to be less buggy than <code>contentEditable</code> 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.</p>
<p>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
<code>document.selection</code>, 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.</p>
<p>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 <a href="https://codemirror.net/story.html">story</a>), things
also kept breaking in newly released versions, requiring me to come up
with <em>new</em> scary hacks in order to keep up. This was starting
to lose its appeal.</p>
<section id=approach>
</p>
<p style="padding: 0 3em 0 2em"><strong>Caution</strong>: 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 <a href="http://marijnhaverbeke.nl/blog/#cm-internals">cm-internals</a>
on my blog.</p>
<p>This is a followup to
my <a href="https://codemirror.net/story.html">Brutal Odyssey to the
Dark Side of the DOM Tree</a> 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.</p>
<p>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 <a href="http://googlecode.blogspot.com/2011/01/make-quick-fixes-quicker-on-google.html">Google
code's project hosting</a>. It works, and it's being used widely.</p>
<p>Still, I did not start replacing it because I was bored. CodeMirror
1 was heavily reliant on <code>designMode</code>
or <code>contentEditable</code> (depending on the browser). Neither of
these are well specified (HTML5 tries
to <a href="http://www.w3.org/TR/html5/editing.html#contenteditable">specify</a>
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.</p>
<p>Also, there is the fact that <code>designMode</code> (which seemed
to be less buggy than <code>contentEditable</code> 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.</p>
<p>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
<code>document.selection</code>, 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.</p>
<p>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 <a href="https://codemirror.net/story.html">story</a>), things
also kept breaking in newly released versions, requiring me to come up
with <em>new</em> scary hacks in order to keep up. This was starting
to lose its appeal.</p>
<section id=approach>
<h2>General Approach</h2>
<p>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
<a href="http://ace.ajax.org">ACE</a> editor for inspiration on how to
approach this.</p>
<p>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 <a href="http://ymacs.org">Ymacs</a>) 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. <a href="#keymap" class="update">[see below for caveat]</a></p>
<p>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.</p>
<p>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 <code>onscroll</code>
event works almost the same on all browsers, and lends itself well to
displaying things only as they are scrolled into view.)</p>
</section>
<section id="input">
<p>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
<a href="http://ace.ajax.org">ACE</a> editor for inspiration on how to
approach this.</p>
<p>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 <a href="http://ymacs.org">Ymacs</a>) 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. <a class="update" href="#keymap">[see below for caveat]</a></p>
<p>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.</p>
<p>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 <code>onscroll</code>
event works almost the same on all browsers, and lends itself well to
displaying things only as they are scrolled into view.)</p>
</section>
<section id="input">
<h2>Input</h2>
<p>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.</p>
<p>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.</p>
<p>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. <a class="update"
href="#keymap">[no longer the case]</a></p>
<p>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.</p>
</section>
<section id="selection">
<p>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.</p>
<p>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.</p>
<p>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. <a class="update"
href="#keymap">[no longer the case]</a></p>
<p>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.</p>
</section>
<section id="selection">
<h2>Selection</h2>
<p>Getting and setting the selection range of a textarea in modern
browsers is trivial—you just use the <code>selectionStart</code>
and <code>selectionEnd</code> 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.</p>
<p>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 <em>head</em>
and its <em>anchor</em> 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.</p>
<p>But not something that the browser selection APIs expose.</p>
<p>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.</p>
<p>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 <em>real</em>
selection.</p>
<p>In short, scary hacks could not be avoided entirely in CodeMirror
2.</p>
<p>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.</p>
</section>
<section id="update">
<p>Getting and setting the selection range of a textarea in modern
browsers is trivial—you just use the <code>selectionStart</code>
and <code>selectionEnd</code> 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.</p>
<p>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 <em>head</em>
and its <em>anchor</em> 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.</p>
<p>But not something that the browser selection APIs expose.</p>
<p>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.</p>
<p>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 <em>real</em>
selection.</p>
<p>In short, scary hacks could not be avoided entirely in CodeMirror
2.</p>
<p>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.</p>
</section>
<section id="update">
<h2>Intelligent Updating</h2>
<p>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.</p>
<p>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.</p>
<p>CodeMirror uses a concept of <em>operations</em>, 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 <code>operation</code> that accepts a function, and returns
another function that wraps the given function as an operation.</p>
<p>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.</p>
<p>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.</p>
<p>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). <span class="update">[the full-refresh
<p>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.</p>
<p>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.</p>
<p>CodeMirror uses a concept of <em>operations</em>, 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 <code>operation</code> that accepts a function, and returns
another function that wraps the given function as an operation.</p>
<p>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.</p>
<p>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.</p>
<p>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). <span class="update">[the full-refresh
algorithm was dropped, it wasn't really faster than the patching
one]</span></p>
<p>All updating uses <code>innerHTML</code> 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, <a href="manual.html#markText">marking</a>, 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 <code>innerHTML</code> update to do the refresh.</p>
</section>
<section id="parse">
<p>All updating uses <code>innerHTML</code> 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, <a href="manual.html#markText">marking</a>, 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 <code>innerHTML</code> update to do the refresh.</p>
</section>
<section id="parse">
<h2>Parsers can be Simple</h2>
<p>When I wrote CodeMirror 1, I
thought <a href="https://codemirror.net/story.html#parser">interruptable
parsers</a> were a hugely scary and complicated thing, and I used a
bunch of heavyweight abstractions to keep this supposed complexity
under control: parsers
were <a href="http://bob.pythonmac.org/archives/2005/07/06/iteration-in-javascript/">iterators</a>
that consumed input from another iterator, and used funny
closure-resetting tricks to copy and resume themselves.</p>
<p>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.</p>
<p>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 <a href="manual.html#modeapi">mode API</a> 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.</p>
<p>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 <em>see</em> 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.</p>
</section>
<section id="summary">
<p>When I wrote CodeMirror 1, I
thought <a href="https://codemirror.net/story.html#parser">interruptable
parsers</a> were a hugely scary and complicated thing, and I used a
bunch of heavyweight abstractions to keep this supposed complexity
under control: parsers
were <a href="http://bob.pythonmac.org/archives/2005/07/06/iteration-in-javascript/">iterators</a>
that consumed input from another iterator, and used funny
closure-resetting tricks to copy and resume themselves.</p>
<p>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.</p>
<p>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 <a href="manual.html#modeapi">mode API</a> 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.</p>
<p>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 <em>see</em> 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.</p>
</section>
<section id="summary">
<h2>What Gives?</h2>
<p>Given all this, what can you expect from CodeMirror 2?</p>
<p>Given all this, what can you expect from CodeMirror 2?</p>
<ul>
<li><strong>Small.</strong> the base library is
some <span class="update">45k</span> when minified
now, <span class="update">17k</span> when gzipped. It's smaller than
its own logo.</li>
<li><strong>Lightweight.</strong> CodeMirror 2 initializes very
quickly, and does almost no work when it is not focused. This means
you can treat it almost like a textarea, have multiple instances on a
page without trouble.</li>
<li><strong>Huge document support.</strong> Since highlighting is
really fast, and no DOM structure is being built for non-visible
content, you don't have to worry about locking up your browser when a
user enters a megabyte-sized document.</li>
<li><strong>Extended API.</strong> Some things kept coming up in the
mailing list, such as marking pieces of text or lines, which were
extremely hard to do with CodeMirror 1. The new version has proper
support for these built in.</li>
<li><strong>Tab support.</strong> Tabs inside editable documents were,
for some reason, a no-go. At least six different people announced they
were going to add tab support to CodeMirror 1, none survived (I mean,
none delivered a working version). CodeMirror 2 no longer removes tabs
from your document.</li>
<ul>
<li><strong>Sane styling.</strong> <code>iframe</code> nodes aren't
really known for respecting document flow. Now that an editor instance
is a plain <code>div</code> 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 <a href="../demo/resize.html">want to</a>.</li>
<li><strong>Small.</strong> the base library is
some <span class="update">45k</span> when minified
now, <span class="update">17k</span> when gzipped. It's smaller than
its own logo.
</li>
<li><strong>Lightweight.</strong> CodeMirror 2 initializes very
quickly, and does almost no work when it is not focused. This means
you can treat it almost like a textarea, have multiple instances on a
page without trouble.
</li>
<li><strong>Huge document support.</strong> Since highlighting is
really fast, and no DOM structure is being built for non-visible
content, you don't have to worry about locking up your browser when a
user enters a megabyte-sized document.
</li>
<li><strong>Extended API.</strong> Some things kept coming up in the
mailing list, such as marking pieces of text or lines, which were
extremely hard to do with CodeMirror 1. The new version has proper
support for these built in.
</li>
<li><strong>Tab support.</strong> Tabs inside editable documents were,
for some reason, a no-go. At least six different people announced they
were going to add tab support to CodeMirror 1, none survived (I mean,
none delivered a working version). CodeMirror 2 no longer removes tabs
from your document.
</li>
<li><strong>Sane styling.</strong> <code>iframe</code> nodes aren't
really known for respecting document flow. Now that an editor instance
is a plain <code>div</code> 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 <a href="../demo/resize.html">want to</a>.
</li>
</ul>
</ul>
<p>On the downside, a CodeMirror 2 instance is <em>not</em> 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.</p>
<p>On the downside, a CodeMirror 2 instance is <em>not</em> 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.</p>
<p id="changes" style="margin-top: 2em;"><span style="font-weight:
<p id="changes" style="margin-top: 2em;"><span style="font-weight:
bold">[Updates from November 13th 2011]</span> 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.</p>
</section>
<section id="btree">
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.</p>
</section>
<section id="btree">
<h2>Content Representation</h2>
<p>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 <code>memmove</code> of a
bunch of pointers, so it is cheap even for huge documents.</p>
<p>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.</p>
<p>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.</p>
<p>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.</p>
<p>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.</p>
</section>
<section id="keymap">
<p>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 <code>memmove</code> of a
bunch of pointers, so it is cheap even for huge documents.</p>
<p>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.</p>
<p>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.</p>
<p>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.</p>
<p>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.</p>
</section>
<section id="keymap">
<h2>Keymaps</h2>
<p><a href="#approach">Above</a>, 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 <a href="#selection">hacks</a> 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.</p>
<p>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.</p>
<p>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.</p>
<p>The key event handlers now translate the key event into a string,
something like <code>Ctrl-Home</code> or <code>Shift-Cmd-R</code>, and
use that string to look up an action to perform. To make keybinding
customizable, this lookup goes through
a <a href="manual.html#option_keyMap">table</a>, 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 <code>Ctrl-F</code>, and which is also
used by the custom Emacs bindings).</p>
<p>A new
option <a href="manual.html#option_extraKeys"><code>extraKeys</code></a>
allows ad-hoc keybindings to be defined in a much nicer way than what
was possible with the
old <a href="manual.html#option_onKeyEvent"><code>onKeyEvent</code></a>
callback. You simply provide an object mapping key identifiers to
functions, instead of painstakingly looking at raw key events.</p>
<p>Built-in commands map to strings, rather than functions, for
example <code>"goLineUp"</code> 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 <code>CodeMirror.commands</code> object, which maps such commands
to functions.</p>
<p>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.</p>
<p>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 <code>input</code> when the
composing is finished, but many don't fire anything when the character
is updated <em>during</em> composition. So we poll, whenever the
editor is focused, to provide immediate updates of the display.</p>
</section>
<p><a href="#approach">Above</a>, 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 <a href="#selection">hacks</a> 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.</p>
<p>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.</p>
<p>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.</p>
<p>The key event handlers now translate the key event into a string,
something like <code>Ctrl-Home</code> or <code>Shift-Cmd-R</code>, and
use that string to look up an action to perform. To make keybinding
customizable, this lookup goes through
a <a href="manual.html#option_keyMap">table</a>, 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 <code>Ctrl-F</code>, and which is also
used by the custom Emacs bindings).</p>
<p>A new
option <a href="manual.html#option_extraKeys"><code>extraKeys</code></a>
allows ad-hoc keybindings to be defined in a much nicer way than what
was possible with the
old <a href="manual.html#option_onKeyEvent"><code>onKeyEvent</code></a>
callback. You simply provide an object mapping key identifiers to
functions, instead of painstakingly looking at raw key events.</p>
<p>Built-in commands map to strings, rather than functions, for
example <code>"goLineUp"</code> 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 <code>CodeMirror.commands</code> object, which maps such commands
to functions.</p>
<p>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.</p>
<p>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 <code>input</code> when the
composing is finished, but many don't fire anything when the character
is updated <em>during</em> composition. So we poll, whenever the
editor is focused, to provide immediate updates of the display.</p>
</section>
</article>
<svg xmlns="http://www.w3.org/2000/svg" width="640" height="640">
<path d="M292.4 53.2s8.7 18.6-3.6 39.2c-6.2 10.2-163.1 43.6-224.3 128.6-37.5 58.7-95 228.7 88.7 341.2 82.5 42.5 117.5 41.2 117.5 41.2s-55-38.7-20-65 86.2-38.7 100-75c16.2 12.5 42.5 38.7 67.5 36.2-2.5-16.2-8.7-22.5 11.2-25s30-2.5 30-2.5-18.7-15-40-16.2-61.2-42.5-60-60c26.2-15 60-40 80-32.5s40 20 43.7 31.2c0 7.5-3.7 20 12.5 10s12.5-16.2 18.7-30 7.5-16.2-6.2-27.5-46.2-37.5-72.5-30-81.2 28.7-108.7 2.5c11.2-25 28.7-65 20-92.5 16.2-12.5 35-26.2 37.5-48.7 18.7-2.5 58.7-13.7 51.2-33.7S374.9 36.0 293.6 53.5" fill="#fff"/>
<svg height="640" width="640" xmlns="http://www.w3.org/2000/svg">
<path
d="M292.4 53.2s8.7 18.6-3.6 39.2c-6.2 10.2-163.1 43.6-224.3 128.6-37.5 58.7-95 228.7 88.7 341.2 82.5 42.5 117.5 41.2 117.5 41.2s-55-38.7-20-65 86.2-38.7 100-75c16.2 12.5 42.5 38.7 67.5 36.2-2.5-16.2-8.7-22.5 11.2-25s30-2.5 30-2.5-18.7-15-40-16.2-61.2-42.5-60-60c26.2-15 60-40 80-32.5s40 20 43.7 31.2c0 7.5-3.7 20 12.5 10s12.5-16.2 18.7-30 7.5-16.2-6.2-27.5-46.2-37.5-72.5-30-81.2 28.7-108.7 2.5c11.2-25 28.7-65 20-92.5 16.2-12.5 35-26.2 37.5-48.7 18.7-2.5 58.7-13.7 51.2-33.7S374.9 36.0 293.6 53.5"
fill="#fff"/>
<path d="M551.9 257.4c13.7-36.2 36.2-97.5 15-100s-45 55-51.2 70-31.2 90-17.5 100 53.7-70 53.7-70" fill="#fff"/>
<path d="M435.6 159.9c3.9-6.1 7.2-.8-9.6.4-16.8 1.2-37.0 6.4-40.4 13.8-6.4 13.7-8.6 47.6-33.0 55.1-1.2 8.7 0 28.7 34.3 32.5s35.6-18.7 55.6-18.1 18.1-6.8 11.2-12.5-30.6-43.7-30.6-56.2 8.1-8.1 12.5-15M128.7 536.8s-10.6-28.1 20.6-43.1 59.3-3.1 65 7.5-9.3 53.1-63.1 53.7c-13.1-10.6-22.5-18.1-22.5-18.1" fill="#da687d"/>
<path
d="M435.6 159.9c3.9-6.1 7.2-.8-9.6.4-16.8 1.2-37.0 6.4-40.4 13.8-6.4 13.7-8.6 47.6-33.0 55.1-1.2 8.7 0 28.7 34.3 32.5s35.6-18.7 55.6-18.1 18.1-6.8 11.2-12.5-30.6-43.7-30.6-56.2 8.1-8.1 12.5-15M128.7 536.8s-10.6-28.1 20.6-43.1 59.3-3.1 65 7.5-9.3 53.1-63.1 53.7c-13.1-10.6-22.5-18.1-22.5-18.1"
fill="#da687d"/>
<g opacity=".7" transform="matrix(1.25 0 0 -1.25 -26.846 640)">
<path d="M466.2 369.8c.4-1.5.2-3.1.2-4.8-.0-1.6-.1-3.3-.6-4.9.1-.3-.0-.8-.0-1.2-.4-.5-.1-.9-.2-1.3.6-.6 1.5-1.3 1.1-2.1-.2-.5-.8-.7-1.4-.9-.3-1.4-.7-3.0-1.0-4.6-.0-.4-.0-1.0-.1-1.4-.3-1.0-.6-1.9-.9-3.0-.2-.9.0-2.6-1.0-2.8-.4-1.0-.6-1.8-1.1-2.8-.2-.1-.4.1-.6.0-.3-.3-.2-.8-.5-1.1-.2-.2-.5-.1-.6-.3-.3-.5-.2-1.2-.5-1.9a4.7 4.7 0 0 1-1.1-1.4c-.5-1.1-.6-2.6-1.5-3.5-.2-.2-.7-.3-1.0-.6-.2-.2-.4-.6-.6-.9-.3-.4-.6-1.0-.9-1.5-1.0-1.7-2.3-3.7-3.3-5.6-.1-.3-.2-.8-.3-1.1-.3-.8-.9-1.5-1.2-2.3-.5-1.2-1.3-3.3-2.5-4.4-.1-.1-.4-.2-.6-.4-.1-.2-.1-.5-.3-.8-.0-.1-.4-.1-.5-.3-.2-.2-.3-.4-.4-.6-.4-.3-1.0-.5-1.3-.9-.2-.3-.3-.7-.6-1.1-.3-.4-.7-.7-1.0-1.2-.9-1.2-1.3-2.8-2.7-3.3-.3-.1-.8-.1-1.2-.2-.3.3-.0.8-.1 1.1-.0.2-.2.3-.3.5-.1.4-.3 1.3-.3 1.8.0.5.3.9.3 1.6.0.5-.1 1.2-.1 1.8-.0.3.0.6.0.9-.0.5-.0.8.0 1.4.0.7-.0 1.4-.1 2.1.3 1.7.5 3.0 1.0 4.9.2.8.7 2.3 1.1 3.3.8 1.8 1.6 3.9 2.6 5.9.3.6.8 1.1 1.1 1.8.1.3.2.8.3 1.2 1.6 3.3 3.8 6.2 5.5 9.7 1.4 2.8 2.8 5.4 4.7 8.5.8 1.4 1.5 2.9 2.4 4.0.6.8 1.6 2.4 2.3 3.9.2.5.2 1.0 1.0 1.1.7.9 1.3 2.0 2.1 2.8.2.2.5.3.7.4.2.2.3.5.6.7.5.4 1.4.8 2.0 1.6.1.2.2.4.4.6.4.5 1.1 1.8 1.8 2.0.0.0.2.0.3-.0" fill="#da687d"/>
<path
d="M466.2 369.8c.4-1.5.2-3.1.2-4.8-.0-1.6-.1-3.3-.6-4.9.1-.3-.0-.8-.0-1.2-.4-.5-.1-.9-.2-1.3.6-.6 1.5-1.3 1.1-2.1-.2-.5-.8-.7-1.4-.9-.3-1.4-.7-3.0-1.0-4.6-.0-.4-.0-1.0-.1-1.4-.3-1.0-.6-1.9-.9-3.0-.2-.9.0-2.6-1.0-2.8-.4-1.0-.6-1.8-1.1-2.8-.2-.1-.4.1-.6.0-.3-.3-.2-.8-.5-1.1-.2-.2-.5-.1-.6-.3-.3-.5-.2-1.2-.5-1.9a4.7 4.7 0 0 1-1.1-1.4c-.5-1.1-.6-2.6-1.5-3.5-.2-.2-.7-.3-1.0-.6-.2-.2-.4-.6-.6-.9-.3-.4-.6-1.0-.9-1.5-1.0-1.7-2.3-3.7-3.3-5.6-.1-.3-.2-.8-.3-1.1-.3-.8-.9-1.5-1.2-2.3-.5-1.2-1.3-3.3-2.5-4.4-.1-.1-.4-.2-.6-.4-.1-.2-.1-.5-.3-.8-.0-.1-.4-.1-.5-.3-.2-.2-.3-.4-.4-.6-.4-.3-1.0-.5-1.3-.9-.2-.3-.3-.7-.6-1.1-.3-.4-.7-.7-1.0-1.2-.9-1.2-1.3-2.8-2.7-3.3-.3-.1-.8-.1-1.2-.2-.3.3-.0.8-.1 1.1-.0.2-.2.3-.3.5-.1.4-.3 1.3-.3 1.8.0.5.3.9.3 1.6.0.5-.1 1.2-.1 1.8-.0.3.0.6.0.9-.0.5-.0.8.0 1.4.0.7-.0 1.4-.1 2.1.3 1.7.5 3.0 1.0 4.9.2.8.7 2.3 1.1 3.3.8 1.8 1.6 3.9 2.6 5.9.3.6.8 1.1 1.1 1.8.1.3.2.8.3 1.2 1.6 3.3 3.8 6.2 5.5 9.7 1.4 2.8 2.8 5.4 4.7 8.5.8 1.4 1.5 2.9 2.4 4.0.6.8 1.6 2.4 2.3 3.9.2.5.2 1.0 1.0 1.1.7.9 1.3 2.0 2.1 2.8.2.2.5.3.7.4.2.2.3.5.6.7.5.4 1.4.8 2.0 1.6.1.2.2.4.4.6.4.5 1.1 1.8 1.8 2.0.0.0.2.0.3-.0"
fill="#da687d"/>
</g>
<g opacity=".7" transform="matrix(1.25 0 0 -1.25 -26.846 640)">
<path d="M459.8 349.4c.6-1.9.7-4.1.9-6.2-.0-2.1-.1-4.5-.7-6.9.0-.4-.1-1.1-.1-1.6-.5-.7-.2-1.2-.4-1.8.7-.8 1.6-1.6 1.1-2.8-.3-.8-1.1-1.0-1.8-1.4-.6-1.9-1.2-4.2-1.8-6.3-.1-.6-.2-1.3-.4-1.9-.5-1.3-1.1-2.6-1.5-4.1-.4-1.2-.3-3.4-1.6-3.6-.6-1.3-.9-2.3-1.6-3.6-.3-.2-.5.1-.8-.0-.3-.4-.4-1.0-.7-1.4-.2-.2-.5-.2-.7-.5-.4-.6-.3-1.5-.7-2.4a6.5 6.5 0 0 1-1.1-1.3l-.1-.2-.0-.1-.0-.0-.0-.0v-.0l.0.1-.0-.0-.0-.0-.0-.0-.0-.0-.0-.0c-.6-1.6-.8-3.6-1.8-4.9-.3-.4-.8-.5-1.1-.9-.2-.3-.4-.9-.7-1.3-.3-.6-.7-1.4-1.0-2.1-1.0-2.4-2.5-5.2-3.5-8.0-.1-.5-.2-1.1-.3-1.6-.4-1.1-1.0-2.2-1.3-3.3-.6-1.7-1.4-4.7-3.0-6.3-.1-.2-.5-.4-.7-.6-.2-.3-.1-.7-.4-1.1-.1-.1-.4-.2-.6-.4-.2-.3-.3-.6-.5-.9-.5-.5-1.2-.8-1.7-1.4-.3-.4-.5-1.0-.9-1.5-.4-.5-.9-1.1-1.4-1.6l-1.8-2.6c-.5-.8-1.2-1.5-2.0-1.8-.4-.2-1.0-.1-1.5-.2-.3.4.0 1.0.0 1.5-.0.2-.2.5-.2.7-.0.6-.1 1.7-.0 2.3.0.6.5 1.1.7 2.0.1.7-.0 1.5-.0 2.3.0.3.1.7.1 1.2.0.6-.0.9.1 1.7.1.9-.0 1.8-.1 2.7.4 2.1.6 3.8 1.2 6.3.2 1.1.7 3.1 1.2 4.4.4 1.3.9 2.6 1.3 4.0.4 1.4.9 2.8 1.4 4.2.3.9.8 1.7 1.2 2.7.2.5.2 1.1.4 1.7.4 1.2.9 2.5 1.5 3.6a147.5 147.5 0 0 0 1.7 3.3l.9 1.8c.3.5.6 1.1.9 1.7a75.5 75.5 0 0 1 1.8 3.6 129.6 129.6 0 0 0 3.1 5.7 340.1 340.1 0 0 0 3.6 5.8c1.1 1.8 2.2 3.8 3.3 5.3.8 1.1 2.1 3.1 3.0 5.1.3.7.3 1.3 1.1 1.4.9 1.2 1.6 2.6 2.4 3.7.2.2.5.4.8.7.2.3.4.7.6 1.0.5.6 1.6 1.2 2.2 2.5.1.3.1.6.3 1.0.3.8 1.0 2.7 1.7 3.1.0.0.2.0.3.0" fill="#da687d"/>
<path
d="M459.8 349.4c.6-1.9.7-4.1.9-6.2-.0-2.1-.1-4.5-.7-6.9.0-.4-.1-1.1-.1-1.6-.5-.7-.2-1.2-.4-1.8.7-.8 1.6-1.6 1.1-2.8-.3-.8-1.1-1.0-1.8-1.4-.6-1.9-1.2-4.2-1.8-6.3-.1-.6-.2-1.3-.4-1.9-.5-1.3-1.1-2.6-1.5-4.1-.4-1.2-.3-3.4-1.6-3.6-.6-1.3-.9-2.3-1.6-3.6-.3-.2-.5.1-.8-.0-.3-.4-.4-1.0-.7-1.4-.2-.2-.5-.2-.7-.5-.4-.6-.3-1.5-.7-2.4a6.5 6.5 0 0 1-1.1-1.3l-.1-.2-.0-.1-.0-.0-.0-.0v-.0l.0.1-.0-.0-.0-.0-.0-.0-.0-.0-.0-.0c-.6-1.6-.8-3.6-1.8-4.9-.3-.4-.8-.5-1.1-.9-.2-.3-.4-.9-.7-1.3-.3-.6-.7-1.4-1.0-2.1-1.0-2.4-2.5-5.2-3.5-8.0-.1-.5-.2-1.1-.3-1.6-.4-1.1-1.0-2.2-1.3-3.3-.6-1.7-1.4-4.7-3.0-6.3-.1-.2-.5-.4-.7-.6-.2-.3-.1-.7-.4-1.1-.1-.1-.4-.2-.6-.4-.2-.3-.3-.6-.5-.9-.5-.5-1.2-.8-1.7-1.4-.3-.4-.5-1.0-.9-1.5-.4-.5-.9-1.1-1.4-1.6l-1.8-2.6c-.5-.8-1.2-1.5-2.0-1.8-.4-.2-1.0-.1-1.5-.2-.3.4.0 1.0.0 1.5-.0.2-.2.5-.2.7-.0.6-.1 1.7-.0 2.3.0.6.5 1.1.7 2.0.1.7-.0 1.5-.0 2.3.0.3.1.7.1 1.2.0.6-.0.9.1 1.7.1.9-.0 1.8-.1 2.7.4 2.1.6 3.8 1.2 6.3.2 1.1.7 3.1 1.2 4.4.4 1.3.9 2.6 1.3 4.0.4 1.4.9 2.8 1.4 4.2.3.9.8 1.7 1.2 2.7.2.5.2 1.1.4 1.7.4 1.2.9 2.5 1.5 3.6a147.5 147.5 0 0 0 1.7 3.3l.9 1.8c.3.5.6 1.1.9 1.7a75.5 75.5 0 0 1 1.8 3.6 129.6 129.6 0 0 0 3.1 5.7 340.1 340.1 0 0 0 3.6 5.8c1.1 1.8 2.2 3.8 3.3 5.3.8 1.1 2.1 3.1 3.0 5.1.3.7.3 1.3 1.1 1.4.9 1.2 1.6 2.6 2.4 3.7.2.2.5.4.8.7.2.3.4.7.6 1.0.5.6 1.6 1.2 2.2 2.5.1.3.1.6.3 1.0.3.8 1.0 2.7 1.7 3.1.0.0.2.0.3.0"
fill="#da687d"/>
</g>
<path d="M520.8 231.2s-10 38.3-5.8 70l5.8-70m11.8-25.9c-.7-2.6-5.7 74.3-4.2 77.0 1.4 2.6 8.9-60.7 4.2-77.0" fill="#fff"/>
<path d="M294.0 57.9s5.8 33.2-48.3 50.7-128.8 32.4-183.8 98.3c-55 65.8-65 174.9-27.5 246.6 37.5 71.6 129.1 160.8 294.1 160.8S624.6 487.7 624.6 400.2c0-45.8-25.1-35.6-33.8 7.3-7.8 38.8-80.5 183.6-263.0 183.6-182.5 0-275.8-110.8-287.5-205.8-11.6-95 14.8-176.8 110.7-226.0 95.8-49.1 156.6-29.5 143.3-101.2" fill="#2d2b2c"/>
<path d="M248.5 314.5s-10.8 50.0-63.3 74.1c-52.5 24.1-75 37.5-82.5 79.1 20-33.3 78.3-40 110.8-72.5s35-80.8 35-80.8m14.1-115.8s45 58.3 98.3 30c53.3-28.3 25.8-94.1 25-98.3-.8-4.1 5.8 73.3-34.1 86.6-40 13.3-89.1-18.3-89.1-18.3M145.2 547.0s28.3 1.6 49.1-21.6c20.8-23.3 9.1-60 43.3-73.3 34.1-13.3 57.5-5 57.5-5s-60.8 8.3-65.8 43.3-13.3 50.8-26.6 61.6c-13.3 10.8-30.8 12.5-30.8 12.5l-26.6-17.5z" fill="#2d2b2c"/>
<path d="M273.7 365.6s50.6-8.4 73.7-60c18.9-42.2 6.2-78.7 6.2-78.7l-25.6 3.7s10.6 30.6 2.5 58.1-26.8 59.3-56.8 76.8m272.6-117.0c-18.0 45.1-44.7 77.5-47.8 75.8-5.6-3.1-.4-42.5 17.6-87.7 18.0-45.1 40.5-79.5 48.8-76.2 8.3 3.2-.6 42.9-18.6 88.1m26.2-90.6c-20.5-11.1-45.8 30.5-66.5 79.1-20.7 48.6-23.1 87.7-7.1 94.7 21.3 9.3 41.4-27.8 62.1-76.4s30-87.5 11.5-97.5" fill="#2d2b2c"/>
<path d="M199.4 402.9s12.9 38.7 77.5 30.8c60.8-7.5 86.9-30.9 124.1-54.1 26.6-16.6 25-9.1 28.3-8.3 3.3.8 18.3 10 34.1 8.3.8 8.3-7.4 1.6-10.8 9.1-1.6 5 12.6 28.5 33.3 25 34.1-5.8 24.5-20 31.2-35.8 6.6-15.8 11.2-24.1-22.0-46.6-25.8-23.3-40-17.4-52.5-17.4s-45 17.4-67.5 13.3-36.6-12.5-36.6-12.5l-14.9 16.6s27.4 20.8 59.1 13.3c31.6-7.5 55.0-31.6 82.5-19.1 27.5 12.5 32.8 20.8 41.2 27.0 9.5 7.0 10.4 14.5 3.7 21.2-6.6 6.6.0 11.6-4.1 19.1-4.1 7.5-25 10.8-25 10.8s22.8-29.3-5-31.6c-20-1.6-32.5-28.3-60.8-14.1-40.6 20.3-98.3 55-138.3 59.1-38.9 4.0-53.3-8.3-63.3-31.6-11.6 10-14.1 17.5-14.1 17.5" fill="#2d2b2c"/>
<path d="M342.7 411.2s28.3 15 20 47.5-45.8 55-70 65.8c-24.1 10.8-42.1 25.1-24.1 34.1 15 7.5 23.3-11.6 45-15 21.6-3.3 40.8 5 42.5 17.5-9.1-5-15.8-5-15.8-5s7.5 5 8.3 13.3c.8 8.3-.8 4.1-5.8 4.1-12.5 0-14.1-10.8-25-9.1-27.5 2.5-29.1 14.1-44.1 15.8-15 1.6-40-6.6-36.6-25.8 3.3-19.1 29.1-32.5 53.3-42.5 24.1-10 60.8-41.6 55.8-70-5-28.3-26.6-22.5-26.6-22.5l23.3-8.3zm71.0-235.9s11.8 5.9 19.3 35.9c5 10.8 11.6 19.5 17.5 22.0 5.8 2.5-2.5 5-6.6 4.1-4.1-.8-12.9-4.5-18.7.4s-20 18.3-40 15c-20-3.3-37.0-16.0-37.0-16.0s7.5 33.1 52.0 31.0c30.9-1.4 24.1-17.5 45-20.8 20.8-3.3 18.6-13.8 14.1-22.5-7.0-13.7-29.1-17.0-31.6-57.2-2.5-4.1-18.1.5-13.9 8.0m71.2 209.1c1.8 1.8-14.7 39.3-14.7 39.3s-3.3 9.1-12.2 5.9c-9.1-3.3-6.7-9.9-6.7-9.9l21.2-38.5s11.6 2.3 12.5 3.2m23.4-53.3l-4.1 12.5-13.6-10.4 3.6-7.9 14.1 5.8z" fill="#2d2b2c"/>
<path d="M356.0 403.7c-2.5 9.1 29.1 62.5 50.8 59.1 21.6-3.3 24.9 3.3 18.3 7.5-6.6 4.1-18.3 5.8-16.6 11.6 1.6 5.8 6.2 11.6-4.5 10-10.8-1.6-32.9-13.3-46.2-35.8-4.1 13.3-5.8 17.5-5.8 17.5s42.5 44.1 85.8 25.8c-7.5-5.8-28.3-4.1-10.8-14.1s30.8-13.3 39.1 2.5c4.1-.8 4.1-37.5-50.8-35-19.1-2.5-45-40-45-59.1-5 5-11.6.8-14.1 10m169.4-184.3s-10.9 32.8-6.3 60l6.3-60m11.2-19.4c-.5-2.0-5.0 57.2-4.0 59.2 1.0 2.0 7.3-46.6 4.0-59.2m-145.6-32.8s47.0-4.1 38.7-22.9c-25.1-56.5-72.9-89.5-161.2-86.2 50-20 92.5-17.5 133.3 17.5 20.4 17.5 29.8 38.8 37.1 55.1 11.6 26 8.2 28.4-4.6 35.7-15.7 8.8-42.7 3.2-43.3.8" fill="#2d2b2c"/>
<path d="M520.8 231.2s-10 38.3-5.8 70l5.8-70m11.8-25.9c-.7-2.6-5.7 74.3-4.2 77.0 1.4 2.6 8.9-60.7 4.2-77.0"
fill="#fff"/>
<path
d="M294.0 57.9s5.8 33.2-48.3 50.7-128.8 32.4-183.8 98.3c-55 65.8-65 174.9-27.5 246.6 37.5 71.6 129.1 160.8 294.1 160.8S624.6 487.7 624.6 400.2c0-45.8-25.1-35.6-33.8 7.3-7.8 38.8-80.5 183.6-263.0 183.6-182.5 0-275.8-110.8-287.5-205.8-11.6-95 14.8-176.8 110.7-226.0 95.8-49.1 156.6-29.5 143.3-101.2"
fill="#2d2b2c"/>
<path
d="M248.5 314.5s-10.8 50.0-63.3 74.1c-52.5 24.1-75 37.5-82.5 79.1 20-33.3 78.3-40 110.8-72.5s35-80.8 35-80.8m14.1-115.8s45 58.3 98.3 30c53.3-28.3 25.8-94.1 25-98.3-.8-4.1 5.8 73.3-34.1 86.6-40 13.3-89.1-18.3-89.1-18.3M145.2 547.0s28.3 1.6 49.1-21.6c20.8-23.3 9.1-60 43.3-73.3 34.1-13.3 57.5-5 57.5-5s-60.8 8.3-65.8 43.3-13.3 50.8-26.6 61.6c-13.3 10.8-30.8 12.5-30.8 12.5l-26.6-17.5z"
fill="#2d2b2c"/>
<path
d="M273.7 365.6s50.6-8.4 73.7-60c18.9-42.2 6.2-78.7 6.2-78.7l-25.6 3.7s10.6 30.6 2.5 58.1-26.8 59.3-56.8 76.8m272.6-117.0c-18.0 45.1-44.7 77.5-47.8 75.8-5.6-3.1-.4-42.5 17.6-87.7 18.0-45.1 40.5-79.5 48.8-76.2 8.3 3.2-.6 42.9-18.6 88.1m26.2-90.6c-20.5-11.1-45.8 30.5-66.5 79.1-20.7 48.6-23.1 87.7-7.1 94.7 21.3 9.3 41.4-27.8 62.1-76.4s30-87.5 11.5-97.5"
fill="#2d2b2c"/>
<path
d="M199.4 402.9s12.9 38.7 77.5 30.8c60.8-7.5 86.9-30.9 124.1-54.1 26.6-16.6 25-9.1 28.3-8.3 3.3.8 18.3 10 34.1 8.3.8 8.3-7.4 1.6-10.8 9.1-1.6 5 12.6 28.5 33.3 25 34.1-5.8 24.5-20 31.2-35.8 6.6-15.8 11.2-24.1-22.0-46.6-25.8-23.3-40-17.4-52.5-17.4s-45 17.4-67.5 13.3-36.6-12.5-36.6-12.5l-14.9 16.6s27.4 20.8 59.1 13.3c31.6-7.5 55.0-31.6 82.5-19.1 27.5 12.5 32.8 20.8 41.2 27.0 9.5 7.0 10.4 14.5 3.7 21.2-6.6 6.6.0 11.6-4.1 19.1-4.1 7.5-25 10.8-25 10.8s22.8-29.3-5-31.6c-20-1.6-32.5-28.3-60.8-14.1-40.6 20.3-98.3 55-138.3 59.1-38.9 4.0-53.3-8.3-63.3-31.6-11.6 10-14.1 17.5-14.1 17.5"
fill="#2d2b2c"/>
<path
d="M342.7 411.2s28.3 15 20 47.5-45.8 55-70 65.8c-24.1 10.8-42.1 25.1-24.1 34.1 15 7.5 23.3-11.6 45-15 21.6-3.3 40.8 5 42.5 17.5-9.1-5-15.8-5-15.8-5s7.5 5 8.3 13.3c.8 8.3-.8 4.1-5.8 4.1-12.5 0-14.1-10.8-25-9.1-27.5 2.5-29.1 14.1-44.1 15.8-15 1.6-40-6.6-36.6-25.8 3.3-19.1 29.1-32.5 53.3-42.5 24.1-10 60.8-41.6 55.8-70-5-28.3-26.6-22.5-26.6-22.5l23.3-8.3zm71.0-235.9s11.8 5.9 19.3 35.9c5 10.8 11.6 19.5 17.5 22.0 5.8 2.5-2.5 5-6.6 4.1-4.1-.8-12.9-4.5-18.7.4s-20 18.3-40 15c-20-3.3-37.0-16.0-37.0-16.0s7.5 33.1 52.0 31.0c30.9-1.4 24.1-17.5 45-20.8 20.8-3.3 18.6-13.8 14.1-22.5-7.0-13.7-29.1-17.0-31.6-57.2-2.5-4.1-18.1.5-13.9 8.0m71.2 209.1c1.8 1.8-14.7 39.3-14.7 39.3s-3.3 9.1-12.2 5.9c-9.1-3.3-6.7-9.9-6.7-9.9l21.2-38.5s11.6 2.3 12.5 3.2m23.4-53.3l-4.1 12.5-13.6-10.4 3.6-7.9 14.1 5.8z"
fill="#2d2b2c"/>
<path
d="M356.0 403.7c-2.5 9.1 29.1 62.5 50.8 59.1 21.6-3.3 24.9 3.3 18.3 7.5-6.6 4.1-18.3 5.8-16.6 11.6 1.6 5.8 6.2 11.6-4.5 10-10.8-1.6-32.9-13.3-46.2-35.8-4.1 13.3-5.8 17.5-5.8 17.5s42.5 44.1 85.8 25.8c-7.5-5.8-28.3-4.1-10.8-14.1s30.8-13.3 39.1 2.5c4.1-.8 4.1-37.5-50.8-35-19.1-2.5-45-40-45-59.1-5 5-11.6.8-14.1 10m169.4-184.3s-10.9 32.8-6.3 60l6.3-60m11.2-19.4c-.5-2.0-5.0 57.2-4.0 59.2 1.0 2.0 7.3-46.6 4.0-59.2m-145.6-32.8s47.0-4.1 38.7-22.9c-25.1-56.5-72.9-89.5-161.2-86.2 50-20 92.5-17.5 133.3 17.5 20.4 17.5 29.8 38.8 37.1 55.1 11.6 26 8.2 28.4-4.6 35.7-15.7 8.8-42.7 3.2-43.3.8"
fill="#2d2b2c"/>
<path d="M395.8 174.2s12.9 8.9 23.1 1.6c4.6-4.8 7.7-7.4-5.4-6.8l-16.1-.6c-11.1-2.2-10.9 1.1-1.5 5.8z" fill="#2d2b2c"/>
</svg>
This source diff could not be displayed because it is too large. You can view the blob instead.
......@@ -2,7 +2,7 @@
<title>CodeMirror: Real-world Uses</title>
<meta charset="utf-8"/>
<link rel=stylesheet href="docs.css">
<link href="docs.css" rel=stylesheet>
<div id=nav>
<a href="https://codemirror.net"><h1>CodeMirror</h1><img id=logo src="logo.png"></a>
......@@ -19,22 +19,29 @@
<article>
<h2>CodeMirror real-world uses</h2>
<h2>CodeMirror real-world uses</h2>
<p>Create a <a href="https://github.com/codemirror/codemirror">pull
request</a> if you'd like your project to be added to this list.</p>
<ul>
<li><a href="https://www.adaface.com/pair-pro">Adaface PairPro</a> (Shared code editor with compiler and video conferencing)</li>
<li><a href="https://www.adaface.com/pair-pro">Adaface PairPro</a> (Shared code editor with compiler and video
conferencing)
</li>
<li><a href="http://brackets.io">Adobe Brackets</a> (code editor)</li>
<li><a href="http://adnuntius.com">Adnuntius</a> (used for in-browser code editing and version history)</li>
<li><a href="http://alm.tools">ALM Tools</a> (TypeScript powered IDE)</li>
<li><a href="http://amber-lang.net/">Amber</a> (JavaScript-based Smalltalk system)</li>
<li><a href="http://apeye.org/">APEye</a> (tool for testing &amp; documenting APIs)</li>
<li><a href="https://github.com/google/appengine-codiad">Appengine Codiad</a></li>
<li><a href="https://chrome.google.com/webstore/detail/better-text-viewer/lcaidopdffhfemoefoaadecppnjdknkc">Better Text Viewer</a> (plain text reader app for Chrome)</li>
<li><a href="http://blog.bitbucket.org/2013/05/14/edit-your-code-in-the-cloud-with-bitbucket/">Bitbucket</a> (code hosting)</li>
<li><a href="https://blogger.googleblog.com/2013/04/improvements-to-blogger-template-html.html">Blogger's theme editor</a></li>
<li><a href="https://chrome.google.com/webstore/detail/better-text-viewer/lcaidopdffhfemoefoaadecppnjdknkc">Better
Text Viewer</a> (plain text reader app for Chrome)
</li>
<li><a href="http://blog.bitbucket.org/2013/05/14/edit-your-code-in-the-cloud-with-bitbucket/">Bitbucket</a> (code
hosting)
</li>
<li><a href="https://blogger.googleblog.com/2013/04/improvements-to-blogger-template-html.html">Blogger's theme
editor</a></li>
<li><a href="http://bluegriffon.org/">BlueGriffon</a> (HTML editor)</li>
<li><a href="https://bnfplayground.pauliankline.com/">BNF Playground</a> (grammar workbench)</li>
<li><a href="https://github.com/isdampe/BosonEditorExperimental">Boson Editor</a> (code editor)</li>
......@@ -52,19 +59,26 @@
<li><a href="http://codefights.com/">CodeFights</a> (practice programming)</li>
<li><a href="https://github.com/angelozerr/CodeMirror-Eclipse">CodeMirror Eclipse</a> (embed CM in Eclipse)</li>
<li><a href="http://emmet.io/blog/codemirror-movie/">CodeMirror movie</a> (scripted editing demos)</li>
<li><a href="https://github.com/haoranyu/codemirror-record/">CodeMirror Record</a> (codemirror activity recording and playback)</li>
<li><a href="https://github.com/haoranyu/codemirror-record/">CodeMirror Record</a> (codemirror activity recording
and playback)
</li>
<li><a href="http://code.google.com/p/codemirror2-gwt/">CodeMirror2-GWT</a> (Google Web Toolkit wrapper)</li>
<li><a href="http://www.crunchzilla.com/code-monster">Code Monster</a> & <a href="http://www.crunchzilla.com/code-maven">Code Maven</a> (learning environment)</li>
<li><a href="http://www.crunchzilla.com/code-monster">Code Monster</a> & <a
href="http://www.crunchzilla.com/code-maven">Code Maven</a> (learning environment)
</li>
<li><a href="http://codepen.io">Codepen</a> (gallery of animations)</li>
<li><a href="https://github.com/pepstock-org/Coderba">Coderba</a> Google Web Toolkit (GWT) wrapper</li>
<li><a href="https://coderpad.io/">Coderpad</a> (interviewing tool)</li>
<li><a href="http://sasstwo.codeschool.com/levels/1/challenges/1">Code School</a> (online tech learning environment)</li>
<li><a href="http://sasstwo.codeschool.com/levels/1/challenges/1">Code School</a> (online tech learning environment)
</li>
<li><a href="http://code-snippets.bungeshea.com/">Code Snippets</a> (WordPress snippet management plugin)</li>
<li><a href="http://antonmi.github.io/code_together/">Code together</a> (collaborative editing)</li>
<li><a href="https://www.codevolve.com/">Codevolve</a> (programming lessons as-a-service)</li>
<li><a href="http://www.codezample.com">CodeZample</a> (code snippet sharing)</li>
<li><a href="http://codio.com">Codio</a> (Web IDE)</li>
<li><a href="https://www.codiva.io/">Codiva.io</a> (Online Java Compiler and IDE with auto-completion and error highlighting)</li>
<li><a href="https://www.codiva.io/">Codiva.io</a> (Online Java Compiler and IDE with auto-completion and error
highlighting)
</li>
<li><a href="http://www.communitycodecamp.com/">Community Code Camp</a> (code snippet sharing)</li>
<li><a href="http://www.compilejava.net/">compilejava.net</a> (online Java sandbox)</li>
<li><a href="http://www.ckwnc.com/">CKWNC</a> (UML editor)</li>
......@@ -72,21 +86,28 @@
<li><a href="http://rsnous.com/cruncher/">Cruncher</a> (notepad with calculation features)</li>
<li><a href="http://www.crudzilla.com/">Crudzilla</a> (self-hosted web IDE)</li>
<li><a href="http://cssdeck.com/">CSSDeck</a> (CSS showcase)</li>
<li><a href="http://ireneros.com/deck/deck.js-codemirror/introduction/#textarea-code">Deck.js integration</a> (slides with editors)</li>
<li><a href="http://ireneros.com/deck/deck.js-codemirror/introduction/#textarea-code">Deck.js integration</a>
(slides with editors)
</li>
<li><a href="http://www.dbninja.com">DbNinja</a> (MySQL access interface)</li>
<li><a href="http://www.ecsspert.com/">eCSSpert</a> (CSS demos and experiments)</li>
<li><a href="https://edabit.com">Edabit</a> (coding challenges)</li>
<li><a href="http://elm-lang.org/Examples.elm">Elm language examples</a></li>
<li><a href="http://eloquentjavascript.net/chapter1.html">Eloquent JavaScript</a> (book)</li>
<li><a href="http://emmet.io">Emmet</a> (fast XML editing)</li>
<li><a href="https://github.com/espruino/EspruinoWebIDE">Espruino Web IDE</a> (Chrome App for writing code on Espruino devices)</li>
<li><a href="https://github.com/espruino/EspruinoWebIDE">Espruino Web IDE</a> (Chrome App for writing code on
Espruino devices)
</li>
<li><a href="https://exlskills.com/learn-en/talent/interviews">EXLskills Live Interivews</a></li>
<li><a href="http://www.fastfig.com/">Fastfig</a> (online computation/math tool)</li>
<li><a href="https://metacpan.org/module/Farabi">Farabi</a> (modern Perl IDE)</li>
<li><a href="http://blog.pamelafox.org/2012/02/interactive-html5-slides-with-fathomjs.html">FathomJS integration</a> (slides with editors, again)</li>
<li><a href="http://blog.pamelafox.org/2012/02/interactive-html5-slides-with-fathomjs.html">FathomJS integration</a>
(slides with editors, again)
</li>
<li><a href="http://fiddlesalad.com/">Fiddle Salad</a> (web development environment)</li>
<li><a href="https://github.com/simogeo/Filemanager">Filemanager</a></li>
<li><a href="https://hacks.mozilla.org/2013/11/firefox-developer-tools-episode-27-edit-as-html-codemirror-more/">Firefox Developer Tools</a></li>
<li><a href="https://hacks.mozilla.org/2013/11/firefox-developer-tools-episode-27-edit-as-html-codemirror-more/">Firefox
Developer Tools</a></li>
<li><a href="http://www.firepad.io">Firepad</a> (collaborative text editor)</li>
<li><a href="https://gerritcodereview.com/">Gerrit</a>'s diff view and inline editor</li>
<li><a href="https://github.com/maks/git-crx">Git Crx</a> (Chrome App for browsing local git repos)</li>
......@@ -99,7 +120,7 @@
<li><a href="https://hackmd.io">HackMD</a> (Realtime collaborative markdown notes on all platforms)</li>
<li><a href="http://www.handcraft.com/">Handcraft</a> (HTML prototyping)</li>
<li><a href="http://hawkee.com/">Hawkee</a></li>
<li><a href="http://try.haxe.org">Haxe</a> (Haxe Playground) </li>
<li><a href="http://try.haxe.org">Haxe</a> (Haxe Playground)</li>
<li><a href="http://haxpad.com/">HaxPad</a> (editor for Win RT)</li>
<li><a href="http://megafonweblab.github.com/histone-javascript/">Histone template engine playground</a></li>
<li><a href="http://www.homegenie.it/docs/automation_getstarted.php">Homegenie</a> (home automation server)</li>
......@@ -126,7 +147,9 @@
<li><a href="http://lighttable.com/">Light Table</a> (experimental IDE)</li>
<li><a href="http://liveweave.com/">Liveweave</a> (HTML/CSS/JS scratchpad)</li>
<li><a href="https://liveuml.com/">LiveUML</a> (PlantUML online editor)</li>
<li><a href="https://github.com/TuvaLabs/markdown-delight-editor">Markdown Delight Editor</a> (extensible markdown editor polymer component)</li>
<li><a href="https://github.com/TuvaLabs/markdown-delight-editor">Markdown Delight Editor</a> (extensible markdown
editor polymer component)
</li>
<li><a href="http://marklighteditor.com/">Marklight editor</a> (lightweight markup editor)</li>
<li><a href="http://www.mergely.com/">Mergely</a> (interactive diffing)</li>
<li><a href="http://www.iunbug.com/mihtool">MIHTool</a> (iOS web-app debugging tool)</li>
......@@ -139,26 +162,38 @@
<li><a href="http://oakoutliner.com">Oak</a> (online outliner)</li>
<li><a href="http://www.greycampus.com/opencampus">OpenCampus</a></li>
<li><a href="http://clrhome.org/asm/">ORG</a> (z80 assembly IDE)</li>
<li><a href="https://github.com/mamacdon/orion-codemirror">Orion-CodeMirror integration</a> (running CodeMirror modes in Orion)</li>
<li><a href="https://github.com/mamacdon/orion-codemirror">Orion-CodeMirror integration</a> (running CodeMirror
modes in Orion)
</li>
<li><a href="http://paperjs.org/">Paper.js</a> (graphics scripting)</li>
<li><a href="http://pharaoh.js.org/">Pharaoh</a> (browser &amp; desktop editor for the classroom)</li>
<li><a href="http://prinbit.com/">PrinBit</a> (collaborative coding tool)</li>
<li><a href="https://www.pramp.com/ref/codemirror">Pramp</a> (free platform to practice mock interviews and coding problems)</li>
<li><a href="https://www.pramp.com/ref/codemirror">Pramp</a> (free platform to practice mock interviews and coding
problems)
</li>
<li><a href="http://prose.io/">Prose.io</a> (github content editor)</li>
<li><a href="https://pypi.python.org/pypi/PubliForge/">PubliForge</a> (online publishing system)</li>
<li><a href="http://www.puzzlescript.net/">Puzzlescript</a> (puzzle game engine)</li>
<li><a href="https://chrome.google.com/webstore/detail/quantum/hmnlklahndgbhdoclhdnoafhafbhmnkm?hl=en-US">Quantum</a> (code editor for Chrome OS)</li>
<li><a href="http://ariya.ofilabs.com/2011/09/hybrid-webnative-desktop-codemirror.html">Qt+Webkit integration</a> (building a desktop CodeMirror app)</li>
<li><a
href="https://chrome.google.com/webstore/detail/quantum/hmnlklahndgbhdoclhdnoafhafbhmnkm?hl=en-US">Quantum</a>
(code editor for Chrome OS)
</li>
<li><a href="http://ariya.ofilabs.com/2011/09/hybrid-webnative-desktop-codemirror.html">Qt+Webkit integration</a>
(building a desktop CodeMirror app)
</li>
<li><a href="http://www.quivive-file-manager.com">Quivive File Manager</a></li>
<li><a href="https://racktables.org">RackTables</a> (data centre resources manager)</li>
<li><a href="http://rascalmicro.com/docs/basic-tutorial-getting-started.html">Rascal</a> (tiny computer)</li>
<li><a href="https://www.realtime.io/">RealTime.io</a> (Internet-of-Things infrastructure)</li>
<li><a href="http://refork.com/">Refork</a> (animation demo gallery and sharing)</li>
<li><a href="http://sagecell.sagemath.org">SageMathCell</a> (interactive mathematical software)</li>
<li><a href="https://www.sass2css.online/">SASS2CSS</a> (SASS, SCSS or LESS to CSS converter and CSS beautifier)</li>
<li><a href="https://www.sass2css.online/">SASS2CSS</a> (SASS, SCSS or LESS to CSS converter and CSS beautifier)
</li>
<li><a href="https://cloud.sagemath.com/">SageMathCloud</a> (interactive mathematical software environment)</li>
<li><a href="https://github.com/szekelymilan/salvare">salvare</a> (real-time collaborative code editor)</li>
<li><a href="https://chrome.google.com/webstore/detail/servephp/mnpikomdchjhkhbhmbboehfdjkobbfpo">ServePHP</a> (PHP code testing in Chrome dev tools)</li>
<li><a href="https://chrome.google.com/webstore/detail/servephp/mnpikomdchjhkhbhmbboehfdjkobbfpo">ServePHP</a> (PHP
code testing in Chrome dev tools)
</li>
<li><a href="http://scala-lang.org/blog/2017/02/20/introducing-scastie.html">Scastie</a> (Scala playground)</li>
<li><a href="https://www.shadertoy.com/">Shadertoy</a> (shader sharing)</li>
<li><a href="http://www.sketchpatch.net/labs/livecodelabIntro.html">sketchPatch Livecodelab</a></li>
......@@ -170,7 +205,9 @@
<li><a href="http://www.cemetech.net/sc/">SourceCoder 3</a> (online calculator IDE and editor)</li>
<li><a href="http://sqlfiddle.com">SQLFiddle</a> (SQL playground)</li>
<li><a href="http://www.subte.org/page/programar-ta-te-ti-online/">SubTe</a> (AI bot programming environment)</li>
<li><a href="http://xuanji.appspot.com/isicp/">Structure and Interpretation of Computer Programs</a>, Interactive Version</li>
<li><a href="http://xuanji.appspot.com/isicp/">Structure and Interpretation of Computer Programs</a>, Interactive
Version
</li>
<li><a href="http://syframework.alwaysdata.net">SyBox</a> (PHP playground)</li>
<li><a href="http://www.tagspaces.org/">TagSpaces</a> (personal data manager)</li>
<li><a href="https://textbox.io/">Textbox.io</a> (WYSIWYG rich text editor)</a></li>
......@@ -178,11 +215,15 @@
<li><a href="http://www.mapbox.com/tilemill/">TileMill</a> (map design tool)</li>
<li><a href="http://doc.tiki.org/Syntax+Highlighter">Tiki</a> (wiki CMS groupware)</li>
<li><a href="https://www.tistory.com">Tistory</a> (blog service)</li>
<li><a href="http://www.toolsverse.com/products/data-explorer/">Toolsverse Data Explorer</a> (database management)</li>
<li><a href="http://www.toolsverse.com/products/data-explorer/">Toolsverse Data Explorer</a> (database management)
</li>
<li><a href="http://blog.englard.net/post/39608000629/codeintumblr">Tumblr code highlighting shim</a></li>
<li><a href="http://turbopy.com/">TurboPY</a> (web publishing framework)</li>
<li><a href="http://cruise.eecs.uottawa.ca/umpleonline/">UmpleOnline</a> (model-oriented programming tool)</li>
<li><a href="https://upsource.jetbrains.com/idea-ce/file/idea-ce-7706e7832aa9e2fd0c2decdb5cbef2225692c696/platform/platform-impl/src/com/intellij/openapi/editor/impl/EditorFactoryImpl.java">Upsource</a> (code browser and review tool)</li>
<li><a
href="https://upsource.jetbrains.com/idea-ce/file/idea-ce-7706e7832aa9e2fd0c2decdb5cbef2225692c696/platform/platform-impl/src/com/intellij/openapi/editor/impl/EditorFactoryImpl.java">Upsource</a>
(code browser and review tool)
</li>
<li><a href="https://violentmonkey.github.io/">Violentmonkey</a> (userscript manager / editor)</li>
<li><a href="https://github.com/mgaitan/waliki">Waliki</a> (wiki engine)</li>
<li><a href="http://wamer.net/">Wamer</a> (web application builder)</li>
......@@ -194,10 +235,15 @@
<li><a href="https://github.com/b3log/wide">Wide</a> (golang web IDE)</li>
<li><a href="http://wordpress.org/extend/plugins/codemirror-for-codeeditor/">WordPress plugin</a></li>
<li><a href="https://www.writelatex.com">writeLaTeX</a> (Collaborative LaTeX Editor)</li>
<li><a href="http://www.xosystem.org/home/applications_websites/xosystem_website/xoside_EN.php">XOSide</a> (online editor)</li>
<li><a href="http://www.xosystem.org/home/applications_websites/xosystem_website/xoside_EN.php">XOSide</a> (online
editor)
</li>
<li><a href="http://videlibri.sourceforge.net/cgi-bin/xidelcgi">XQuery tester</a></li>
<li><a href="http://q42jaap.github.io/xsd2codemirror/">xsd2codemirror</a> (convert XSD to CM XML completion info)</li>
<li><a href="http://www.yacas.org/yacas_online/yacas_online.html">Yacas Online</a> (interactive mathematical software)</li>
<li><a href="http://q42jaap.github.io/xsd2codemirror/">xsd2codemirror</a> (convert XSD to CM XML completion info)
</li>
<li><a href="http://www.yacas.org/yacas_online/yacas_online.html">Yacas Online</a> (interactive mathematical
software)
</li>
</ul>
</article>
......
This source diff could not be displayed because it is too large. You can view the blob instead.
......@@ -2,7 +2,7 @@
<title>CodeMirror: Reporting Bugs</title>
<meta charset="utf-8"/>
<link rel=stylesheet href="docs.css">
<link href="docs.css" rel=stylesheet>
<div id=nav>
<a href="https://codemirror.net"><h1>CodeMirror</h1><img id=logo src="logo.png"></a>
......@@ -19,42 +19,47 @@
<article>
<h2>Reporting bugs effectively</h2>
<h2>Reporting bugs effectively</h2>
<div class="left">
<div class="left">
<p>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:</p>
<p>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:</p>
<ol>
<ol>
<li>CodeMirror is maintained by volunteers. They don't owe you
anything, so be polite. Reports with an indignant or belligerent
tone tend to be moved to the bottom of the pile.</li>
tone tend to be moved to the bottom of the pile.
</li>
<li>Include information about <strong>the browser in which the
problem occurred</strong>. Even if you tested several browsers, and
the problem occurred in all of them, mention this fact in the bug
report. Also include browser version numbers and the operating
system that you're on.</li>
system that you're on.
</li>
<li>Mention which release of CodeMirror you're using. Preferably,
try also with the current development snapshot, to ensure the
problem has not already been fixed.</li>
problem has not already been fixed.
</li>
<li>Mention very precisely what went wrong. "X is broken" is not a
good bug report. What did you expect to happen? What happened
instead? Describe the exact steps a maintainer has to take to reproduce
the error. We can not fix something that we can not observe.</li>
the error. We can not fix something that we can not observe.
</li>
<li>If the problem can not be reproduced in any of the demos
included in the CodeMirror distribution, please provide an HTML
document that demonstrates the problem. The best way to do this is
to go to <a href="http://jsbin.com/ihunin/1/edit">jsbin.com</a>, enter
it there, press save, and include the resulting link in your bug
report.</li>
</ol>
report.
</li>
</ol>
</div>
</div>
</article>
......@@ -2,7 +2,7 @@
<title>CodeMirror: Version 2.2 upgrade guide</title>
<meta charset="utf-8"/>
<link rel=stylesheet href="docs.css">
<link href="docs.css" rel=stylesheet>
<div id=nav>
<a href="https://codemirror.net"><h1>CodeMirror</h1><img id=logo src="logo.png"></a>
......@@ -19,78 +19,78 @@
<article>
<h2>Upgrading to v2.2</h2>
<h2>Upgrading to v2.2</h2>
<p>There are a few things in the 2.2 release that require some care
when upgrading.</p>
<p>There are a few things in the 2.2 release that require some care
when upgrading.</p>
<h3>No more default.css</h3>
<h3>No more default.css</h3>
<p>The default theme is now included
in <a href="../lib/codemirror.css"><code>codemirror.css</code></a>, 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.)
<p>The default theme is now included
in <a href="../lib/codemirror.css"><code>codemirror.css</code></a>, 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.)
<h3>Different key customization</h3>
<h3>Different key customization</h3>
<p>CodeMirror has moved to a system
where <a href="manual.html#option_keyMap">keymaps</a> are used to
bind behavior to keys. This means <a href="../demo/emacs.html">custom
bindings</a> are now possible.</p>
<p>CodeMirror has moved to a system
where <a href="manual.html#option_keyMap">keymaps</a> are used to
bind behavior to keys. This means <a href="../demo/emacs.html">custom
bindings</a> are now possible.</p>
<p>Three options that influenced key
behavior, <code>tabMode</code>, <code>enterMode</code>,
and <code>smartHome</code>, are no longer supported. Instead, you can
provide custom bindings to influence the way these keys act. This is
done through the
new <a href="manual.html#option_extraKeys"><code>extraKeys</code></a>
option, which can hold an object mapping key names to functionality. A
simple example would be:</p>
<p>Three options that influenced key
behavior, <code>tabMode</code>, <code>enterMode</code>,
and <code>smartHome</code>, are no longer supported. Instead, you can
provide custom bindings to influence the way these keys act. This is
done through the
new <a href="manual.html#option_extraKeys"><code>extraKeys</code></a>
option, which can hold an object mapping key names to functionality. A
simple example would be:</p>
<pre> extraKeys: {
<pre> extraKeys: {
"Ctrl-S": function(instance) { saveText(instance.getValue()); },
"Ctrl-/": "undo"
}</pre>
<p>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 <code>CodeMirror.commands</code> table, which
contains all the built-in editing commands, and can be inspected and
extended by external code.</p>
<p>By default, the <code>Home</code> key is bound to
the <code>"goLineStartSmart"</code> 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:</p>
<pre> extraKeys: {"Home": "goLineStart"}</pre>
<p>Similarly, <code>Enter</code> is bound
to <code>"newlineAndIndent"</code> 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 <code>false</code>:</p>
<pre> extraKeys: {"Enter": false}</pre>
<p>The same works for <code>Tab</code>. If you don't want CodeMirror
to handle it, bind it to <code>false</code>. The default behaviour is
to indent the current line more (<code>"indentMore"</code> command),
and indent it less when shift is held (<code>"indentLess"</code>).
There are also <code>"indentAuto"</code> (smart indent)
and <code>"insertTab"</code> commands provided for alternate
behaviors. Or you can write your own handler function to do something
different altogether.</p>
<h3>Tabs</h3>
<p>Handling of tabs changed completely. The display width of tabs can
now be set with the <code>tabSize</code> option, and tabs can
be <a href="../demo/visibletabs.html">styled</a> by setting CSS rules
for the <code>cm-tab</code> class.</p>
<p>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 <code>tabSize: 8</code> in your options.</p>
<p>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 <code>CodeMirror.commands</code> table, which
contains all the built-in editing commands, and can be inspected and
extended by external code.</p>
<p>By default, the <code>Home</code> key is bound to
the <code>"goLineStartSmart"</code> 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:</p>
<pre> extraKeys: {"Home": "goLineStart"}</pre>
<p>Similarly, <code>Enter</code> is bound
to <code>"newlineAndIndent"</code> 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 <code>false</code>:</p>
<pre> extraKeys: {"Enter": false}</pre>
<p>The same works for <code>Tab</code>. If you don't want CodeMirror
to handle it, bind it to <code>false</code>. The default behaviour is
to indent the current line more (<code>"indentMore"</code> command),
and indent it less when shift is held (<code>"indentLess"</code>).
There are also <code>"indentAuto"</code> (smart indent)
and <code>"insertTab"</code> commands provided for alternate
behaviors. Or you can write your own handler function to do something
different altogether.</p>
<h3>Tabs</h3>
<p>Handling of tabs changed completely. The display width of tabs can
now be set with the <code>tabSize</code> option, and tabs can
be <a href="../demo/visibletabs.html">styled</a> by setting CSS rules
for the <code>cm-tab</code> class.</p>
<p>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 <code>tabSize: 8</code> in your options.</p>
</article>
......@@ -2,9 +2,9 @@
<title>CodeMirror: Version 3 upgrade guide</title>
<meta charset="utf-8"/>
<link rel=stylesheet href="docs.css">
<link href="docs.css" rel=stylesheet>
<script src="../lib/codemirror.js"></script>
<link rel="stylesheet" href="../lib/codemirror.css">
<link href="../lib/codemirror.css" rel="stylesheet">
<script src="../addon/runmode/runmode.js"></script>
<script src="../addon/runmode/colorize.js"></script>
<script src="../mode/javascript/javascript.js"></script>
......@@ -38,55 +38,55 @@
<article>
<h2 id=upgrade>Upgrading to version 3</h2>
<h2 id=upgrade>Upgrading to version 3</h2>
<p>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.</p>
<p>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.</p>
<p>Note that <strong>version 3 drops full support for Internet
Explorer 7</strong>. The editor will mostly work on that browser, but
it'll be significantly glitchy.</p>
<p>Note that <strong>version 3 drops full support for Internet
Explorer 7</strong>. The editor will mostly work on that browser, but
it'll be significantly glitchy.</p>
<section id=dom>
<section id=dom>
<h2>DOM structure</h2>
<p>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.</p>
<p>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.</p>
<p>Editor height is now set on the outer wrapper element (CSS
class <code>CodeMirror</code>), not on the scroller element
(<code>CodeMirror-scroll</code>).</p>
<p>Editor height is now set on the outer wrapper element (CSS
class <code>CodeMirror</code>), not on the scroller element
(<code>CodeMirror-scroll</code>).</p>
<p>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.</p>
<p>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.</p>
<p>See the <a href="manual.html#styling">styling section</a> of the
manual for more information.</p>
</section>
<section id=gutters>
<p>See the <a href="manual.html#styling">styling section</a> of the
manual for more information.</p>
</section>
<section id=gutters>
<h2>Gutter model</h2>
<p>In CodeMirror 2.x, there was a single gutter, and line markers
created with <code>setMarker</code> would have to somehow coexist with
the line numbers (if present). Version 3 allows you to specify an
array of gutters, <a href="manual.html#option_gutters">by class
name</a>,
use <a href="manual.html#setGutterMarker"><code>setGutterMarker</code></a>
to add or remove markers in individual gutters, and clear whole
gutters
with <a href="manual.html#clearGutter"><code>clearGutter</code></a>.
Gutter markers are now specified as DOM nodes, rather than HTML
snippets.</p>
<p>The gutters no longer horizontally scrolls along with the content.
The <code>fixedGutter</code> option was removed (since it is now the
only behavior).</p>
<pre data-lang="text/html">
<p>In CodeMirror 2.x, there was a single gutter, and line markers
created with <code>setMarker</code> would have to somehow coexist with
the line numbers (if present). Version 3 allows you to specify an
array of gutters, <a href="manual.html#option_gutters">by class
name</a>,
use <a href="manual.html#setGutterMarker"><code>setGutterMarker</code></a>
to add or remove markers in individual gutters, and clear whole
gutters
with <a href="manual.html#clearGutter"><code>clearGutter</code></a>.
Gutter markers are now specified as DOM nodes, rather than HTML
snippets.</p>
<p>The gutters no longer horizontally scrolls along with the content.
The <code>fixedGutter</code> option was removed (since it is now the
only behavior).</p>
<pre data-lang="text/html">
&lt;style>
/* Define a gutter style */
.note-gutter { width: 3em; background: cyan; }
......@@ -101,38 +101,38 @@ only behavior).</p>
cm.setGutterMarker(0, "note-gutter", document.createTextNode("hi"));
&lt;/script>
</pre>
</section>
<section id=events>
</section>
<section id=events>
<h2>Event handling</h2>
<p>Most of the <code>onXYZ</code> options have been removed. The same
effect is now obtained by calling
the <a href="manual.html#on"><code>on</code></a> 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 <a href="manual.html#events">the
full list here</a>.</p>
<p>Most of the <code>onXYZ</code> options have been removed. The same
effect is now obtained by calling
the <a href="manual.html#on"><code>on</code></a> 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 <a href="manual.html#events">the
full list here</a>.</p>
<p>(The <code>onKeyEvent</code> and <code>onDragEvent</code> options,
which act more as hooks than as event handlers, are still there in
their old form.)</p>
<p>(The <code>onKeyEvent</code> and <code>onDragEvent</code> options,
which act more as hooks than as event handlers, are still there in
their old form.)</p>
<pre data-lang="javascript">
<pre data-lang="javascript">
cm.on("change", function(cm, change) {
console.log("something changed! (" + change.origin + ")");
});
</pre>
</section>
<section id=marktext>
</section>
<section id=marktext>
<h2>markText method arguments</h2>
<p>The <a href="manual.html#markText"><code>markText</code></a> 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.</p>
<p>The <a href="manual.html#markText"><code>markText</code></a> 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.</p>
<pre data-lang="javascript">
<pre data-lang="javascript">
// Style first ten lines, and forbid the cursor from entering them
cm.markText({line: 0, ch: 0}, {line: 10, ch: 0}, {
className: "magic-text",
......@@ -140,18 +140,18 @@ cm.markText({line: 0, ch: 0}, {line: 10, ch: 0}, {
atomic: true
});
</pre>
</section>
<section id=folding>
</section>
<section id=folding>
<h2>Line folding</h2>
<p>The interface for hiding lines has been
removed. <a href="manual.html#markText"><code>markText</code></a> can
now be used to do the same in a more flexible and powerful way.</p>
<p>The interface for hiding lines has been
removed. <a href="manual.html#markText"><code>markText</code></a> can
now be used to do the same in a more flexible and powerful way.</p>
<p>The <a href="../demo/folding.html">folding script</a> has been
updated to use the new interface, and should now be more robust.</p>
<p>The <a href="../demo/folding.html">folding script</a> has been
updated to use the new interface, and should now be more robust.</p>
<pre data-lang="javascript">
<pre data-lang="javascript">
// Fold a range, replacing it with the text "??"
var range = cm.markText({line: 4, ch: 2}, {line: 8, ch: 1}, {
replacedWith: document.createTextNode("??"),
......@@ -163,68 +163,75 @@ CodeMirror.on(range, "clear", function() {
console.log("boom");
});
</pre>
</section>
<section id=lineclass>
</section>
<section id=lineclass>
<h2>Line CSS classes</h2>
<p>The <code>setLineClass</code> method has been replaced
by <a href="manual.html#addLineClass"><code>addLineClass</code></a>
and <a href="manual.html#removeLineClass"><code>removeLineClass</code></a>,
which allow more modular control over the classes attached to a line.</p>
<p>The <code>setLineClass</code> method has been replaced
by <a href="manual.html#addLineClass"><code>addLineClass</code></a>
and <a href="manual.html#removeLineClass"><code>removeLineClass</code></a>,
which allow more modular control over the classes attached to a line.</p>
<pre data-lang="javascript">
<pre data-lang="javascript">
var marked = cm.addLineClass(10, "background", "highlighted-line");
setTimeout(function() {
cm.removeLineClass(marked, "background", "highlighted-line");
});
</pre>
</section>
<section id=positions>
</section>
<section id=positions>
<h2>Position properties</h2>
<p>All methods that take or return objects that represent screen
positions now use <code>{left, top, bottom, right}</code> properties
(not always all of them) instead of the <code>{x, y, yBot}</code> used
by some methods in v2.x.</p>
<p>Affected methods
are <a href="manual.html#cursorCoords"><code>cursorCoords</code></a>, <a href="manual.html#charCoords"><code>charCoords</code></a>, <a href="manual.html#coordsChar"><code>coordsChar</code></a>,
and <a href="manual.html#getScrollInfo"><code>getScrollInfo</code></a>.</p>
</section>
<section id=matchbrackets>
<p>All methods that take or return objects that represent screen
positions now use <code>{left, top, bottom, right}</code> properties
(not always all of them) instead of the <code>{x, y, yBot}</code> used
by some methods in v2.x.</p>
<p>Affected methods
are <a href="manual.html#cursorCoords"><code>cursorCoords</code></a>, <a href="manual.html#charCoords"><code>charCoords</code></a>,
<a href="manual.html#coordsChar"><code>coordsChar</code></a>,
and <a href="manual.html#getScrollInfo"><code>getScrollInfo</code></a>.</p>
</section>
<section id=matchbrackets>
<h2>Bracket matching no longer in core</h2>
<p>The <a href="manual.html#addon_matchbrackets"><code>matchBrackets</code></a>
option is no longer defined in the core editor.
Load <code>addon/edit/matchbrackets.js</code> to enable it.</p>
</section>
<section id=modes>
<p>The <a href="manual.html#addon_matchbrackets"><code>matchBrackets</code></a>
option is no longer defined in the core editor.
Load <code>addon/edit/matchbrackets.js</code> to enable it.</p>
</section>
<section id=modes>
<h2>Mode management</h2>
<p>The <code>CodeMirror.listModes</code>
and <code>CodeMirror.listMIMEs</code> functions, used for listing
defined modes, are gone. You are now encouraged to simply
inspect <code>CodeMirror.modes</code> (mapping mode names to mode
constructors) and <code>CodeMirror.mimeModes</code> (mapping MIME
strings to mode specs).</p>
</section>
<section id=new>
<p>The <code>CodeMirror.listModes</code>
and <code>CodeMirror.listMIMEs</code> functions, used for listing
defined modes, are gone. You are now encouraged to simply
inspect <code>CodeMirror.modes</code> (mapping mode names to mode
constructors) and <code>CodeMirror.mimeModes</code> (mapping MIME
strings to mode specs).</p>
</section>
<section id=new>
<h2>New features</h2>
<p>Some more reasons to upgrade to version 3.</p>
<p>Some more reasons to upgrade to version 3.</p>
<ul>
<ul>
<li>Bi-directional text support. CodeMirror will now mostly do the
right thing when editing Arabic or Hebrew text.</li>
right thing when editing Arabic or Hebrew text.
</li>
<li>Arbitrary line heights. Using fonts with different heights
inside the editor (whether off by one pixel or fifty) is now
supported and handled gracefully.</li>
supported and handled gracefully.
</li>
<li>In-line widgets. See <a href="../demo/widget.html">the demo</a>
and <a href="manual.html#addLineWidget">the docs</a>.</li>
and <a href="manual.html#addLineWidget">the docs</a>.
</li>
<li>Defining custom options
with <a href="manual.html#defineOption"><code>CodeMirror.defineOption</code></a>.</li>
</ul>
</section>
with <a href="manual.html#defineOption"><code>CodeMirror.defineOption</code></a>.
</li>
</ul>
</section>
</article>
<script>setTimeout(function(){CodeMirror.colorize();}, 20);</script>
<script>setTimeout(function () {
CodeMirror.colorize();
}, 20);</script>
......@@ -2,7 +2,7 @@
<title>CodeMirror: Version 4 upgrade guide</title>
<meta charset="utf-8"/>
<link rel=stylesheet href="docs.css">
<link href="docs.css" rel=stylesheet>
<script src="activebookmark.js"></script>
<div id=nav>
......@@ -28,117 +28,117 @@
<article>
<h2 id=upgrade>Upgrading to version 4</h2>
<h2 id=upgrade>Upgrading to version 4</h2>
<p>CodeMirror 4's interface is <em>very</em> close version 3, but it
does fix a few awkward details in a backwards-incompatible ways. At
least skim the text below before upgrading.</p>
<p>CodeMirror 4's interface is <em>very</em> close version 3, but it
does fix a few awkward details in a backwards-incompatible ways. At
least skim the text below before upgrading.</p>
<section id=multisel><h2>Multiple selections</h2>
<section id=multisel><h2>Multiple selections</h2>
<p>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 <em>primary</em> selection (usually the last
one added).</p>
<p>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 <em>primary</em> selection (usually the last
one added).</p>
<p>The exception to this
is <a href="manual.html#getSelection"><strong><code>getSelection</code></strong></a>,
which will now return the content of <em>all</em> selections
(separated by newlines, or whatever <code>lineSep</code> parameter you passed
it).</p>
<p>The exception to this
is <a href="manual.html#getSelection"><strong><code>getSelection</code></strong></a>,
which will now return the content of <em>all</em> selections
(separated by newlines, or whatever <code>lineSep</code> parameter you passed
it).</p>
</section>
</section>
<section id=beforeSelectionChange><h2>The beforeSelectionChange event</h2>
<section id=beforeSelectionChange><h2>The beforeSelectionChange event</h2>
<p>This event still exists, but the object it is passed has
a <a href="manual.html#event_beforeSelectionChange">completely new
interface</a>, because such changes now concern multiple
selections.</p>
<p>This event still exists, but the object it is passed has
a <a href="manual.html#event_beforeSelectionChange">completely new
interface</a>, because such changes now concern multiple
selections.</p>
</section>
</section>
<section id=replaceSelection><h2>replaceSelection's collapsing behavior</h2>
<section id=replaceSelection><h2>replaceSelection's collapsing behavior</h2>
<p>By
default, <a href="manual.html#replaceSelection"><code>replaceSelection</code></a>
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 <code>"end"</code>, meaning the old behavior
must be explicitly specified by passing a second argument
of <code>"around"</code>.</p>
<p>By
default, <a href="manual.html#replaceSelection"><code>replaceSelection</code></a>
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 <code>"end"</code>, meaning the old behavior
must be explicitly specified by passing a second argument
of <code>"around"</code>.</p>
</section>
</section>
<section id=changeEvent><h2>change event data</h2>
<section id=changeEvent><h2>change event data</h2>
<p>Rather than forcing client code to follow <code>next</code>
pointers from one change object to the next, the library will now
simply fire
multiple <a href="manual.html#event_change"><code>"change"</code></a>
events. Existing code will probably continue to work unmodified.</p>
<p>Rather than forcing client code to follow <code>next</code>
pointers from one change object to the next, the library will now
simply fire
multiple <a href="manual.html#event_change"><code>"change"</code></a>
events. Existing code will probably continue to work unmodified.</p>
</section>
</section>
<section id=showIfHidden><h2>showIfHidden option to line widgets</h2>
<section id=showIfHidden><h2>showIfHidden option to line widgets</h2>
<p>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.</p>
<p>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.</p>
</section>
</section>
<section id=module><h2>Module loaders</h2>
<section id=module><h2>Module loaders</h2>
<p>All modules in the CodeMirror distribution are now wrapped in a
shim function to make them compatible with both AMD
(<a href="http://requirejs.org">requirejs</a>) and CommonJS (as used
by <a href="http://nodejs.org/">node</a>
and <a href="http://browserify.org/">browserify</a>) module loaders.
When neither of these is present, they fall back to simply using the
global <code>CodeMirror</code> variable.</p>
<p>All modules in the CodeMirror distribution are now wrapped in a
shim function to make them compatible with both AMD
(<a href="http://requirejs.org">requirejs</a>) and CommonJS (as used
by <a href="http://nodejs.org/">node</a>
and <a href="http://browserify.org/">browserify</a>) module loaders.
When neither of these is present, they fall back to simply using the
global <code>CodeMirror</code> variable.</p>
<p>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.</p>
<p>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.</p>
</section>
</section>
<section id=shareddata><h2>Mutating shared data structures</h2>
<section id=shareddata><h2>Mutating shared data structures</h2>
<p>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 <a href="manual.html#getCursor"><code>getCursor</code></a>
for nebulous, historic reasons. In 4.0, mutating these
objects <em>will</em> corrupt your editor's selection.</p>
<p>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 <a href="manual.html#getCursor"><code>getCursor</code></a>
for nebulous, historic reasons. In 4.0, mutating these
objects <em>will</em> corrupt your editor's selection.</p>
</section>
</section>
<section id=deprecated><h2>Deprecated interfaces dropped</h2>
<section id=deprecated><h2>Deprecated interfaces dropped</h2>
<p>A few properties and methods that have been deprecated for a while
are now gone. Most notably, the <code>onKeyEvent</code>
and <code>onDragEvent</code> options (use the
corresponding <a href="manual.html#event_dom">events</a> instead).</p>
<p>A few properties and methods that have been deprecated for a while
are now gone. Most notably, the <code>onKeyEvent</code>
and <code>onDragEvent</code> options (use the
corresponding <a href="manual.html#event_dom">events</a> instead).</p>
<p>Two silly methods, which were mostly there to stay close to the 0.x
API, <code>setLine</code> and <code>removeLine</code> are now gone.
Use the more
flexible <a href="manual.html#replaceRange"><code>replaceRange</code></a>
method instead.</p>
<p>Two silly methods, which were mostly there to stay close to the 0.x
API, <code>setLine</code> and <code>removeLine</code> are now gone.
Use the more
flexible <a href="manual.html#replaceRange"><code>replaceRange</code></a>
method instead.</p>
<p>The long names for folding and completing functions
(<code>CodeMirror.braceRangeFinder</code>, <code>CodeMirror.javascriptHint</code>,
etc) are also gone
(use <code>CodeMirror.fold.brace</code>, <code>CodeMirror.hint.javascript</code>).</p>
<p>The long names for folding and completing functions
(<code>CodeMirror.braceRangeFinder</code>, <code>CodeMirror.javascriptHint</code>,
etc) are also gone
(use <code>CodeMirror.fold.brace</code>, <code>CodeMirror.hint.javascript</code>).</p>
<p>The <code>className</code> property in the return value
of <a href="manual.html#getTokenAt"><code>getTokenAt</code></a>, which
has been superseded by the <code>type</code> property, is also no
longer present.</p>
<p>The <code>className</code> property in the return value
of <a href="manual.html#getTokenAt"><code>getTokenAt</code></a>, which
has been superseded by the <code>type</code> property, is also no
longer present.</p>
</section>
</section>
</article>
......@@ -13,6 +13,7 @@
.CodeMirror-lines {
padding: 4px 0; /* Vertical padding around content */
}
.CodeMirror pre.CodeMirror-line,
.CodeMirror pre.CodeMirror-line-like {
padding: 0 4px; /* Horizontal padding of content */
......@@ -29,7 +30,10 @@
background-color: #f7f7f7;
white-space: nowrap;
}
.CodeMirror-linenumbers {}
.CodeMirror-linenumbers {
}
.CodeMirror-linenumber {
padding: 0 3px 0 5px;
min-width: 20px;
......@@ -38,8 +42,13 @@
white-space: nowrap;
}
.CodeMirror-guttermarker { color: black; }
.CodeMirror-guttermarker-subtle { color: #999; }
.CodeMirror-guttermarker {
color: black;
}
.CodeMirror-guttermarker-subtle {
color: #999;
}
/* CURSOR */
......@@ -48,24 +57,29 @@
border-right: none;
width: 0;
}
/* Shown when moving in bi-directional text */
.CodeMirror div.CodeMirror-secondarycursor {
border-left: 1px solid silver;
}
.cm-fat-cursor .CodeMirror-cursor {
width: auto;
border: 0 !important;
background: #7e7;
}
.cm-fat-cursor div.CodeMirror-cursors {
z-index: 1;
}
.cm-fat-cursor-mark {
background-color: rgba(20, 255, 20, 0.5);
-webkit-animation: blink 1.06s steps(1) infinite;
-moz-animation: blink 1.06s steps(1) infinite;
animation: blink 1.06s steps(1) infinite;
}
.cm-animate-fat-cursor {
width: auto;
border: 0;
......@@ -74,82 +88,199 @@
animation: blink 1.06s steps(1) infinite;
background-color: #7e7;
}
@-moz-keyframes blink {
0% {}
50% { background-color: transparent; }
100% {}
0% {
}
50% {
background-color: transparent;
}
100% {
}
}
@-webkit-keyframes blink {
0% {}
50% { background-color: transparent; }
100% {}
0% {
}
50% {
background-color: transparent;
}
100% {
}
}
@keyframes blink {
0% {}
50% { background-color: transparent; }
100% {}
0% {
}
50% {
background-color: transparent;
}
100% {
}
}
/* Can style cursor different in overwrite (non-insert) mode */
.CodeMirror-overwrite .CodeMirror-cursor {}
.CodeMirror-overwrite .CodeMirror-cursor {
}
.cm-tab { display: inline-block; text-decoration: inherit; }
.cm-tab {
display: inline-block;
text-decoration: inherit;
}
.CodeMirror-rulers {
position: absolute;
left: 0; right: 0; top: -50px; bottom: 0;
left: 0;
right: 0;
top: -50px;
bottom: 0;
overflow: hidden;
}
.CodeMirror-ruler {
border-left: 1px solid #ccc;
top: 0; bottom: 0;
top: 0;
bottom: 0;
position: absolute;
}
/* DEFAULT THEME */
.cm-s-default .cm-header {color: blue;}
.cm-s-default .cm-quote {color: #090;}
.cm-negative {color: #d44;}
.cm-positive {color: #292;}
.cm-header, .cm-strong {font-weight: bold;}
.cm-em {font-style: italic;}
.cm-link {text-decoration: underline;}
.cm-strikethrough {text-decoration: line-through;}
.cm-s-default .cm-keyword {color: #708;}
.cm-s-default .cm-atom {color: #219;}
.cm-s-default .cm-number {color: #164;}
.cm-s-default .cm-def {color: #00f;}
.cm-s-default .cm-header {
color: blue;
}
.cm-s-default .cm-quote {
color: #090;
}
.cm-negative {
color: #d44;
}
.cm-positive {
color: #292;
}
.cm-header, .cm-strong {
font-weight: bold;
}
.cm-em {
font-style: italic;
}
.cm-link {
text-decoration: underline;
}
.cm-strikethrough {
text-decoration: line-through;
}
.cm-s-default .cm-keyword {
color: #708;
}
.cm-s-default .cm-atom {
color: #219;
}
.cm-s-default .cm-number {
color: #164;
}
.cm-s-default .cm-def {
color: #00f;
}
.cm-s-default .cm-variable,
.cm-s-default .cm-punctuation,
.cm-s-default .cm-property,
.cm-s-default .cm-operator {}
.cm-s-default .cm-variable-2 {color: #05a;}
.cm-s-default .cm-variable-3, .cm-s-default .cm-type {color: #085;}
.cm-s-default .cm-comment {color: #a50;}
.cm-s-default .cm-string {color: #a11;}
.cm-s-default .cm-string-2 {color: #f50;}
.cm-s-default .cm-meta {color: #555;}
.cm-s-default .cm-qualifier {color: #555;}
.cm-s-default .cm-builtin {color: #30a;}
.cm-s-default .cm-bracket {color: #997;}
.cm-s-default .cm-tag {color: #170;}
.cm-s-default .cm-attribute {color: #00c;}
.cm-s-default .cm-hr {color: #999;}
.cm-s-default .cm-link {color: #00c;}
.cm-s-default .cm-error {color: #f00;}
.cm-invalidchar {color: #f00;}
.CodeMirror-composing { border-bottom: 2px solid; }
.cm-s-default .cm-operator {
}
.cm-s-default .cm-variable-2 {
color: #05a;
}
.cm-s-default .cm-variable-3, .cm-s-default .cm-type {
color: #085;
}
.cm-s-default .cm-comment {
color: #a50;
}
.cm-s-default .cm-string {
color: #a11;
}
.cm-s-default .cm-string-2 {
color: #f50;
}
.cm-s-default .cm-meta {
color: #555;
}
.cm-s-default .cm-qualifier {
color: #555;
}
.cm-s-default .cm-builtin {
color: #30a;
}
.cm-s-default .cm-bracket {
color: #997;
}
.cm-s-default .cm-tag {
color: #170;
}
.cm-s-default .cm-attribute {
color: #00c;
}
.cm-s-default .cm-hr {
color: #999;
}
.cm-s-default .cm-link {
color: #00c;
}
.cm-s-default .cm-error {
color: #f00;
}
.cm-invalidchar {
color: #f00;
}
.CodeMirror-composing {
border-bottom: 2px solid;
}
/* Default styles for common addons */
div.CodeMirror span.CodeMirror-matchingbracket {color: #0b0;}
div.CodeMirror span.CodeMirror-nonmatchingbracket {color: #a22;}
.CodeMirror-matchingtag { background: rgba(255, 150, 0, .3); }
.CodeMirror-activeline-background {background: #e8f2ff;}
div.CodeMirror span.CodeMirror-matchingbracket {
color: #0b0;
}
div.CodeMirror span.CodeMirror-nonmatchingbracket {
color: #a22;
}
.CodeMirror-matchingtag {
background: rgba(255, 150, 0, .3);
}
.CodeMirror-activeline-background {
background: #e8f2ff;
}
/* STOP */
......@@ -166,12 +297,14 @@ div.CodeMirror span.CodeMirror-nonmatchingbracket {color: #a22;}
overflow: scroll !important; /* Things will break if this is overridden */
/* 50px is the magic margin used to hide the element's real scrollbars */
/* See overflow: hidden in .CodeMirror */
margin-bottom: -50px; margin-right: -50px;
margin-bottom: -50px;
margin-right: -50px;
padding-bottom: 50px;
height: 100%;
outline: none; /* Prevent dragging from highlighting the element */
position: relative;
}
.CodeMirror-sizer {
position: relative;
border-right: 50px solid transparent;
......@@ -186,28 +319,39 @@ div.CodeMirror span.CodeMirror-nonmatchingbracket {color: #a22;}
display: none;
outline: none;
}
.CodeMirror-vscrollbar {
right: 0; top: 0;
right: 0;
top: 0;
overflow-x: hidden;
overflow-y: scroll;
}
.CodeMirror-hscrollbar {
bottom: 0; left: 0;
bottom: 0;
left: 0;
overflow-y: hidden;
overflow-x: scroll;
}
.CodeMirror-scrollbar-filler {
right: 0; bottom: 0;
right: 0;
bottom: 0;
}
.CodeMirror-gutter-filler {
left: 0; bottom: 0;
left: 0;
bottom: 0;
}
.CodeMirror-gutters {
position: absolute; left: 0; top: 0;
position: absolute;
left: 0;
top: 0;
min-height: 100%;
z-index: 3;
}
.CodeMirror-gutter {
white-space: normal;
height: 100%;
......@@ -215,33 +359,46 @@ div.CodeMirror span.CodeMirror-nonmatchingbracket {color: #a22;}
vertical-align: top;
margin-bottom: -50px;
}
.CodeMirror-gutter-wrapper {
position: absolute;
z-index: 4;
background: none !important;
border: none !important;
}
.CodeMirror-gutter-background {
position: absolute;
top: 0; bottom: 0;
top: 0;
bottom: 0;
z-index: 4;
}
.CodeMirror-gutter-elt {
position: absolute;
cursor: default;
z-index: 4;
}
.CodeMirror-gutter-wrapper ::selection { background-color: transparent }
.CodeMirror-gutter-wrapper ::-moz-selection { background-color: transparent }
.CodeMirror-gutter-wrapper ::selection {
background-color: transparent
}
.CodeMirror-gutter-wrapper ::-moz-selection {
background-color: transparent
}
.CodeMirror-lines {
cursor: text;
min-height: 1px; /* prevents collapsing before first draw */
}
.CodeMirror pre.CodeMirror-line,
.CodeMirror pre.CodeMirror-line-like {
/* Reset some styles that the rest of the page might have set */
-moz-border-radius: 0; -webkit-border-radius: 0; border-radius: 0;
-moz-border-radius: 0;
-webkit-border-radius: 0;
border-radius: 0;
border-width: 0;
background: transparent;
font-family: inherit;
......@@ -258,6 +415,7 @@ div.CodeMirror span.CodeMirror-nonmatchingbracket {color: #a22;}
-webkit-font-variant-ligatures: contextual;
font-variant-ligatures: contextual;
}
.CodeMirror-wrap pre.CodeMirror-line,
.CodeMirror-wrap pre.CodeMirror-line-like {
word-wrap: break-word;
......@@ -267,7 +425,10 @@ div.CodeMirror span.CodeMirror-nonmatchingbracket {color: #a22;}
.CodeMirror-linebackground {
position: absolute;
left: 0; right: 0; top: 0; bottom: 0;
left: 0;
right: 0;
top: 0;
bottom: 0;
z-index: 0;
}
......@@ -277,9 +438,12 @@ div.CodeMirror span.CodeMirror-nonmatchingbracket {color: #a22;}
padding: 0.1px; /* Force widget margins to stay inside of the container */
}
.CodeMirror-widget {}
.CodeMirror-widget {
}
.CodeMirror-rtl pre { direction: rtl; }
.CodeMirror-rtl pre {
direction: rtl;
}
.CodeMirror-code {
outline: none;
......@@ -307,13 +471,17 @@ div.CodeMirror span.CodeMirror-nonmatchingbracket {color: #a22;}
position: absolute;
pointer-events: none;
}
.CodeMirror-measure pre { position: static; }
.CodeMirror-measure pre {
position: static;
}
div.CodeMirror-cursors {
visibility: hidden;
position: relative;
z-index: 3;
}
div.CodeMirror-dragcursors {
visibility: visible;
}
......@@ -322,11 +490,25 @@ div.CodeMirror-dragcursors {
visibility: visible;
}
.CodeMirror-selected { background: #d9d9d9; }
.CodeMirror-focused .CodeMirror-selected { background: #d7d4f0; }
.CodeMirror-crosshair { cursor: crosshair; }
.CodeMirror-line::selection, .CodeMirror-line > span::selection, .CodeMirror-line > span > span::selection { background: #d7d4f0; }
.CodeMirror-line::-moz-selection, .CodeMirror-line > span::-moz-selection, .CodeMirror-line > span > span::-moz-selection { background: #d7d4f0; }
.CodeMirror-selected {
background: #d9d9d9;
}
.CodeMirror-focused .CodeMirror-selected {
background: #d7d4f0;
}
.CodeMirror-crosshair {
cursor: crosshair;
}
.CodeMirror-line::selection, .CodeMirror-line > span::selection, .CodeMirror-line > span > span::selection {
background: #d7d4f0;
}
.CodeMirror-line::-moz-selection, .CodeMirror-line > span::-moz-selection, .CodeMirror-line > span > span::-moz-selection {
background: #d7d4f0;
}
.cm-searching {
background-color: #ffa;
......@@ -334,7 +516,9 @@ div.CodeMirror-dragcursors {
}
/* Used to force a border model for a node */
.cm-force-border { padding-right: .1px; }
.cm-force-border {
padding-right: .1px;
}
@media print {
/* Hide the cursor when printing */
......@@ -344,7 +528,11 @@ div.CodeMirror-dragcursors {
}
/* See issue #2901 */
.cm-tab-wrap-hack:after { content: ''; }
.cm-tab-wrap-hack:after {
content: '';
}
/* Help users use markselection to safely style text background */
span.CodeMirror-selectedtext { background: none; }
span.CodeMirror-selectedtext {
background: none;
}
This source diff could not be displayed because it is too large. You can view the blob instead.
// CodeMirror, copyright (c) by Marijn Haverbeke and others
// Distributed under an MIT license: https://codemirror.net/LICENSE
(function(mod) {
(function (mod) {
if (typeof exports == "object" && typeof module == "object") // CommonJS
mod(require("../../lib/codemirror"));
else if (typeof define == "function" && define.amd) // AMD
define(["../../lib/codemirror"], mod);
else // Plain browser env
mod(CodeMirror);
})(function(CodeMirror) {
"use strict";
})(function (CodeMirror) {
"use strict";
CodeMirror.defineMode("apl", function() {
CodeMirror.defineMode("apl", function () {
var builtInOps = {
".": "innerProduct",
"\\": "scan",
......@@ -80,10 +80,10 @@ CodeMirror.defineMode("apl", function() {
var isArrow = /←/;
var isComment = /[⍝#].*$/;
var stringEater = function(type) {
var stringEater = function (type) {
var prev;
prev = false;
return function(c) {
return function (c) {
prev = c;
if (c === type) {
return prev === "\\";
......@@ -92,7 +92,7 @@ CodeMirror.defineMode("apl", function() {
};
};
return {
startState: function() {
startState: function () {
return {
prev: false,
func: false,
......@@ -101,7 +101,7 @@ CodeMirror.defineMode("apl", function() {
escape: false
};
},
token: function(stream, state) {
token: function (stream, state) {
var ch, funcName;
if (stream.eatSpace()) {
return null;
......@@ -167,8 +167,8 @@ CodeMirror.defineMode("apl", function() {
return "keyword";
}
};
});
});
CodeMirror.defineMIME("text/apl", "apl");
CodeMirror.defineMIME("text/apl", "apl");
});
......@@ -2,17 +2,19 @@
<title>CodeMirror: APL mode</title>
<meta charset="utf-8"/>
<link rel=stylesheet href="../../doc/docs.css">
<link href="../../doc/docs.css" rel=stylesheet>
<link rel="stylesheet" href="../../lib/codemirror.css">
<link href="../../lib/codemirror.css" rel="stylesheet">
<script src="../../lib/codemirror.js"></script>
<script src="../../addon/edit/matchbrackets.js"></script>
<script src="./apl.js"></script>
<style>
.CodeMirror { border: 2px inset #dee; }
</style>
.CodeMirror {
border: 2px inset #dee;
}
</style>
<div id=nav>
<a href="https://codemirror.net"><h1>CodeMirror</h1><img id=logo src="../../doc/logo.png" alt=""></a>
<a href="https://codemirror.net"><h1>CodeMirror</h1><img alt="" id=logo src="../../doc/logo.png"></a>
<ul>
<li><a href="../../index.html">Home</a>
......@@ -26,8 +28,8 @@
</div>
<article>
<h2>APL mode</h2>
<form><textarea id="code" name="code">
<h2>APL mode</h2>
<form><textarea id="code" name="code">
⍝ Conway's game of life
⍝ This example was inspired by the impressive demo at
......@@ -69,4 +71,4 @@ gen ← {' #'[(life ⍣ ⍵) board]}
have popups etc.</p>
<p><strong>MIME types defined:</strong> <code>text/apl</code> (APL code)</p>
</article>
</article>
// CodeMirror, copyright (c) by Marijn Haverbeke and others
// Distributed under an MIT license: https://codemirror.net/LICENSE
(function(mod) {
(function (mod) {
if (typeof exports == "object" && typeof module == "object") // CommonJS
mod(require("../../lib/codemirror"));
else if (typeof define == "function" && define.amd) // AMD
define(["../../lib/codemirror"], mod);
else // Plain browser env
mod(CodeMirror);
})(function(CodeMirror) {
"use strict";
})(function (CodeMirror) {
"use strict";
CodeMirror.defineMode("css", function(config, parserConfig) {
CodeMirror.defineMode("css", function (config, parserConfig) {
var inline = parserConfig.inline
if (!parserConfig.propertyKeywords) parserConfig = CodeMirror.resolveMode("text/css");
......@@ -33,7 +33,11 @@ CodeMirror.defineMode("css", function(config, parserConfig) {
highlightNonStandardPropertyKeywords = config.highlightNonStandardPropertyKeywords !== false;
var type, override;
function ret(style, tp) { type = tp; return style; }
function ret(style, tp) {
type = tp;
return style;
}
// Tokenizers
......@@ -92,7 +96,7 @@ CodeMirror.defineMode("css", function(config, parserConfig) {
}
function tokenString(quote) {
return function(stream, state) {
return function (stream, state) {
var escaped = false, ch;
while ((ch = stream.next()) != null) {
if (ch == quote && !escaped) {
......@@ -137,6 +141,7 @@ CodeMirror.defineMode("css", function(config, parserConfig) {
function pass(type, stream, state) {
return states[state.context.type](type, stream, state);
}
function popAndPass(type, stream, state, n) {
for (var i = n || 1; i > 0; i--)
state.context = state.context.prev;
......@@ -157,7 +162,7 @@ CodeMirror.defineMode("css", function(config, parserConfig) {
var states = {};
states.top = function(type, stream, state) {
states.top = function (type, stream, state) {
if (type == "{") {
return pushContext(state, stream, "block");
} else if (type == "}" && state.context.prev) {
......@@ -191,7 +196,7 @@ CodeMirror.defineMode("css", function(config, parserConfig) {
return state.context.type;
};
states.block = function(type, stream, state) {
states.block = function (type, stream, state) {
if (type == "word") {
var word = stream.current().toLowerCase();
if (propertyKeywords.hasOwnProperty(word)) {
......@@ -217,12 +222,12 @@ CodeMirror.defineMode("css", function(config, parserConfig) {
}
};
states.maybeprop = function(type, stream, state) {
states.maybeprop = function (type, stream, state) {
if (type == ":") return pushContext(state, stream, "prop");
return pass(type, stream, state);
};
states.prop = function(type, stream, state) {
states.prop = function (type, stream, state) {
if (type == ";") return popContext(state);
if (type == "{" && allowNested) return pushContext(state, stream, "propBlock");
if (type == "}" || type == "{") return popAndPass(type, stream, state);
......@@ -238,13 +243,16 @@ CodeMirror.defineMode("css", function(config, parserConfig) {
return "prop";
};
states.propBlock = function(type, _stream, state) {
states.propBlock = function (type, _stream, state) {
if (type == "}") return popContext(state);
if (type == "word") { override = "property"; return "maybeprop"; }
if (type == "word") {
override = "property";
return "maybeprop";
}
return state.context.type;
};
states.parens = function(type, stream, state) {
states.parens = function (type, stream, state) {
if (type == "{" || type == "}") return popAndPass(type, stream, state);
if (type == ")") return popContext(state);
if (type == "(") return pushContext(state, stream, "parens");
......@@ -253,7 +261,7 @@ CodeMirror.defineMode("css", function(config, parserConfig) {
return "parens";
};
states.pseudo = function(type, stream, state) {
states.pseudo = function (type, stream, state) {
if (type == "meta") return "pseudo";
if (type == "word") {
......@@ -263,7 +271,7 @@ CodeMirror.defineMode("css", function(config, parserConfig) {
return pass(type, stream, state);
};
states.documentTypes = function(type, stream, state) {
states.documentTypes = function (type, stream, state) {
if (type == "word" && documentTypes.hasOwnProperty(stream.current())) {
override = "tag";
return state.context.type;
......@@ -272,7 +280,7 @@ CodeMirror.defineMode("css", function(config, parserConfig) {
}
};
states.atBlock = function(type, stream, state) {
states.atBlock = function (type, stream, state) {
if (type == "(") return pushContext(state, stream, "atBlock_parens");
if (type == "}" || type == ";") return popAndPass(type, stream, state);
if (type == "{") return popContext(state) && pushContext(state, stream, allowNested ? "block" : "top");
......@@ -303,7 +311,7 @@ CodeMirror.defineMode("css", function(config, parserConfig) {
return state.context.type;
};
states.atComponentBlock = function(type, stream, state) {
states.atComponentBlock = function (type, stream, state) {
if (type == "}")
return popAndPass(type, stream, state);
if (type == "{")
......@@ -313,13 +321,13 @@ CodeMirror.defineMode("css", function(config, parserConfig) {
return state.context.type;
};
states.atBlock_parens = function(type, stream, state) {
states.atBlock_parens = function (type, stream, state) {
if (type == ")") return popContext(state);
if (type == "{" || type == "}") return popAndPass(type, stream, state, 2);
return states.atBlock(type, stream, state);
};
states.restricted_atBlock_before = function(type, stream, state) {
states.restricted_atBlock_before = function (type, stream, state) {
if (type == "{")
return pushContext(state, stream, "restricted_atBlock");
if (type == "word" && state.stateArg == "@counter-style") {
......@@ -329,7 +337,7 @@ CodeMirror.defineMode("css", function(config, parserConfig) {
return pass(type, stream, state);
};
states.restricted_atBlock = function(type, stream, state) {
states.restricted_atBlock = function (type, stream, state) {
if (type == "}") {
state.stateArg = null;
return popContext(state);
......@@ -345,13 +353,16 @@ CodeMirror.defineMode("css", function(config, parserConfig) {
return "restricted_atBlock";
};
states.keyframes = function(type, stream, state) {
if (type == "word") { override = "variable"; return "keyframes"; }
states.keyframes = function (type, stream, state) {
if (type == "word") {
override = "variable";
return "keyframes";
}
if (type == "{") return pushContext(state, stream, "top");
return pass(type, stream, state);
};
states.at = function(type, stream, state) {
states.at = function (type, stream, state) {
if (type == ";") return popContext(state);
if (type == "{" || type == "}") return popAndPass(type, stream, state);
if (type == "word") override = "tag";
......@@ -359,7 +370,7 @@ CodeMirror.defineMode("css", function(config, parserConfig) {
return "at";
};
states.interpolation = function(type, stream, state) {
states.interpolation = function (type, stream, state) {
if (type == "}") return popContext(state);
if (type == "{" || type == ";") return popAndPass(type, stream, state);
if (type == "word") override = "variable";
......@@ -368,14 +379,16 @@ CodeMirror.defineMode("css", function(config, parserConfig) {
};
return {
startState: function(base) {
return {tokenize: null,
startState: function (base) {
return {
tokenize: null,
state: inline ? "block" : "top",
stateArg: null,
context: new Context(inline ? "block" : "top", base || 0, null)};
context: new Context(inline ? "block" : "top", base || 0, null)
};
},
token: function(stream, state) {
token: function (stream, state) {
if (!state.tokenize && stream.eatSpace()) return null;
var style = (state.tokenize || tokenBase)(stream, state);
if (style && typeof style == "object") {
......@@ -388,7 +401,7 @@ CodeMirror.defineMode("css", function(config, parserConfig) {
return override;
},
indent: function(state, textAfter) {
indent: function (state, textAfter) {
var cx = state.context, ch = textAfter && textAfter.charAt(0);
var indent = cx.indent;
if (cx.type == "prop" && (ch == "}" || ch == ")")) cx = cx.prev;
......@@ -414,7 +427,7 @@ CodeMirror.defineMode("css", function(config, parserConfig) {
lineComment: lineComment,
fold: "brace"
};
});
});
function keySet(array) {
var keys = {};
......@@ -506,7 +519,7 @@ CodeMirror.defineMode("css", function(config, parserConfig) {
"margin-bottom", "margin-left", "margin-right", "margin-top", "marks",
"marquee-direction", "marquee-loop", "marquee-play-count", "marquee-speed",
"marquee-style", "mask-clip", "mask-composite", "mask-image", "mask-mode",
"mask-origin", "mask-position", "mask-repeat", "mask-size","mask-type",
"mask-origin", "mask-position", "mask-repeat", "mask-size", "mask-type",
"max-block-size", "max-height", "max-inline-size",
"max-width", "min-block-size", "min-height", "min-inline-size", "min-width",
"mix-blend-mode", "move-to", "nav-down", "nav-index", "nav-left", "nav-right",
......@@ -748,7 +761,7 @@ CodeMirror.defineMode("css", function(config, parserConfig) {
colorKeywords: colorKeywords,
valueKeywords: valueKeywords,
tokenHooks: {
"/": function(stream, state) {
"/": function (stream, state) {
if (!stream.eat("*")) return false;
state.tokenize = tokenCComment;
return tokenCComment(stream, state);
......@@ -769,7 +782,7 @@ CodeMirror.defineMode("css", function(config, parserConfig) {
allowNested: true,
lineComment: "//",
tokenHooks: {
"/": function(stream, state) {
"/": function (stream, state) {
if (stream.eat("/")) {
stream.skipToEnd();
return ["comment", "comment"];
......@@ -780,18 +793,18 @@ CodeMirror.defineMode("css", function(config, parserConfig) {
return ["operator", "operator"];
}
},
":": function(stream) {
":": function (stream) {
if (stream.match(/\s*\{/, false))
return [null, null]
return false;
},
"$": function(stream) {
"$": function (stream) {
stream.match(/^[\w-]+/);
if (stream.match(/^\s*:/, false))
return ["variable-2", "variable-definition"];
return ["variable-2", "variable"];
},
"#": function(stream) {
"#": function (stream) {
if (!stream.eat("{")) return false;
return [null, "interpolation"];
}
......@@ -812,7 +825,7 @@ CodeMirror.defineMode("css", function(config, parserConfig) {
allowNested: true,
lineComment: "//",
tokenHooks: {
"/": function(stream, state) {
"/": function (stream, state) {
if (stream.eat("/")) {
stream.skipToEnd();
return ["comment", "comment"];
......@@ -823,7 +836,7 @@ CodeMirror.defineMode("css", function(config, parserConfig) {
return ["operator", "operator"];
}
},
"@": function(stream) {
"@": function (stream) {
if (stream.eat("{")) return [null, "interpolation"];
if (stream.match(/^(charset|document|font-face|import|(-(moz|ms|o|webkit)-)?keyframes|media|namespace|page|supports)\b/i, false)) return false;
stream.eatWhile(/[\w\\\-]/);
......@@ -831,7 +844,7 @@ CodeMirror.defineMode("css", function(config, parserConfig) {
return ["variable-2", "variable-definition"];
return ["variable-2", "variable"];
},
"&": function() {
"&": function () {
return ["atom", "atom"];
}
},
......@@ -851,7 +864,7 @@ CodeMirror.defineMode("css", function(config, parserConfig) {
valueKeywords: valueKeywords,
supportsAtComponent: true,
tokenHooks: {
"/": function(stream, state) {
"/": function (stream, state) {
if (!stream.eat("*")) return false;
state.tokenize = tokenCComment;
return tokenCComment(stream, state);
......
......@@ -2,18 +2,20 @@
<title>CodeMirror: Closure Stylesheets (GSS) mode</title>
<meta charset="utf-8"/>
<link rel=stylesheet href="../../doc/docs.css">
<link href="../../doc/docs.css" rel=stylesheet>
<link rel="stylesheet" href="../../lib/codemirror.css">
<link rel="stylesheet" href="../../addon/hint/show-hint.css">
<link href="../../lib/codemirror.css" rel="stylesheet">
<link href="../../addon/hint/show-hint.css" rel="stylesheet">
<script src="../../lib/codemirror.js"></script>
<script src="css.js"></script>
<script src="../../addon/edit/matchbrackets.js"></script>
<script src="../../addon/hint/show-hint.js"></script>
<script src="../../addon/hint/css-hint.js"></script>
<style>.CodeMirror {background: #f8f8f8;}</style>
<style>.CodeMirror {
background: #f8f8f8;
}</style>
<div id=nav>
<a href="https://codemirror.net"><h1>CodeMirror</h1><img id=logo src="../../doc/logo.png" alt=""></a>
<a href="https://codemirror.net"><h1>CodeMirror</h1><img alt="" id=logo src="../../doc/logo.png"></a>
<ul>
<li><a href="../../index.html">Home</a>
......@@ -27,8 +29,8 @@
</div>
<article>
<h2>Closure Stylesheets (GSS) mode</h2>
<form><textarea id="code" name="code">
<h2>Closure Stylesheets (GSS) mode</h2>
<form><textarea id="code" name="code">
/* Some example Closure Stylesheets */
@provide 'some.styles';
......@@ -37,19 +39,19 @@
@component {
@def FONT_FAMILY "Times New Roman", Georgia, Serif;
@def FONT_SIZE_NORMAL 15px;
@def FONT_NORMAL normal FONT_SIZE_NORMAL FONT_FAMILY;
@def FONT_FAMILY "Times New Roman", Georgia, Serif;
@def FONT_SIZE_NORMAL 15px;
@def FONT_NORMAL normal FONT_SIZE_NORMAL FONT_FAMILY;
@def BG_COLOR rgb(235, 239, 249);
@def BG_COLOR rgb(235, 239, 249);
@def DIALOG_BORDER_COLOR rgb(107, 144, 218);
@def DIALOG_BG_COLOR BG_COLOR;
@def DIALOG_BORDER_COLOR rgb(107, 144, 218);
@def DIALOG_BG_COLOR BG_COLOR;
@def LEFT_HAND_NAV_WIDTH 180px;
@def LEFT_HAND_NAV_PADDING 3px;
@def LEFT_HAND_NAV_WIDTH 180px;
@def LEFT_HAND_NAV_PADDING 3px;
@defmixin size(WIDTH, HEIGHT) {
@defmixin size(WIDTH, HEIGHT) {
width: WIDTH;
height: HEIGHT;
}
......@@ -99,6 +101,7 @@ body {
<p>A mode for <a href="https://github.com/google/closure-stylesheets">Closure Stylesheets</a> (GSS).</p>
<p><strong>MIME type defined:</strong> <code>text/x-gss</code>.</p>
<p><strong>Parsing/Highlighting Tests:</strong> <a href="../../test/index.html#gss_*">normal</a>, <a href="../../test/index.html#verbose,gss_*">verbose</a>.</p>
<p><strong>Parsing/Highlighting Tests:</strong> <a href="../../test/index.html#gss_*">normal</a>, <a
href="../../test/index.html#verbose,gss_*">verbose</a>.</p>
</article>
</article>
// CodeMirror, copyright (c) by Marijn Haverbeke and others
// Distributed under an MIT license: https://codemirror.net/LICENSE
(function() {
(function () {
"use strict";
var mode = CodeMirror.getMode({indentUnit: 2}, "text/x-gss");
function MT(name) { test.mode(name, mode, Array.prototype.slice.call(arguments, 1), "gss"); }
function MT(name) {
test.mode(name, mode, Array.prototype.slice.call(arguments, 1), "gss");
}
MT("atComponent",
"[def @component] {",
......
......@@ -2,17 +2,19 @@
<title>CodeMirror: CSS mode</title>
<meta charset="utf-8"/>
<link rel=stylesheet href="../../doc/docs.css">
<link href="../../doc/docs.css" rel=stylesheet>
<link rel="stylesheet" href="../../lib/codemirror.css">
<link rel="stylesheet" href="../../addon/hint/show-hint.css">
<link href="../../lib/codemirror.css" rel="stylesheet">
<link href="../../addon/hint/show-hint.css" rel="stylesheet">
<script src="../../lib/codemirror.js"></script>
<script src="css.js"></script>
<script src="../../addon/hint/show-hint.js"></script>
<script src="../../addon/hint/css-hint.js"></script>
<style>.CodeMirror {background: #f8f8f8;}</style>
<style>.CodeMirror {
background: #f8f8f8;
}</style>
<div id=nav>
<a href="https://codemirror.net"><h1>CodeMirror</h1><img id=logo src="../../doc/logo.png" alt=""></a>
<a href="https://codemirror.net"><h1>CodeMirror</h1><img alt="" id=logo src="../../doc/logo.png"></a>
<ul>
<li><a href="../../index.html">Home</a>
......@@ -26,8 +28,8 @@
</div>
<article>
<h2>CSS mode</h2>
<form><textarea id="code" name="code">
<h2>CSS mode</h2>
<form><textarea id="code" name="code">
/* Some example CSS */
@import url("something.css");
......@@ -71,11 +73,15 @@ code {
<p>CSS mode supports this option:</p>
<d1>
<dt><code><strong>highlightNonStandardPropertyKeywords</strong>: boolean</code></dt>
<dd>Whether to highlight non-standard CSS property keywords such as <code>margin-inline</code> or <code>zoom</code> (default: <code>true</code>).</dd>
<dd>Whether to highlight non-standard CSS property keywords such as <code>margin-inline</code> or <code>zoom</code>
(default: <code>true</code>).
</dd>
</d1>
<p><strong>MIME types defined:</strong> <code>text/css</code>, <code>text/x-scss</code> (<a href="scss.html">demo</a>), <code>text/x-less</code> (<a href="less.html">demo</a>).</p>
<p><strong>MIME types defined:</strong> <code>text/css</code>, <code>text/x-scss</code> (<a href="scss.html">demo</a>),
<code>text/x-less</code> (<a href="less.html">demo</a>).</p>
<p><strong>Parsing/Highlighting Tests:</strong> <a href="../../test/index.html#css_*">normal</a>, <a href="../../test/index.html#verbose,css_*">verbose</a>.</p>
<p><strong>Parsing/Highlighting Tests:</strong> <a href="../../test/index.html#css_*">normal</a>, <a
href="../../test/index.html#verbose,css_*">verbose</a>.</p>
</article>
</article>
......@@ -2,15 +2,18 @@
<title>CodeMirror: LESS mode</title>
<meta charset="utf-8"/>
<link rel=stylesheet href="../../doc/docs.css">
<link href="../../doc/docs.css" rel=stylesheet>
<link rel="stylesheet" href="../../lib/codemirror.css">
<link href="../../lib/codemirror.css" rel="stylesheet">
<script src="../../lib/codemirror.js"></script>
<script src="../../addon/edit/matchbrackets.js"></script>
<script src="css.js"></script>
<style>.CodeMirror {border: 1px solid #ddd; line-height: 1.2;}</style>
<style>.CodeMirror {
border: 1px solid #ddd;
line-height: 1.2;
}</style>
<div id=nav>
<a href="https://codemirror.net"><h1>CodeMirror</h1><img id=logo src="../../doc/logo.png" alt=""></a>
<a href="https://codemirror.net"><h1>CodeMirror</h1><img alt="" id=logo src="../../doc/logo.png"></a>
<ul>
<li><a href="../../index.html">Home</a>
......@@ -24,8 +27,8 @@
</div>
<article>
<h2>LESS mode</h2>
<form><textarea id="code" name="code">@media screen and (device-aspect-ratio: 16/9) { … }
<h2>LESS mode</h2>
<form><textarea id="code" name="code">@media screen and (device-aspect-ratio: 16/9) { … }
@media screen and (device-aspect-ratio: 1280/720) { … }
@media screen and (device-aspect-ratio: 2560/1440) { … }
......@@ -148,5 +151,6 @@ fieldset span button, fieldset span input[type="file"] {
<p>The LESS mode is a sub-mode of the <a href="index.html">CSS mode</a> (defined in <code>css.js</code>).</p>
<p><strong>Parsing/Highlighting Tests:</strong> <a href="../../test/index.html#less_*">normal</a>, <a href="../../test/index.html#verbose,less_*">verbose</a>.</p>
</article>
<p><strong>Parsing/Highlighting Tests:</strong> <a href="../../test/index.html#less_*">normal</a>, <a
href="../../test/index.html#verbose,less_*">verbose</a>.</p>
</article>
// CodeMirror, copyright (c) by Marijn Haverbeke and others
// Distributed under an MIT license: https://codemirror.net/LICENSE
(function() {
(function () {
"use strict";
var mode = CodeMirror.getMode({indentUnit: 2}, "text/x-less");
function MT(name) { test.mode(name, mode, Array.prototype.slice.call(arguments, 1), "less"); }
function MT(name) {
test.mode(name, mode, Array.prototype.slice.call(arguments, 1), "less");
}
MT("variable",
"[variable-2 @base]: [atom #f04615];",
......
......@@ -2,15 +2,17 @@
<title>CodeMirror: SCSS mode</title>
<meta charset="utf-8"/>
<link rel=stylesheet href="../../doc/docs.css">
<link href="../../doc/docs.css" rel=stylesheet>
<link rel="stylesheet" href="../../lib/codemirror.css">
<link href="../../lib/codemirror.css" rel="stylesheet">
<script src="../../lib/codemirror.js"></script>
<script src="../../addon/edit/matchbrackets.js"></script>
<script src="css.js"></script>
<style>.CodeMirror {background: #f8f8f8;}</style>
<style>.CodeMirror {
background: #f8f8f8;
}</style>
<div id=nav>
<a href="https://codemirror.net"><h1>CodeMirror</h1><img id=logo src="../../doc/logo.png" alt=""></a>
<a href="https://codemirror.net"><h1>CodeMirror</h1><img alt="" id=logo src="../../doc/logo.png"></a>
<ul>
<li><a href="../../index.html">Home</a>
......@@ -24,8 +26,8 @@
</div>
<article>
<h2>SCSS mode</h2>
<form><textarea id="code" name="code">
<h2>SCSS mode</h2>
<form><textarea id="code" name="code">
/* Some example SCSS */
@import "compass/css3";
......@@ -38,7 +40,7 @@ $margin: 16px;
#nested {
background-color: black;
}
border-color: $blue;
border - color: $blue;
color:
darken($blue, 9%);
}
......@@ -55,9 +57,9 @@ $margin: 16px;
font-weight: bold;
}
td, th {padding: 2px}
}
}
table.hl {
table.hl {
margin: 2em 0;
td.ln {
text-align: right;
......@@ -153,6 +155,7 @@ code {
<p>The SCSS mode is a sub-mode of the <a href="index.html">CSS mode</a> (defined in <code>css.js</code>).</p>
<p><strong>Parsing/Highlighting Tests:</strong> <a href="../../test/index.html#scss_*">normal</a>, <a href="../../test/index.html#verbose,scss_*">verbose</a>.</p>
<p><strong>Parsing/Highlighting Tests:</strong> <a href="../../test/index.html#scss_*">normal</a>, <a
href="../../test/index.html#verbose,scss_*">verbose</a>.</p>
</article>
</article>
// CodeMirror, copyright (c) by Marijn Haverbeke and others
// Distributed under an MIT license: https://codemirror.net/LICENSE
(function() {
(function () {
var mode = CodeMirror.getMode({indentUnit: 2}, "text/x-scss");
function MT(name) { test.mode(name, mode, Array.prototype.slice.call(arguments, 1), "scss"); }
function MT(name) {
test.mode(name, mode, Array.prototype.slice.call(arguments, 1), "scss");
}
MT('url_with_quotation',
"[tag foo] { [property background]:[variable&callee url]([string test.jpg]) }");
......
// CodeMirror, copyright (c) by Marijn Haverbeke and others
// Distributed under an MIT license: https://codemirror.net/LICENSE
(function() {
(function () {
var mode = CodeMirror.getMode({indentUnit: 2}, "css");
function MT(name) { test.mode(name, mode, Array.prototype.slice.call(arguments, 1)); }
function MT(name) {
test.mode(name, mode, Array.prototype.slice.call(arguments, 1));
}
// Error, because "foobarhello" is neither a known type or property, but
// property was expected (after "and"), and it should be in parentheses.
......
// CodeMirror, copyright (c) by Marijn Haverbeke and others
// Distributed under an MIT license: https://codemirror.net/LICENSE
(function(mod) {
(function (mod) {
if (typeof exports == "object" && typeof module == "object") // CommonJS
mod(require("../../lib/codemirror"));
else if (typeof define == "function" && define.amd) // AMD
define(["../../lib/codemirror"], mod);
else // Plain browser env
mod(CodeMirror);
})(function(CodeMirror) {
"use strict";
})(function (CodeMirror) {
"use strict";
CodeMirror.defineMode("d", function(config, parserConfig) {
CodeMirror.defineMode("d", function (config, parserConfig) {
var indentUnit = config.indentUnit,
statementIndentUnit = parserConfig.statementIndentUnit || indentUnit,
keywords = parserConfig.keywords || {},
......@@ -75,10 +75,13 @@ CodeMirror.defineMode("d", function(config, parserConfig) {
}
function tokenString(quote) {
return function(stream, state) {
return function (stream, state) {
var escaped = false, next, end = false;
while ((next = stream.next()) != null) {
if (next == quote && !escaped) {end = true; break;}
if (next == quote && !escaped) {
end = true;
break;
}
escaped = !escaped && next == "\\";
}
if (end || !(escaped || multiLineStrings))
......@@ -118,12 +121,14 @@ CodeMirror.defineMode("d", function(config, parserConfig) {
this.align = align;
this.prev = prev;
}
function pushContext(state, col, type) {
var indent = state.indented;
if (state.context && state.context.type == "statement")
indent = state.context.indented;
return state.context = new Context(indent, col, type, null, state.context);
}
function popContext(state) {
var t = state.context.type;
if (t == ")" || t == "]" || t == "}")
......@@ -134,7 +139,7 @@ CodeMirror.defineMode("d", function(config, parserConfig) {
// Interface
return {
startState: function(basecolumn) {
startState: function (basecolumn) {
return {
tokenize: null,
context: new Context((basecolumn || 0) - indentUnit, 0, "top", false),
......@@ -143,7 +148,7 @@ CodeMirror.defineMode("d", function(config, parserConfig) {
};
},
token: function(stream, state) {
token: function (stream, state) {
var ctx = state.context;
if (stream.sol()) {
if (ctx.align == null) ctx.align = false;
......@@ -164,15 +169,14 @@ CodeMirror.defineMode("d", function(config, parserConfig) {
while (ctx.type == "statement") ctx = popContext(state);
if (ctx.type == "}") ctx = popContext(state);
while (ctx.type == "statement") ctx = popContext(state);
}
else if (curPunc == ctx.type) popContext(state);
} else if (curPunc == ctx.type) popContext(state);
else if (((ctx.type == "}" || ctx.type == "top") && curPunc != ';') || (ctx.type == "statement" && curPunc == "newstatement"))
pushContext(state, stream.column(), "statement");
state.startOfLine = false;
return style;
},
indent: function(state, textAfter) {
indent: function (state, textAfter) {
if (state.tokenize != tokenBase && state.tokenize != null) return CodeMirror.Pass;
var ctx = state.context, firstChar = textAfter && textAfter.charAt(0);
if (ctx.type == "statement" && firstChar == "}") ctx = ctx.prev;
......@@ -189,7 +193,7 @@ CodeMirror.defineMode("d", function(config, parserConfig) {
lineComment: "//",
fold: "brace"
};
});
});
function words(str) {
var obj = {}, words = str.split(" ");
......@@ -213,7 +217,7 @@ CodeMirror.defineMode("d", function(config, parserConfig) {
"ucent uint ulong ushort wchar wstring void size_t sizediff_t"),
atoms: words("exit failure success true false null"),
hooks: {
"@": function(stream, _state) {
"@": function (stream, _state) {
stream.eatWhile(/[\w\$_]/);
return "meta";
}
......
......@@ -2,15 +2,17 @@
<title>CodeMirror: D mode</title>
<meta charset="utf-8"/>
<link rel=stylesheet href="../../doc/docs.css">
<link href="../../doc/docs.css" rel=stylesheet>
<link rel="stylesheet" href="../../lib/codemirror.css">
<link href="../../lib/codemirror.css" rel="stylesheet">
<script src="../../lib/codemirror.js"></script>
<script src="../../addon/edit/matchbrackets.js"></script>
<script src="d.js"></script>
<style>.CodeMirror {border: 2px inset #dee;}</style>
<style>.CodeMirror {
border: 2px inset #dee;
}</style>
<div id=nav>
<a href="https://codemirror.net"><h1>CodeMirror</h1><img id=logo src="../../doc/logo.png" alt=""></a>
<a href="https://codemirror.net"><h1>CodeMirror</h1><img alt="" id=logo src="../../doc/logo.png"></a>
<ul>
<li><a href="../../index.html">Home</a>
......@@ -24,8 +26,8 @@
</div>
<article>
<h2>D mode</h2>
<form><textarea id="code" name="code">
<h2>D mode</h2>
<form><textarea id="code" name="code">
/* D demo code // copied from phobos/sd/metastrings.d */
// Written in the D programming language.
......@@ -67,7 +69,7 @@ import std.metastrings;
import std.stdio;
void main()
{
{
string s = Format!("Arg %s = %s", "foo", 27);
writefln(s); // "Arg foo = 27"
}
......@@ -75,7 +77,7 @@ void main()
*/
template Format(A...)
{
{
static if (A.length == 0)
enum Format = "";
else static if (is(typeof(A[0]) : const(char)[]))
......@@ -85,7 +87,7 @@ template Format(A...)
}
template FormatString(const(char)[] F, A...)
{
{
static if (F.length == 0)
enum FormatString = Format!(A);
else static if (F.length == 1)
......@@ -103,7 +105,7 @@ template FormatString(const(char)[] F, A...)
}
unittest
{
{
auto s = Format!("hel%slo", "world", -138, 'c', true);
assert(s == "helworldlo-138ctrue", "[" ~ s ~ "]");
}
......@@ -113,7 +115,7 @@ unittest
*/
template toStringNow(ulong v)
{
{
static if (v < 10)
enum toStringNow = "" ~ cast(char)(v + '0');
else
......@@ -121,13 +123,13 @@ template toStringNow(ulong v)
}
unittest
{
{
static assert(toStringNow!(1uL << 62) == "4611686018427387904");
}
/// ditto
template toStringNow(long v)
{
{
static if (v < 0)
enum toStringNow = "-" ~ toStringNow!(cast(ulong) -v);
else
......@@ -135,38 +137,38 @@ template toStringNow(long v)
}
unittest
{
{
static assert(toStringNow!(0x100000000) == "4294967296");
static assert(toStringNow!(-138L) == "-138");
}
/// ditto
template toStringNow(uint U)
{
{
enum toStringNow = toStringNow!(cast(ulong)U);
}
/// ditto
template toStringNow(int I)
{
{
enum toStringNow = toStringNow!(cast(long)I);
}
/// ditto
template toStringNow(bool B)
{
{
enum toStringNow = B ? "true" : "false";
}
/// ditto
template toStringNow(string S)
{
{
enum toStringNow = S;
}
/// ditto
template toStringNow(char C)
{
{
enum toStringNow = "" ~ C;
}
......@@ -182,7 +184,7 @@ template toStringNow(char C)
*/
template parseUinteger(const(char)[] s)
{
{
static if (s.length == 0)
{
enum value = "";
......@@ -214,7 +216,7 @@ Otherwise:
*/
template parseInteger(const(char)[] s)
{
{
static if (s.length == 0)
{
enum value = "";
......@@ -239,7 +241,7 @@ template parseInteger(const(char)[] s)
}
unittest
{
{
assert(parseUinteger!("1234abc").value == "1234");
assert(parseUinteger!("1234abc").rest == "abc");
assert(parseInteger!("-1234abc").value == "-1234");
......@@ -270,4 +272,4 @@ deprecated alias parseUinteger ParseInteger;
<p><strong>MIME types defined:</strong> <code>text/x-d</code>
.</p>
</article>
</article>
// CodeMirror, copyright (c) by Marijn Haverbeke and others
// Distributed under an MIT license: https://codemirror.net/LICENSE
(function() {
(function () {
var mode = CodeMirror.getMode({indentUnit: 2}, "d");
function MT(name) { test.mode(name, mode, Array.prototype.slice.call(arguments, 1)); }
function MT(name) {
test.mode(name, mode, Array.prototype.slice.call(arguments, 1));
}
MT("nested_comments",
"[comment /+]","[comment comment]","[comment +/]","[variable void] [variable main](){}");
"[comment /+]", "[comment comment]", "[comment +/]", "[variable void] [variable main](){}");
})();
......@@ -8,24 +8,28 @@
GitHub: @peterkroon
*/
(function(mod) {
(function (mod) {
if (typeof exports == "object" && typeof module == "object") // CommonJS
mod(require("../../lib/codemirror"));
else if (typeof define == "function" && define.amd) // AMD
define(["../../lib/codemirror"], mod);
else // Plain browser env
mod(CodeMirror);
})(function(CodeMirror) {
"use strict";
})(function (CodeMirror) {
"use strict";
CodeMirror.defineMode("dtd", function(config) {
CodeMirror.defineMode("dtd", function (config) {
var indentUnit = config.indentUnit, type;
function ret(style, tp) {type = tp; return style;}
function ret(style, tp) {
type = tp;
return style;
}
function tokenBase(stream, state) {
var ch = stream.next();
if (ch == "<" && stream.eat("!") ) {
if (ch == "<" && stream.eat("!")) {
if (stream.eatWhile(/[\-]/)) {
state.tokenize = tokenSGMLComment;
return tokenSGMLComment(stream, state);
......@@ -42,9 +46,9 @@ CodeMirror.defineMode("dtd", function(config) {
return state.tokenize(stream, state);
} else if (stream.eatWhile(/[a-zA-Z\?\+\d]/)) {
var sc = stream.current();
if( sc.substr(sc.length-1,sc.length).match(/\?|\+/) !== null )stream.backUp(1);
if (sc.substr(sc.length - 1, sc.length).match(/\?|\+/) !== null) stream.backUp(1);
return ret("tag", "tag");
} else if (ch == "%" || ch == "*" ) return ret("number", "number");
} else if (ch == "%" || ch == "*") return ret("number", "number");
else {
stream.eatWhile(/[\w\\\-_%.{,]/);
return ret(null, null);
......@@ -64,7 +68,7 @@ CodeMirror.defineMode("dtd", function(config) {
}
function tokenString(quote) {
return function(stream, state) {
return function (stream, state) {
var escaped = false, ch;
while ((ch = stream.next()) != null) {
if (ch == quote && !escaped) {
......@@ -78,7 +82,7 @@ CodeMirror.defineMode("dtd", function(config) {
}
function inBlock(style, terminator) {
return function(stream, state) {
return function (stream, state) {
while (!stream.eol()) {
if (stream.match(terminator)) {
state.tokenize = tokenBase;
......@@ -91,43 +95,45 @@ CodeMirror.defineMode("dtd", function(config) {
}
return {
startState: function(base) {
return {tokenize: tokenBase,
startState: function (base) {
return {
tokenize: tokenBase,
baseIndent: base || 0,
stack: []};
stack: []
};
},
token: function(stream, state) {
token: function (stream, state) {
if (stream.eatSpace()) return null;
var style = state.tokenize(stream, state);
var context = state.stack[state.stack.length-1];
var context = state.stack[state.stack.length - 1];
if (stream.current() == "[" || type === "doindent" || type == "[") state.stack.push("rule");
else if (type === "endtag") state.stack[state.stack.length-1] = "endtag";
else if (type === "endtag") state.stack[state.stack.length - 1] = "endtag";
else if (stream.current() == "]" || type == "]" || (type == ">" && context == "rule")) state.stack.pop();
else if (type == "[") state.stack.push("[");
return style;
},
indent: function(state, textAfter) {
indent: function (state, textAfter) {
var n = state.stack.length;
if( textAfter.match(/\]\s+|\]/) )n=n-1;
else if(textAfter.substr(textAfter.length-1, textAfter.length) === ">"){
if(textAfter.substr(0,1) === "<") {}
else if( type == "doindent" && textAfter.length > 1 ) {}
else if( type == "doindent")n--;
else if( type == ">" && textAfter.length > 1) {}
else if( type == "tag" && textAfter !== ">") {}
else if( type == "tag" && state.stack[state.stack.length-1] == "rule")n--;
else if( type == "tag")n++;
else if( textAfter === ">" && state.stack[state.stack.length-1] == "rule" && type === ">")n--;
else if( textAfter === ">" && state.stack[state.stack.length-1] == "rule") {}
else if( textAfter.substr(0,1) !== "<" && textAfter.substr(0,1) === ">" )n=n-1;
else if( textAfter === ">") {}
else n=n-1;
if (textAfter.match(/\]\s+|\]/)) n = n - 1;
else if (textAfter.substr(textAfter.length - 1, textAfter.length) === ">") {
if (textAfter.substr(0, 1) === "<") {
} else if (type == "doindent" && textAfter.length > 1) {
} else if (type == "doindent") n--;
else if (type == ">" && textAfter.length > 1) {
} else if (type == "tag" && textAfter !== ">") {
} else if (type == "tag" && state.stack[state.stack.length - 1] == "rule") n--;
else if (type == "tag") n++;
else if (textAfter === ">" && state.stack[state.stack.length - 1] == "rule" && type === ">") n--;
else if (textAfter === ">" && state.stack[state.stack.length - 1] == "rule") {
} else if (textAfter.substr(0, 1) !== "<" && textAfter.substr(0, 1) === ">") n = n - 1;
else if (textAfter === ">") {
} else n = n - 1;
//over rule them all
if(type == null || type == "]")n--;
if (type == null || type == "]") n--;
}
return state.baseIndent + n * indentUnit;
......@@ -135,8 +141,8 @@ CodeMirror.defineMode("dtd", function(config) {
electricChars: "]>"
};
});
});
CodeMirror.defineMIME("application/xml-dtd", "dtd");
CodeMirror.defineMIME("application/xml-dtd", "dtd");
});
......@@ -7,7 +7,10 @@
<link rel="stylesheet" href="../../lib/codemirror.css">
<script src="../../lib/codemirror.js"></script>
<script src="dtd.js"></script>
<style>.CodeMirror {border-top: 1px solid black; border-bottom: 1px solid black;}</style>
<style>.CodeMirror {
border-top: 1px solid black;
border-bottom: 1px solid black;
}</style>
<div id=nav>
<a href="https://codemirror.net"><h1>CodeMirror</h1><img id=logo src="../../doc/logo.png" alt=""></a>
......@@ -23,19 +26,19 @@
</div>
<article>
<h2>DTD mode</h2>
<form><textarea id="code" name="code"><?xml version="1.0" encoding="UTF-8"?>
<h2>DTD mode</h2>
<form><textarea id="code" name="code"><?xml version="1.0" encoding="UTF-8"?>
<!ATTLIST title
<!ATTLIST title
xmlns CDATA #FIXED "http://docbook.org/ns/docbook"
role CDATA #IMPLIED
%db.common.attributes;
%db.common.linking.attributes;
>
<!--
<!--
Try: http://docbook.org/xml/5.0/dtd/docbook.dtd
-->
-->
<!DOCTYPE xsl:stylesheet
[
......@@ -86,4 +89,4 @@
</script>
<p><strong>MIME types defined:</strong> <code>application/xml-dtd</code>.</p>
</article>
</article>
// CodeMirror, copyright (c) by Marijn Haverbeke and others
// Distributed under an MIT license: https://codemirror.net/LICENSE
(function(mod) {
(function (mod) {
if (typeof exports == "object" && typeof module == "object") // CommonJS
mod(require("../../lib/codemirror"));
else if (typeof define == "function" && define.amd) // AMD
define(["../../lib/codemirror"], mod);
else // Plain browser env
mod(CodeMirror);
})(function(CodeMirror) {
"use strict";
})(function (CodeMirror) {
"use strict";
CodeMirror.defineMode("go", function(config) {
CodeMirror.defineMode("go", function (config) {
var indentUnit = config.indentUnit;
var keywords = {
"break":true, "case":true, "chan":true, "const":true, "continue":true,
"default":true, "defer":true, "else":true, "fallthrough":true, "for":true,
"func":true, "go":true, "goto":true, "if":true, "import":true,
"interface":true, "map":true, "package":true, "range":true, "return":true,
"select":true, "struct":true, "switch":true, "type":true, "var":true,
"bool":true, "byte":true, "complex64":true, "complex128":true,
"float32":true, "float64":true, "int8":true, "int16":true, "int32":true,
"int64":true, "string":true, "uint8":true, "uint16":true, "uint32":true,
"uint64":true, "int":true, "uint":true, "uintptr":true, "error": true,
"rune":true
"break": true, "case": true, "chan": true, "const": true, "continue": true,
"default": true, "defer": true, "else": true, "fallthrough": true, "for": true,
"func": true, "go": true, "goto": true, "if": true, "import": true,
"interface": true, "map": true, "package": true, "range": true, "return": true,
"select": true, "struct": true, "switch": true, "type": true, "var": true,
"bool": true, "byte": true, "complex64": true, "complex128": true,
"float32": true, "float64": true, "int8": true, "int16": true, "int32": true,
"int64": true, "string": true, "uint8": true, "uint16": true, "uint32": true,
"uint64": true, "int": true, "uint": true, "uintptr": true, "error": true,
"rune": true
};
var atoms = {
"true":true, "false":true, "iota":true, "nil":true, "append":true,
"cap":true, "close":true, "complex":true, "copy":true, "delete":true, "imag":true,
"len":true, "make":true, "new":true, "panic":true, "print":true,
"println":true, "real":true, "recover":true
"true": true, "false": true, "iota": true, "nil": true, "append": true,
"cap": true, "close": true, "complex": true, "copy": true, "delete": true, "imag": true,
"len": true, "make": true, "new": true, "panic": true, "print": true,
"println": true, "real": true, "recover": true
};
var isOperatorChar = /[+\-*&^%:=<>!|\/]/;
......@@ -83,10 +83,13 @@ CodeMirror.defineMode("go", function(config) {
}
function tokenString(quote) {
return function(stream, state) {
return function (stream, state) {
var escaped = false, next, end = false;
while ((next = stream.next()) != null) {
if (next == quote && !escaped) {end = true; break;}
if (next == quote && !escaped) {
end = true;
break;
}
escaped = !escaped && quote != "`" && next == "\\";
}
if (end || !(escaped || quote == "`"))
......@@ -114,9 +117,11 @@ CodeMirror.defineMode("go", function(config) {
this.align = align;
this.prev = prev;
}
function pushContext(state, col, type) {
return state.context = new Context(state.indented, col, type, null, state.context);
}
function popContext(state) {
if (!state.context.prev) return;
var t = state.context.type;
......@@ -128,7 +133,7 @@ CodeMirror.defineMode("go", function(config) {
// Interface
return {
startState: function(basecolumn) {
startState: function (basecolumn) {
return {
tokenize: null,
context: new Context((basecolumn || 0) - indentUnit, 0, "top", false),
......@@ -137,7 +142,7 @@ CodeMirror.defineMode("go", function(config) {
};
},
token: function(stream, state) {
token: function (stream, state) {
var ctx = state.context;
if (stream.sol()) {
if (ctx.align == null) ctx.align = false;
......@@ -161,7 +166,7 @@ CodeMirror.defineMode("go", function(config) {
return style;
},
indent: function(state, textAfter) {
indent: function (state, textAfter) {
if (state.tokenize != tokenBase && state.tokenize != null) return CodeMirror.Pass;
var ctx = state.context, firstChar = textAfter && textAfter.charAt(0);
if (ctx.type == "case" && /^(?:case|default)\b/.test(textAfter)) {
......@@ -180,8 +185,8 @@ CodeMirror.defineMode("go", function(config) {
blockCommentEnd: "*/",
lineComment: "//"
};
});
});
CodeMirror.defineMIME("text/x-go", "go");
CodeMirror.defineMIME("text/x-go", "go");
});
......@@ -2,16 +2,19 @@
<title>CodeMirror: Go mode</title>
<meta charset="utf-8"/>
<link rel=stylesheet href="../../doc/docs.css">
<link href="../../doc/docs.css" rel=stylesheet>
<link rel="stylesheet" href="../../lib/codemirror.css">
<link rel="stylesheet" href="../../theme/elegant.css">
<link href="../../lib/codemirror.css" rel="stylesheet">
<link href="../../theme/elegant.css" rel="stylesheet">
<script src="../../lib/codemirror.js"></script>
<script src="../../addon/edit/matchbrackets.js"></script>
<script src="go.js"></script>
<style>.CodeMirror {border:1px solid #999; background:#ffc}</style>
<style>.CodeMirror {
border: 1px solid #999;
background: #ffc
}</style>
<div id=nav>
<a href="https://codemirror.net"><h1>CodeMirror</h1><img id=logo src="../../doc/logo.png" alt=""></a>
<a href="https://codemirror.net"><h1>CodeMirror</h1><img alt="" id=logo src="../../doc/logo.png"></a>
<ul>
<li><a href="../../index.html">Home</a>
......@@ -25,8 +28,8 @@
</div>
<article>
<h2>Go mode</h2>
<form><textarea id="code" name="code">
<h2>Go mode</h2>
<form><textarea id="code" name="code">
// Prime Sieve in Go.
// Taken from the Go specification.
// Copyright © The Go Authors.
......@@ -82,4 +85,4 @@ func main() {
</script>
<p><strong>MIME type:</strong> <code>text/x-go</code></p>
</article>
</article>
......@@ -2,17 +2,19 @@
<title>CodeMirror: Oz mode</title>
<meta charset="utf-8"/>
<link rel=stylesheet href="../../doc/docs.css">
<link href="../../doc/docs.css" rel=stylesheet>
<link rel="stylesheet" href="../../lib/codemirror.css">
<link href="../../lib/codemirror.css" rel="stylesheet">
<script src="../../lib/codemirror.js"></script>
<script src="oz.js"></script>
<script src="../../addon/runmode/runmode.js"></script>
<style>
.CodeMirror {border: 1px solid #aaa;}
.CodeMirror {
border: 1px solid #aaa;
}
</style>
<div id=nav>
<a href="https://codemirror.net"><h1>CodeMirror</h1><img id=logo src="../../doc/logo.png" alt=""></a>
<a href="https://codemirror.net"><h1>CodeMirror</h1><img alt="" id=logo src="../../doc/logo.png"></a>
<ul>
<li><a href="../../index.html">Home</a>
<li><a href="../../doc/manual.html">Manual</a>
......@@ -25,8 +27,8 @@
</div>
<article>
<h2>Oz mode</h2>
<textarea id="code" name="code">
<h2>Oz mode</h2>
<textarea id="code" name="code">
declare
fun {Ints N Max}
if N == Max then nil
......@@ -47,13 +49,13 @@ local X Y in
{Browse Y}
end
</textarea>
<p>MIME type defined: <code>text/x-oz</code>.</p>
<p>MIME type defined: <code>text/x-oz</code>.</p>
<script>
var editor = CodeMirror.fromTextArea(document.getElementById("code"), {
<script>
var editor = CodeMirror.fromTextArea(document.getElementById("code"), {
lineNumbers: true,
mode: "text/x-oz",
readOnly: false
});
</script>
});
</script>
</article>
// CodeMirror, copyright (c) by Marijn Haverbeke and others
// Distributed under an MIT license: https://codemirror.net/LICENSE
(function(mod) {
(function (mod) {
if (typeof exports == "object" && typeof module == "object") // CommonJS
mod(require("../../lib/codemirror"));
else if (typeof define == "function" && define.amd) // AMD
define(["../../lib/codemirror"], mod);
else // Plain browser env
mod(CodeMirror);
})(function(CodeMirror) {
"use strict";
})(function (CodeMirror) {
"use strict";
CodeMirror.defineMode("oz", function (conf) {
CodeMirror.defineMode("oz", function (conf) {
function wordRegexp(words) {
return new RegExp("^((" + words.join(")|(") + "))\\b");
......@@ -40,7 +40,7 @@ CodeMirror.defineMode("oz", function (conf) {
}
// Brackets
if(stream.match(/[{}]/)) {
if (stream.match(/[{}]/)) {
return "bracket";
}
......@@ -55,7 +55,7 @@ CodeMirror.defineMode("oz", function (conf) {
}
// Atoms
if(stream.match(atoms)) {
if (stream.match(atoms)) {
return 'atom';
}
......@@ -68,11 +68,11 @@ CodeMirror.defineMode("oz", function (conf) {
state.doInCurrentLine = false;
// Special matching for signatures
if(matched[0] == "proc" || matched[0] == "fun")
if (matched[0] == "proc" || matched[0] == "fun")
state.tokenize = tokenFunProc;
else if(matched[0] == "class")
else if (matched[0] == "class")
state.tokenize = tokenClass;
else if(matched[0] == "meth")
else if (matched[0] == "meth")
state.tokenize = tokenMeth;
return 'keyword';
......@@ -101,9 +101,9 @@ CodeMirror.defineMode("oz", function (conf) {
// Numbers
if (/[~\d]/.test(ch)) {
if (ch == "~") {
if(! /^[0-9]/.test(stream.peek()))
if (!/^[0-9]/.test(stream.peek()))
return null;
else if (( stream.next() == "0" && stream.match(/^[xX][0-9a-fA-F]+/)) || stream.match(/^[0-9]*(\.[0-9]+)?([eE][~+]?[0-9]+)?/))
else if ((stream.next() == "0" && stream.match(/^[xX][0-9a-fA-F]+/)) || stream.match(/^[0-9]*(\.[0-9]+)?([eE][~+]?[0-9]+)?/))
return "number";
}
......@@ -117,8 +117,7 @@ CodeMirror.defineMode("oz", function (conf) {
if (ch == "%") {
stream.skipToEnd();
return 'comment';
}
else if (ch == "/") {
} else if (ch == "/") {
if (stream.eat("*")) {
state.tokenize = tokenComment;
return tokenComment(stream, state);
......@@ -126,7 +125,7 @@ CodeMirror.defineMode("oz", function (conf) {
}
// Single operators
if(singleOperators.test(ch)) {
if (singleOperators.test(ch)) {
return "operator";
}
......@@ -159,17 +158,15 @@ CodeMirror.defineMode("oz", function (conf) {
return null;
}
if(!state.hasPassedFirstStage && stream.eat("{")) {
if (!state.hasPassedFirstStage && stream.eat("{")) {
state.hasPassedFirstStage = true;
return "bracket";
}
else if(state.hasPassedFirstStage) {
} else if (state.hasPassedFirstStage) {
stream.match(/([A-Z][A-Za-z0-9_]*)|(`.+`)|\$/);
state.hasPassedFirstStage = false;
state.tokenize = tokenBase;
return "def"
}
else {
} else {
state.tokenize = tokenBase;
return null;
}
......@@ -245,8 +242,8 @@ CodeMirror.defineMode("oz", function (conf) {
blockCommentStart: "/*",
blockCommentEnd: "*/"
};
});
});
CodeMirror.defineMIME("text/x-oz", "oz");
CodeMirror.defineMIME("text/x-oz", "oz");
});
......@@ -2,15 +2,18 @@
<title>CodeMirror: Q mode</title>
<meta charset="utf-8"/>
<link rel=stylesheet href="../../doc/docs.css">
<link href="../../doc/docs.css" rel=stylesheet>
<link rel="stylesheet" href="../../lib/codemirror.css">
<link href="../../lib/codemirror.css" rel="stylesheet">
<script src="../../lib/codemirror.js"></script>
<script src="../../addon/edit/matchbrackets.js"></script>
<script src="q.js"></script>
<style>.CodeMirror {border-top: 1px solid black; border-bottom: 1px solid black;}</style>
<style>.CodeMirror {
border-top: 1px solid black;
border-bottom: 1px solid black;
}</style>
<div id=nav>
<a href="https://codemirror.net"><h1>CodeMirror</h1><img id=logo src="../../doc/logo.png" alt=""></a>
<a href="https://codemirror.net"><h1>CodeMirror</h1><img alt="" id=logo src="../../doc/logo.png"></a>
<ul>
<li><a href="../../index.html">Home</a>
......@@ -24,10 +27,10 @@
</div>
<article>
<h2>Q mode</h2>
<h2>Q mode</h2>
<div><textarea id="code" name="code">
<div><textarea id="code" name="code">
/ utilities to quickly load a csv file - for more exhaustive analysis of the csv contents see csvguess.q
/ 2009.09.20 - updated to match latest csvguess.q
......@@ -61,20 +64,43 @@ CHUNKSIZE:50000000 / used in fs2 (modified .Q.fs)
k)nameltrim:{$[~@x;.z.s'x;~(*x)in aA:.Q.a,.Q.A;(+/&\~x in aA)_x;x]}
k)fs2:{[f;s]((-7!s)>){[f;s;x]i:1+last@&0xa=r:1:(s;x;CHUNKSIZE);f@`\:i#r;x+i}[f;s]/0j}
cleanhdrs:{{$[ZAPHDRS;lower x except"_";x]}x where x in DELIM,.Q.an}
cancast:{nw:x$"";if[not x in"BXCS";nw:(min 0#;max 0#;::)@\:nw];$[not any nw in x$(11&count y)#y;$[11<count y;not any nw in x$y;1b];0b]}
cancast:{nw:x$"";if[not x in"BXCS";nw:(min 0#;max 0#;::)@\:nw];$[not any nw in x$(11&count y)#y;$[11<count (exec )xcol(exec 0xa=read1(file;0;WIDTHHDR))}
0xa=read1(file;0;15*WIDTHHDR))}
0xa=read1(file;0;WIDTHHDR));
read:{[file]data[file;info[file]]}
read10:{[file]data10[file;info[file]]}
11#where
DELIM
colhdrs:{[file]
`$nameltrim DELIM vs cleanhdrs first read0(file;0;1+first where 0xa=read1(file;0;WIDTHHDR))}
data:{[file;info]
(exec c from info where not t=" ")xcol(exec t from info;enlist DELIM)0:file}
data10:{[file;info]
data[;info](file;0;1+last 11#where 0xa=read1(file;0;15*WIDTHHDR))}
info0:{[file;onlycols]
colhdrs:`$nameltrim DELIM vs cleanhdrs first head:read0(file;0;1+last where 0xa=read1(file;0;WIDTHHDR));
loadfmts:(count colhdrs)#"S";if[count onlycols;loadfmts[where not colhdrs in onlycols]:"C"];
DELIM
DELIM)0:file}
`$nameltrim any
c
cleanhdrs
cleanhdrs
colhdrs)#
colhdrs:`$nameltrim
colhdrs:{[file]
data10:{[file;info] data:{[file;info] data[;info](file;0;1+last
first first
from from
head:read0(file;0;1+last in
info
info0:{[file;onlycols]
info;enlist
loadfmts:(count
not
nw
read0(file;0;1+first
read10:{[file]data10[file;info[file]]}
read:{[file]data[file;info[file]]}
t=" " t
vs
vs
where
where
where
x$y;1b];0b]}
y;not"S";if[count onlycols;loadfmts[where not colhdrs in onlycols]:"C"];
breaks:where 0xa=read1(file;0;floor(10+READLINES)*WIDTHHDR%count head);
nas:count as:colhdrs xcol(loadfmts;enlist DELIM)0:(file;0;1+last((1+READLINES)&count breaks)#breaks);
info:([]c:key flip as;v:value flip as);as:();
......@@ -99,20 +125,24 @@ info0:{[file;onlycols]
info:update t:"E",rule:100,maybe:1b from info where t="F",mw<9;
info:update t:"M",rule:110,maybe:1b from info where t in"nIHEF",mdot<2,mw within 4 7,.csv.cancast["M"]peach sdv;
info:update t:"D",rule:120,maybe:1b from info where t in"nI",mdot in 0 2,mw within 6 11,.csv.cancast["D"]peach sdv;
info:update t:"V",rule:130,maybe:1b from info where t="I",mw in 5 6,7<count each dchar,{all x like"*[0-9][0-5][0-9][0-5][0-9]"}peach sdv,.csv.cancast["V"]peach sdv; / 235959 12345
info:update t:"U",rule:140,maybe:1b from info where t="H",mw in 3 4,7<count each dchar,{all x like"*[0-9][0-5][0-9]"}peach sdv,.csv.cancast["U"]peach sdv; /2359
info:update t:"V",rule:130,maybe:1b from info where t="I",mw in 5 6,7<count dchar,{all each like x"*[0-9][0-5][0-9][0-5][0-9]"}peach sdv,.csv.cancast["V"]peach sdv; / 235959 12345
info:update t:"U",rule:140,maybe:1b from info where t="H",mw in 3 4,7<count dchar,{all each like x"*[0-9][0-5][0-9]"}peach sdv,.csv.cancast["U"]peach sdv; /2359
info:update t:"U",rule:150,maybe:0b from info where t="n",mw in 4 5,mdot=0,{all x like"*[0-9]:[0-5][0-9]"}peach sdv,.csv.cancast["U"]peach sdv;
info:update t:"T",rule:160,maybe:0b from info where t="n",mw within 7 12,mdot<2,{all x like"*[0-9]:[0-5][0-9]:[0-5][0-9]*"}peach sdv,.csv.cancast["T"]peach sdv;
info:update t:"T",rule:160,maybe:0b from info where t="n",mw within 7 12,mdot<2,{
all x like"*[0-9]:[0-5][0-9]:[0-5][0-9]*"}peach sdv,.csv.cancast["T"]peach sdv;
info:update t:"V",rule:170,maybe:0b from info where t="T",mw in 7 8,mdot=0,.csv.cancast["V"]peach sdv;
info:update t:"T",rule:180,maybe:1b from info where t in"EF",mw within 7 10,mdot=1,{all x like"*[0-9][0-5][0-9][0-5][0-9].*"}peach sdv,.csv.cancast["T"]peach sdv;
info:update t:"T",rule:180,maybe:1b from info where t in"EF",mw within 7 10,mdot=1,{
all x like"*[0-9][0-5][0-9][0-5][0-9].*"}peach sdv,.csv.cancast["T"]peach sdv;
info:update t:"Z",rule:190,maybe:0b from info where t="n",mw within 11 24,mdot<4,.csv.cancast["Z"]peach sdv;
info:update t:"P",rule:200,maybe:1b from info where t="n",mw within 12 29,mdot<4,{all x like"[12]*"}peach sdv,.csv.cancast["P"]peach sdv;
info:update t:"N",rule:210,maybe:1b from info where t="n",mw within 3 28,mdot=1,.csv.cancast["N"]peach sdv;
info:update t:"?",rule:220,maybe:0b from info where t="n"; / reset remaining maybe numeric
info:update t:"C",rule:230,maybe:0b from info where t="?",mw=1; / char
info:update t:"B",rule:240,maybe:0b from info where t in"HC",mw=1,mdot=0,{$[all x in"01tTfFyYnN";(any"0fFnN"in x)and any"1tTyY"in x;0b]}each dchar; / boolean
info:update t:"B",rule:240,maybe:0b from info where t in"HC",mw=1,mdot=0,{
$[all x in"01tTfFyYnN";(any"0fFnN"in x)and any"1tTyY"in x;0b]}each dchar; / boolean
info:update t:"B",rule:250,maybe:1b from info where t in"HC",mw=1,mdot=0,{all x in"01tTfFyYnN"}each dchar; / boolean
info:update t:"X",rule:260,maybe:0b from info where t="?",mw=2,{$[all x in"0123456789abcdefABCDEF";(any .Q.n in x)and any"abcdefABCDEF"in x;0b]}each dchar; /hex
info:update t:"X",rule:260,maybe:0b from info where t="?",mw=2,{
$[all x in"0123456789abcdefABCDEF";(any .Q.n in x)and any"abcdefABCDEF"in x;0b]}each dchar; /hex
info:update t:"S",rule:270,maybe:1b from info where t="?",mw<.csv.SYMMAXWIDTH,mw>1,gr<.csv.SYMMAXGR; / symbols (max width permitting)
info:update t:"*",rule:280,maybe:0b from info where t="?"; / the rest as strings
/ flag those S/* columns which could be encoded to integers (.Q.j10/x10/j12/x12) to avoid symbols
......@@ -141,4 +171,4 @@ bulkload:{[file;info]
</script>
<p><strong>MIME type defined:</strong> <code>text/x-q</code>.</p>
</article>
</article>
// CodeMirror, copyright (c) by Marijn Haverbeke and others
// Distributed under an MIT license: https://codemirror.net/LICENSE
(function(mod) {
(function (mod) {
if (typeof exports == "object" && typeof module == "object") // CommonJS
mod(require("../../lib/codemirror"));
else if (typeof define == "function" && define.amd) // AMD
define(["../../lib/codemirror"], mod);
else // Plain browser env
mod(CodeMirror);
})(function(CodeMirror) {
"use strict";
})(function (CodeMirror) {
"use strict";
CodeMirror.defineMode("q",function(config){
var indentUnit=config.indentUnit,
CodeMirror.defineMode("q", function (config) {
var indentUnit = config.indentUnit,
curPunc,
keywords=buildRE(["abs","acos","aj","aj0","all","and","any","asc","asin","asof","atan","attr","avg","avgs","bin","by","ceiling","cols","cor","cos","count","cov","cross","csv","cut","delete","deltas","desc","dev","differ","distinct","div","do","each","ej","enlist","eval","except","exec","exit","exp","fby","fills","first","fkeys","flip","floor","from","get","getenv","group","gtime","hclose","hcount","hdel","hopen","hsym","iasc","idesc","if","ij","in","insert","inter","inv","key","keys","last","like","list","lj","load","log","lower","lsq","ltime","ltrim","mavg","max","maxs","mcount","md5","mdev","med","meta","min","mins","mmax","mmin","mmu","mod","msum","neg","next","not","null","or","over","parse","peach","pj","plist","prd","prds","prev","prior","rand","rank","ratios","raze","read0","read1","reciprocal","reverse","rload","rotate","rsave","rtrim","save","scan","select","set","setenv","show","signum","sin","sqrt","ss","ssr","string","sublist","sum","sums","sv","system","tables","tan","til","trim","txf","type","uj","ungroup","union","update","upper","upsert","value","var","view","views","vs","wavg","where","where","while","within","wj","wj1","wsum","xasc","xbar","xcol","xcols","xdesc","xexp","xgroup","xkey","xlog","xprev","xrank"]),
E=/[|/&^!+:\\\-*%$=~#;@><,?_\'\"\[\(\]\)\s{}]/;
function buildRE(w){return new RegExp("^("+w.join("|")+")$");}
function tokenBase(stream,state){
var sol=stream.sol(),c=stream.next();
curPunc=null;
if(sol)
if(c=="/")
return(state.tokenize=tokenLineComment)(stream,state);
else if(c=="\\"){
if(stream.eol()||/\s/.test(stream.peek()))
return stream.skipToEnd(),/^\\\s*$/.test(stream.current())?(state.tokenize=tokenCommentToEOF)(stream):state.tokenize=tokenBase,"comment";
keywords = buildRE(["abs", "acos", "aj", "aj0", "all", "and", "any", "asc", "asin", "asof", "atan", "attr", "avg", "avgs", "bin", "by", "ceiling", "cols", "cor", "cos", "count", "cov", "cross", "csv", "cut", "delete", "deltas", "desc", "dev", "differ", "distinct", "div", "do", "each", "ej", "enlist", "eval", "except", "exec", "exit", "exp", "fby", "fills", "first", "fkeys", "flip", "floor", "from", "get", "getenv", "group", "gtime", "hclose", "hcount", "hdel", "hopen", "hsym", "iasc", "idesc", "if", "ij", "in", "insert", "inter", "inv", "key", "keys", "last", "like", "list", "lj", "load", "log", "lower", "lsq", "ltime", "ltrim", "mavg", "max", "maxs", "mcount", "md5", "mdev", "med", "meta", "min", "mins", "mmax", "mmin", "mmu", "mod", "msum", "neg", "next", "not", "null", "or", "over", "parse", "peach", "pj", "plist", "prd", "prds", "prev", "prior", "rand", "rank", "ratios", "raze", "read0", "read1", "reciprocal", "reverse", "rload", "rotate", "rsave", "rtrim", "save", "scan", "select", "set", "setenv", "show", "signum", "sin", "sqrt", "ss", "ssr", "string", "sublist", "sum", "sums", "sv", "system", "tables", "tan", "til", "trim", "txf", "type", "uj", "ungroup", "union", "update", "upper", "upsert", "value", "var", "view", "views", "vs", "wavg", "where", "where", "while", "within", "wj", "wj1", "wsum", "xasc", "xbar", "xcol", "xcols", "xdesc", "xexp", "xgroup", "xkey", "xlog", "xprev", "xrank"]),
E = /[|/&^!+:\\\-*%$=~#;@><,?_\'\"\[\(\]\)\s{}]/;
function buildRE(w) {
return new RegExp("^(" + w.join("|") + ")$");
}
function tokenBase(stream, state) {
var sol = stream.sol(), c = stream.next();
curPunc = null;
if (sol)
if (c == "/")
return (state.tokenize = tokenLineComment)(stream, state);
else if (c == "\\") {
if (stream.eol() || /\s/.test(stream.peek()))
return stream.skipToEnd(), /^\\\s*$/.test(stream.current()) ? (state.tokenize = tokenCommentToEOF)(stream) : state.tokenize = tokenBase, "comment";
else
return state.tokenize=tokenBase,"builtin";
}
if(/\s/.test(c))
return stream.peek()=="/"?(stream.skipToEnd(),"comment"):"whitespace";
if(c=='"')
return(state.tokenize=tokenString)(stream,state);
if(c=='`')
return stream.eatWhile(/[A-Za-z\d_:\/.]/),"symbol";
if(("."==c&&/\d/.test(stream.peek()))||/\d/.test(c)){
var t=null;
return state.tokenize = tokenBase, "builtin";
}
if (/\s/.test(c))
return stream.peek() == "/" ? (stream.skipToEnd(), "comment") : "whitespace";
if (c == '"')
return (state.tokenize = tokenString)(stream, state);
if (c == '`')
return stream.eatWhile(/[A-Za-z\d_:\/.]/), "symbol";
if (("." == c && /\d/.test(stream.peek())) || /\d/.test(c)) {
var t = null;
stream.backUp(1);
if(stream.match(/^\d{4}\.\d{2}(m|\.\d{2}([DT](\d{2}(:\d{2}(:\d{2}(\.\d{1,9})?)?)?)?)?)/)
if (stream.match(/^\d{4}\.\d{2}(m|\.\d{2}([DT](\d{2}(:\d{2}(:\d{2}(\.\d{1,9})?)?)?)?)?)/)
|| stream.match(/^\d+D(\d{2}(:\d{2}(:\d{2}(\.\d{1,9})?)?)?)/)
|| stream.match(/^\d{2}:\d{2}(:\d{2}(\.\d{1,9})?)?/)
|| stream.match(/^\d+[ptuv]{1}/))
t="temporal";
else if(stream.match(/^0[NwW]{1}/)
t = "temporal";
else if (stream.match(/^0[NwW]{1}/)
|| stream.match(/^0x[\da-fA-F]*/)
|| stream.match(/^[01]+[b]{1}/)
|| stream.match(/^\d+[chijn]{1}/)
|| stream.match(/-?\d*(\.\d*)?(e[+\-]?\d+)?(e|f)?/))
t="number";
return(t&&(!(c=stream.peek())||E.test(c)))?t:(stream.next(),"error");
t = "number";
return (t && (!(c = stream.peek()) || E.test(c))) ? t : (stream.next(), "error");
}
if(/[A-Za-z]|\./.test(c))
return stream.eatWhile(/[A-Za-z._\d]/),keywords.test(stream.current())?"keyword":"variable";
if(/[|/&^!+:\\\-*%$=~#;@><\.,?_\']/.test(c))
if (/[A-Za-z]|\./.test(c))
return stream.eatWhile(/[A-Za-z._\d]/), keywords.test(stream.current()) ? "keyword" : "variable";
if (/[|/&^!+:\\\-*%$=~#;@><\.,?_\']/.test(c))
return null;
if(/[{}\(\[\]\)]/.test(c))
if (/[{}\(\[\]\)]/.test(c))
return null;
return"error";
return "error";
}
function tokenLineComment(stream,state){
return stream.skipToEnd(),/\/\s*$/.test(stream.current())?(state.tokenize=tokenBlockComment)(stream,state):(state.tokenize=tokenBase),"comment";
function tokenLineComment(stream, state) {
return stream.skipToEnd(), /\/\s*$/.test(stream.current()) ? (state.tokenize = tokenBlockComment)(stream, state) : (state.tokenize = tokenBase), "comment";
}
function tokenBlockComment(stream,state){
var f=stream.sol()&&stream.peek()=="\\";
function tokenBlockComment(stream, state) {
var f = stream.sol() && stream.peek() == "\\";
stream.skipToEnd();
if(f&&/^\\\s*$/.test(stream.current()))
state.tokenize=tokenBase;
return"comment";
}
function tokenCommentToEOF(stream){return stream.skipToEnd(),"comment";}
function tokenString(stream,state){
var escaped=false,next,end=false;
while((next=stream.next())){
if(next=="\""&&!escaped){end=true;break;}
escaped=!escaped&&next=="\\";
}
if(end)state.tokenize=tokenBase;
return"string";
}
function pushContext(state,type,col){state.context={prev:state.context,indent:state.indent,col:col,type:type};}
function popContext(state){state.indent=state.context.indent;state.context=state.context.prev;}
return{
startState:function(){
return{tokenize:tokenBase,
context:null,
indent:0,
col:0};
if (f && /^\\\s*$/.test(stream.current()))
state.tokenize = tokenBase;
return "comment";
}
function tokenCommentToEOF(stream) {
return stream.skipToEnd(), "comment";
}
function tokenString(stream, state) {
var escaped = false, next, end = false;
while ((next = stream.next())) {
if (next == "\"" && !escaped) {
end = true;
break;
}
escaped = !escaped && next == "\\";
}
if (end) state.tokenize = tokenBase;
return "string";
}
function pushContext(state, type, col) {
state.context = {prev: state.context, indent: state.indent, col: col, type: type};
}
function popContext(state) {
state.indent = state.context.indent;
state.context = state.context.prev;
}
return {
startState: function () {
return {
tokenize: tokenBase,
context: null,
indent: 0,
col: 0
};
},
token:function(stream,state){
if(stream.sol()){
if(state.context&&state.context.align==null)
state.context.align=false;
state.indent=stream.indentation();
token: function (stream, state) {
if (stream.sol()) {
if (state.context && state.context.align == null)
state.context.align = false;
state.indent = stream.indentation();
}
//if (stream.eatSpace()) return null;
var style=state.tokenize(stream,state);
if(style!="comment"&&state.context&&state.context.align==null&&state.context.type!="pattern"){
state.context.align=true;
}
if(curPunc=="(")pushContext(state,")",stream.column());
else if(curPunc=="[")pushContext(state,"]",stream.column());
else if(curPunc=="{")pushContext(state,"}",stream.column());
else if(/[\]\}\)]/.test(curPunc)){
while(state.context&&state.context.type=="pattern")popContext(state);
if(state.context&&curPunc==state.context.type)popContext(state);
}
else if(curPunc=="."&&state.context&&state.context.type=="pattern")popContext(state);
else if(/atom|string|variable/.test(style)&&state.context){
if(/[\}\]]/.test(state.context.type))
pushContext(state,"pattern",stream.column());
else if(state.context.type=="pattern"&&!state.context.align){
state.context.align=true;
state.context.col=stream.column();
var style = state.tokenize(stream, state);
if (style != "comment" && state.context && state.context.align == null && state.context.type != "pattern") {
state.context.align = true;
}
if (curPunc == "(") pushContext(state, ")", stream.column());
else if (curPunc == "[") pushContext(state, "]", stream.column());
else if (curPunc == "{") pushContext(state, "}", stream.column());
else if (/[\]\}\)]/.test(curPunc)) {
while (state.context && state.context.type == "pattern") popContext(state);
if (state.context && curPunc == state.context.type) popContext(state);
} else if (curPunc == "." && state.context && state.context.type == "pattern") popContext(state);
else if (/atom|string|variable/.test(style) && state.context) {
if (/[\}\]]/.test(state.context.type))
pushContext(state, "pattern", stream.column());
else if (state.context.type == "pattern" && !state.context.align) {
state.context.align = true;
state.context.col = stream.column();
}
}
return style;
},
indent:function(state,textAfter){
var firstChar=textAfter&&textAfter.charAt(0);
var context=state.context;
if(/[\]\}]/.test(firstChar))
while (context&&context.type=="pattern")context=context.prev;
var closing=context&&firstChar==context.type;
if(!context)
indent: function (state, textAfter) {
var firstChar = textAfter && textAfter.charAt(0);
var context = state.context;
if (/[\]\}]/.test(firstChar))
while (context && context.type == "pattern") context = context.prev;
var closing = context && firstChar == context.type;
if (!context)
return 0;
else if(context.type=="pattern")
else if (context.type == "pattern")
return context.col;
else if(context.align)
return context.col+(closing?0:1);
else if (context.align)
return context.col + (closing ? 0 : 1);
else
return context.indent+(closing?0:indentUnit);
return context.indent + (closing ? 0 : indentUnit);
}
};
});
CodeMirror.defineMIME("text/x-q","q");
});
CodeMirror.defineMIME("text/x-q", "q");
});
......@@ -2,20 +2,37 @@
<title>CodeMirror: R mode</title>
<meta charset="utf-8"/>
<link rel=stylesheet href="../../doc/docs.css">
<link href="../../doc/docs.css" rel=stylesheet>
<link rel="stylesheet" href="../../lib/codemirror.css">
<link href="../../lib/codemirror.css" rel="stylesheet">
<script src="../../lib/codemirror.js"></script>
<script src="r.js"></script>
<style>
.CodeMirror { border-top: 1px solid silver; border-bottom: 1px solid silver; }
.cm-s-default span.cm-semi { color: blue; font-weight: bold; }
.cm-s-default span.cm-dollar { color: orange; font-weight: bold; }
.cm-s-default span.cm-arrow { color: brown; }
.cm-s-default span.cm-arg-is { color: brown; }
</style>
.CodeMirror {
border-top: 1px solid silver;
border-bottom: 1px solid silver;
}
.cm-s-default span.cm-semi {
color: blue;
font-weight: bold;
}
.cm-s-default span.cm-dollar {
color: orange;
font-weight: bold;
}
.cm-s-default span.cm-arrow {
color: brown;
}
.cm-s-default span.cm-arg-is {
color: brown;
}
</style>
<div id=nav>
<a href="https://codemirror.net"><h1>CodeMirror</h1><img id=logo src="../../doc/logo.png" alt=""></a>
<a href="https://codemirror.net"><h1>CodeMirror</h1><img alt="" id=logo src="../../doc/logo.png"></a>
<ul>
<li><a href="../../index.html">Home</a>
......@@ -29,8 +46,8 @@
</div>
<article>
<h2>R mode</h2>
<form><textarea id="code" name="code">
<h2>R mode</h2>
<form><textarea id="code" name="code">
X <- list(height = 5.4, weight = 54)
cat("Printing objects: "); print(X)
print("Accessing individual elements:")
......@@ -85,4 +102,4 @@ c = 3
<p>Development of the CodeMirror R mode was kindly sponsored
by <a href="https://twitter.com/ubalo">Ubalo</a>.</p>
</article>
</article>
// CodeMirror, copyright (c) by Marijn Haverbeke and others
// Distributed under an MIT license: https://codemirror.net/LICENSE
(function(mod) {
(function (mod) {
if (typeof exports == "object" && typeof module == "object") // CommonJS
mod(require("../../lib/codemirror"));
else if (typeof define == "function" && define.amd) // AMD
define(["../../lib/codemirror"], mod);
else // Plain browser env
mod(CodeMirror);
})(function(CodeMirror) {
"use strict";
})(function (CodeMirror) {
"use strict";
CodeMirror.registerHelper("wordChars", "r", /[\w.]/);
CodeMirror.registerHelper("wordChars", "r", /[\w.]/);
CodeMirror.defineMode("r", function(config) {
CodeMirror.defineMode("r", function (config) {
function wordObj(words) {
var res = {};
for (var i = 0; i < words.length; ++i) res[words[i]] = true;
return res;
}
var commonAtoms = ["NULL", "NA", "Inf", "NaN", "NA_integer_", "NA_real_", "NA_complex_", "NA_character_", "TRUE", "FALSE"];
var commonBuiltins = ["list", "quote", "bquote", "eval", "return", "call", "parse", "deparse"];
var commonKeywords = ["if", "else", "repeat", "while", "function", "for", "in", "next", "break"];
......@@ -95,7 +96,7 @@ CodeMirror.defineMode("r", function(config) {
}
function tokenString(quote) {
return function(stream, state) {
return function (stream, state) {
if (stream.eat("\\")) {
var ch = stream.next();
if (ch == "x") stream.match(/^[a-f0-9]{2}/i);
......@@ -107,8 +108,14 @@ CodeMirror.defineMode("r", function(config) {
} else {
var next;
while ((next = stream.next()) != null) {
if (next == quote) { state.tokenize = tokenBase; break; }
if (next == "\\") { stream.backUp(1); break; }
if (next == quote) {
state.tokenize = tokenBase;
break;
}
if (next == "\\") {
stream.backUp(1);
break;
}
}
return "string";
}
......@@ -118,36 +125,46 @@ CodeMirror.defineMode("r", function(config) {
var ALIGN_YES = 1, ALIGN_NO = 2, BRACELESS = 4
function push(state, type, stream) {
state.ctx = {type: type,
state.ctx = {
type: type,
indent: state.indent,
flags: 0,
column: stream.column(),
prev: state.ctx};
prev: state.ctx
};
}
function setFlag(state, flag) {
var ctx = state.ctx
state.ctx = {type: ctx.type,
state.ctx = {
type: ctx.type,
indent: ctx.indent,
flags: ctx.flags | flag,
column: ctx.column,
prev: ctx.prev}
prev: ctx.prev
}
}
function pop(state) {
state.indent = state.ctx.indent;
state.ctx = state.ctx.prev;
}
return {
startState: function() {
return {tokenize: tokenBase,
ctx: {type: "top",
startState: function () {
return {
tokenize: tokenBase,
ctx: {
type: "top",
indent: -config.indentUnit,
flags: ALIGN_NO},
flags: ALIGN_NO
},
indent: 0,
afterIdent: false};
afterIdent: false
};
},
token: function(stream, state) {
token: function (stream, state) {
if (stream.sol()) {
if ((state.ctx.flags & 3) == 0) state.ctx.flags |= ALIGN_NO
if (state.ctx.flags & BRACELESS) pop(state)
......@@ -162,8 +179,7 @@ CodeMirror.defineMode("r", function(config) {
else if (curPunc == "(") {
push(state, ")", stream);
if (state.afterIdent) state.ctx.argList = true;
}
else if (curPunc == "[") push(state, "]", stream);
} else if (curPunc == "[") push(state, "]", stream);
else if (curPunc == "block") push(state, "block", stream);
else if (curPunc == state.ctx.type) pop(state);
else if (state.ctx.type == "block" && style != "comment") setFlag(state, BRACELESS)
......@@ -171,7 +187,7 @@ CodeMirror.defineMode("r", function(config) {
return style;
},
indent: function(state, textAfter) {
indent: function (state, textAfter) {
if (state.tokenize != tokenBase) return 0;
var firstChar = textAfter && textAfter.charAt(0), ctx = state.ctx,
closing = firstChar == ctx.type;
......@@ -183,8 +199,8 @@ CodeMirror.defineMode("r", function(config) {
lineComment: "#"
};
});
});
CodeMirror.defineMIME("text/x-rsrc", "r");
CodeMirror.defineMIME("text/x-rsrc", "r");
});
......@@ -2,20 +2,32 @@
<title>CodeMirror: VB.NET mode</title>
<meta charset="utf-8"/>
<link rel=stylesheet href="../../doc/docs.css">
<link href="../../doc/docs.css" rel=stylesheet>
<link rel="stylesheet" href="../../lib/codemirror.css">
<link href="../../lib/codemirror.css" rel="stylesheet">
<link href="http://fonts.googleapis.com/css?family=Inconsolata" rel="stylesheet">
<script src="../../lib/codemirror.js"></script>
<script src="vb.js"></script>
<script src="../../addon/runmode/runmode.js"></script>
<style>
.CodeMirror {border: 1px solid #aaa; height:210px; height: auto;}
.CodeMirror-scroll { overflow-x: auto; overflow-y: hidden;}
.CodeMirror pre { font-family: Inconsolata; font-size: 14px}
</style>
.CodeMirror {
border: 1px solid #aaa;
height: 210px;
height: auto;
}
.CodeMirror-scroll {
overflow-x: auto;
overflow-y: hidden;
}
.CodeMirror pre {
font-family: Inconsolata;
font-size: 14px
}
</style>
<div id=nav>
<a href="https://codemirror.net"><h1>CodeMirror</h1><img id=logo src="../../doc/logo.png" alt=""></a>
<a href="https://codemirror.net"><h1>CodeMirror</h1><img alt="" id=logo src="../../doc/logo.png"></a>
<ul>
<li><a href="../../index.html">Home</a>
......@@ -29,9 +41,9 @@
</div>
<article>
<h2>VB.NET mode</h2>
<h2>VB.NET mode</h2>
<div id="edit">
<textarea name="code" id="code" >
<textarea id="code" name="code">
Class rocket
Private quality as Double
Public Sub launch() as String
......@@ -45,5 +57,5 @@ End class
</textarea>
</div>
<p>MIME type defined: <code>text/x-vb</code>.</p>
<script>CodeMirror.fromTextArea(document.getElementById("code"))</script>
<script>CodeMirror.fromTextArea(document.getElementById("code"))</script>
</article>
// CodeMirror, copyright (c) by Marijn Haverbeke and others
// Distributed under an MIT license: https://codemirror.net/LICENSE
(function(mod) {
(function (mod) {
if (typeof exports == "object" && typeof module == "object") // CommonJS
mod(require("../../lib/codemirror"));
else if (typeof define == "function" && define.amd) // AMD
define(["../../lib/codemirror"], mod);
else // Plain browser env
mod(CodeMirror);
})(function(CodeMirror) {
"use strict";
})(function (CodeMirror) {
"use strict";
CodeMirror.defineMode("vb", function(conf, parserConf) {
CodeMirror.defineMode("vb", function (conf, parserConf) {
var ERRORCLASS = 'error';
function wordRegexp(words) {
......@@ -25,9 +25,9 @@ CodeMirror.defineMode("vb", function(conf, parserConf) {
var tripleDelimiters = new RegExp("^((//=)|(>>=)|(<<=)|(\\*\\*=))");
var identifiers = new RegExp("^[_A-Za-z][_A-Za-z0-9]*");
var openingKeywords = ['class','module', 'sub','enum','select','while','if','function', 'get','set','property', 'try', 'structure', 'synclock', 'using', 'with'];
var middleKeywords = ['else','elseif','case', 'catch', 'finally'];
var endKeywords = ['next','loop'];
var openingKeywords = ['class', 'module', 'sub', 'enum', 'select', 'while', 'if', 'function', 'get', 'set', 'property', 'try', 'structure', 'synclock', 'using', 'with'];
var middleKeywords = ['else', 'elseif', 'case', 'catch', 'finally'];
var endKeywords = ['next', 'loop'];
var operatorKeywords = ['and', "andalso", 'or', 'orelse', 'xor', 'in', 'not', 'is', 'isnot', 'like'];
var wordOperators = wordRegexp(operatorKeywords);
......@@ -58,6 +58,7 @@ CodeMirror.defineMode("vb", function(conf, parserConf) {
function dedent(_stream, state) {
state.currentIndent--;
}
// tokenizers
function tokenBase(stream, state) {
if (stream.eatSpace()) {
......@@ -77,9 +78,13 @@ CodeMirror.defineMode("vb", function(conf, parserConf) {
if (stream.match(/^((&H)|(&O))?[0-9\.a-f]/i, false)) {
var floatLiteral = false;
// Floats
if (stream.match(/^\d*\.\d+F?/i)) { floatLiteral = true; }
else if (stream.match(/^\d+\.\d*F?/)) { floatLiteral = true; }
else if (stream.match(/^\.\d+F?/)) { floatLiteral = true; }
if (stream.match(/^\d*\.\d+F?/i)) {
floatLiteral = true;
} else if (stream.match(/^\d+\.\d*F?/)) {
floatLiteral = true;
} else if (stream.match(/^\.\d+F?/)) {
floatLiteral = true;
}
if (floatLiteral) {
// Float literals may be "imaginary"
......@@ -89,9 +94,13 @@ CodeMirror.defineMode("vb", function(conf, parserConf) {
// Integers
var intLiteral = false;
// Hex
if (stream.match(/^&H[0-9a-f]+/i)) { intLiteral = true; }
if (stream.match(/^&H[0-9a-f]+/i)) {
intLiteral = true;
}
// Octal
else if (stream.match(/^&O[0-7]+/i)) { intLiteral = true; }
else if (stream.match(/^&O[0-7]+/i)) {
intLiteral = true;
}
// Decimal
else if (stream.match(/^[1-9]\d*F?/)) {
// Decimal literals may be "imaginary"
......@@ -100,7 +109,9 @@ CodeMirror.defineMode("vb", function(conf, parserConf) {
intLiteral = true;
}
// Zero by itself with no other piece of number.
else if (stream.match(/^0(?![\dx])/i)) { intLiteral = true; }
else if (stream.match(/^0(?![\dx])/i)) {
intLiteral = true;
}
if (intLiteral) {
// Integer literals may be "long"
stream.eat(/L/i);
......@@ -127,13 +138,13 @@ CodeMirror.defineMode("vb", function(conf, parserConf) {
return null;
}
if (stream.match(doOpening)) {
indent(stream,state);
indent(stream, state);
state.doInCurrentLine = true;
return 'keyword';
}
if (stream.match(opening)) {
if (! state.doInCurrentLine)
indent(stream,state);
if (!state.doInCurrentLine)
indent(stream, state);
else
state.doInCurrentLine = false;
return 'keyword';
......@@ -143,12 +154,12 @@ CodeMirror.defineMode("vb", function(conf, parserConf) {
}
if (stream.match(doubleClosing)) {
dedent(stream,state);
dedent(stream,state);
dedent(stream, state);
dedent(stream, state);
return 'keyword';
}
if (stream.match(closing)) {
dedent(stream,state);
dedent(stream, state);
return 'keyword';
}
......@@ -173,7 +184,7 @@ CodeMirror.defineMode("vb", function(conf, parserConf) {
var singleline = delimiter.length == 1;
var OUTCLASS = 'string';
return function(stream, state) {
return function (stream, state) {
while (!stream.eol()) {
stream.eatWhile(/[^'"]/);
if (stream.match(delimiter)) {
......@@ -212,7 +223,7 @@ CodeMirror.defineMode("vb", function(conf, parserConf) {
var delimiter_index = '[({'.indexOf(current);
if (delimiter_index !== -1) {
indent(stream, state );
indent(stream, state);
}
if (indentInfo === 'dedent') {
if (dedent(stream, state)) {
......@@ -230,8 +241,8 @@ CodeMirror.defineMode("vb", function(conf, parserConf) {
}
var external = {
electricChars:"dDpPtTfFeE ",
startState: function() {
electricChars: "dDpPtTfFeE ",
startState: function () {
return {
tokenize: tokenBase,
lastToken: null,
......@@ -243,7 +254,7 @@ CodeMirror.defineMode("vb", function(conf, parserConf) {
};
},
token: function(stream, state) {
token: function (stream, state) {
if (stream.sol()) {
state.currentIndent += state.nextLineIndent;
state.nextLineIndent = 0;
......@@ -251,25 +262,24 @@ CodeMirror.defineMode("vb", function(conf, parserConf) {
}
var style = tokenLexer(stream, state);
state.lastToken = {style:style, content: stream.current()};
state.lastToken = {style: style, content: stream.current()};
return style;
},
indent: function(state, textAfter) {
var trueText = textAfter.replace(/^\s+|\s+$/g, '') ;
if (trueText.match(closing) || trueText.match(doubleClosing) || trueText.match(middle)) return conf.indentUnit*(state.currentIndent-1);
if(state.currentIndent < 0) return 0;
indent: function (state, textAfter) {
var trueText = textAfter.replace(/^\s+|\s+$/g, '');
if (trueText.match(closing) || trueText.match(doubleClosing) || trueText.match(middle)) return conf.indentUnit * (state.currentIndent - 1);
if (state.currentIndent < 0) return 0;
return state.currentIndent * conf.indentUnit;
},
lineComment: "'"
};
return external;
});
});
CodeMirror.defineMIME("text/x-vb", "vb");
CodeMirror.defineMIME("text/x-vb", "vb");
});
......@@ -16,7 +16,9 @@ CodeMirror.defineMode("null", () => ({token: stream => stream.skipToEnd()}))
CodeMirror.defineMIME("text/plain", "null")
CodeMirror.registerHelper = CodeMirror.registerGlobalHelper = Math.min
CodeMirror.splitLines = function(string) { return string.split(/\r?\n|\r/) }
CodeMirror.splitLines = function (string) {
return string.split(/\r?\n|\r/)
}
CodeMirror.defaults = { indentUnit: 2 }
CodeMirror.defaults = {indentUnit: 2}
export default CodeMirror
......@@ -16,6 +16,8 @@ exports.defineMode("null", () => ({token: stream => stream.skipToEnd()}))
exports.defineMIME("text/plain", "null")
exports.registerHelper = exports.registerGlobalHelper = Math.min
exports.splitLines = function(string) { return string.split(/\r?\n|\r/) }
exports.splitLines = function (string) {
return string.split(/\r?\n|\r/)
}
exports.defaults = { indentUnit: 2 }
exports.defaults = {indentUnit: 2}
import { CodeMirror } from "./edit/main.js"
import {CodeMirror} from "./edit/main.js"
export default CodeMirror
import { gecko, ie, ie_version, mobile, webkit } from "../util/browser.js"
import { elt, eltP } from "../util/dom.js"
import { scrollerGap } from "../util/misc.js"
import { getGutters, renderGutters } from "./gutters.js"
import {gecko, ie, ie_version, mobile, webkit} from "../util/browser.js"
import {elt, eltP} from "../util/dom.js"
import {scrollerGap} from "../util/misc.js"
import {getGutters, renderGutters} from "./gutters.js"
// The display handles the DOM integration, both for input reading
// and content drawing. It holds references to DOM nodes and
......@@ -50,7 +50,10 @@ export function Display(place, doc, input, options) {
d.wrapper = elt("div", [d.scrollbarFiller, d.gutterFiller, d.scroller], "CodeMirror")
// Work around IE7 z-index bug (not perfect, hence IE7 not really being supported)
if (ie && ie_version < 8) { d.gutters.style.zIndex = -1; d.scroller.style.paddingRight = 0 }
if (ie && ie_version < 8) {
d.gutters.style.zIndex = -1;
d.scroller.style.paddingRight = 0
}
if (!webkit && !(gecko && mobile)) d.scroller.draggable = true
if (place) {
......
import { restartBlink } from "./selection.js"
import { webkit } from "../util/browser.js"
import { addClass, rmClass } from "../util/dom.js"
import { signal } from "../util/event.js"
import {restartBlink} from "./selection.js"
import {webkit} from "../util/browser.js"
import {addClass, rmClass} from "../util/dom.js"
import {signal} from "../util/event.js"
export function ensureFocus(cm) {
if (!cm.state.focused) { cm.display.input.focus(); onFocus(cm) }
if (!cm.state.focused) {
cm.display.input.focus();
onFocus(cm)
}
}
export function delayBlurEvent(cm) {
cm.state.delayingBlurEvent = true
setTimeout(() => { if (cm.state.delayingBlurEvent) {
setTimeout(() => {
if (cm.state.delayingBlurEvent) {
cm.state.delayingBlurEvent = false
onBlur(cm)
} }, 100)
}
}, 100)
}
export function onFocus(cm, e) {
......@@ -34,6 +39,7 @@ export function onFocus(cm, e) {
}
restartBlink(cm)
}
export function onBlur(cm, e) {
if (cm.state.delayingBlurEvent) return
......@@ -43,5 +49,7 @@ export function onBlur(cm, e) {
rmClass(cm.display.wrapper, "CodeMirror-focused")
}
clearInterval(cm.display.blinker)
setTimeout(() => { if (!cm.state.focused) cm.display.shift = false }, 150)
setTimeout(() => {
if (!cm.state.focused) cm.display.shift = false
}, 150)
}
import { elt, removeChildren } from "../util/dom.js"
import { regChange } from "./view_tracking.js"
import { alignHorizontally } from "./line_numbers.js"
import { updateGutterSpace } from "./update_display.js"
import {elt, removeChildren} from "../util/dom.js"
import {regChange} from "./view_tracking.js"
import {alignHorizontally} from "./line_numbers.js"
import {updateGutterSpace} from "./update_display.js"
export function getGutters(gutters, lineNumbers) {
let result = [], sawLineNumbers = false
for (let i = 0; i < gutters.length; i++) {
let name = gutters[i], style = null
if (typeof name != "string") { style = name.style; name = name.className }
if (typeof name != "string") {
style = name.style;
name = name.className
}
if (name == "CodeMirror-linenumbers") {
if (!lineNumbers) continue
else sawLineNumbers = true
......
import { getContextBefore, highlightLine, processLine } from "../line/highlight.js"
import { copyState } from "../modes.js"
import { bind } from "../util/misc.js"
import {getContextBefore, highlightLine, processLine} from "../line/highlight.js"
import {copyState} from "../modes.js"
import {bind} from "../util/misc.js"
import { runInOp } from "./operations.js"
import { regLineChange } from "./view_tracking.js"
import {runInOp} from "./operations.js"
import {regLineChange} from "./view_tracking.js"
// HIGHLIGHT WORKER
......
import { lineNumberFor } from "../line/utils_line.js"
import { compensateForHScroll } from "../measurement/position_measurement.js"
import { elt } from "../util/dom.js"
import {lineNumberFor} from "../line/utils_line.js"
import {compensateForHScroll} from "../measurement/position_measurement.js"
import {elt} from "../util/dom.js"
import { updateGutterSpace } from "./update_display.js"
import {updateGutterSpace} from "./update_display.js"
// Re-align line numbers and gutter marks to compensate for
// horizontal scrolling.
......
import { getMode } from "../modes.js"
import {getMode} from "../modes.js"
import { startWorker } from "./highlight_worker.js"
import { regChange } from "./view_tracking.js"
import {startWorker} from "./highlight_worker.js"
import {regChange} from "./view_tracking.js"
// Used to get the editor into a consistent state again when options change.
......
import { clipPos } from "../line/pos.js"
import { findMaxLine } from "../line/spans.js"
import { displayWidth, measureChar, scrollGap } from "../measurement/position_measurement.js"
import { signal } from "../util/event.js"
import { activeElt } from "../util/dom.js"
import { finishOperation, pushOperation } from "../util/operation_group.js"
import { ensureFocus } from "./focus.js"
import { measureForScrollbars, updateScrollbars } from "./scrollbars.js"
import { restartBlink } from "./selection.js"
import { maybeScrollWindow, scrollPosIntoView, setScrollLeft, setScrollTop } from "./scrolling.js"
import { DisplayUpdate, maybeClipScrollbars, postUpdateDisplay, setDocumentHeight, updateDisplayIfNeeded } from "./update_display.js"
import { updateHeightsInViewport } from "./update_lines.js"
import {clipPos} from "../line/pos.js"
import {findMaxLine} from "../line/spans.js"
import {displayWidth, measureChar, scrollGap} from "../measurement/position_measurement.js"
import {signal} from "../util/event.js"
import {activeElt} from "../util/dom.js"
import {finishOperation, pushOperation} from "../util/operation_group.js"
import {ensureFocus} from "./focus.js"
import {measureForScrollbars, updateScrollbars} from "./scrollbars.js"
import {restartBlink} from "./selection.js"
import {maybeScrollWindow, scrollPosIntoView, setScrollLeft, setScrollTop} from "./scrolling.js"
import {
DisplayUpdate,
maybeClipScrollbars,
postUpdateDisplay,
setDocumentHeight,
updateDisplayIfNeeded
} from "./update_display.js"
import {updateHeightsInViewport} from "./update_lines.js"
// Operations are used to wrap a series of changes to the editor
// state in such a way that each change won't have to update the
......@@ -19,6 +25,7 @@ import { updateHeightsInViewport } from "./update_lines.js"
// combined and executed at once.
let nextOpId = 0
// Start a new operation.
export function startOperation(cm) {
cm.curOp = {
......@@ -172,34 +179,49 @@ function endOperation_finish(op) {
export function runInOp(cm, f) {
if (cm.curOp) return f()
startOperation(cm)
try { return f() }
finally { endOperation(cm) }
try {
return f()
} finally {
endOperation(cm)
}
}
// Wraps a function in an operation. Returns the wrapped function.
export function operation(cm, f) {
return function() {
return function () {
if (cm.curOp) return f.apply(cm, arguments)
startOperation(cm)
try { return f.apply(cm, arguments) }
finally { endOperation(cm) }
try {
return f.apply(cm, arguments)
} finally {
endOperation(cm)
}
}
}
// Used to add methods to editor and doc instances, wrapping them in
// operations.
export function methodOp(f) {
return function() {
return function () {
if (this.curOp) return f.apply(this, arguments)
startOperation(this)
try { return f.apply(this, arguments) }
finally { endOperation(this) }
try {
return f.apply(this, arguments)
} finally {
endOperation(this)
}
}
}
export function docMethodOp(f) {
return function() {
return function () {
let cm = this.cm
if (!cm || cm.curOp) return f.apply(this, arguments)
startOperation(cm)
try { return f.apply(this, arguments) }
finally { endOperation(cm) }
try {
return f.apply(this, arguments)
} finally {
endOperation(cm)
}
}
}
import { chrome, gecko, ie, mac, presto, safari, webkit } from "../util/browser.js"
import { e_preventDefault } from "../util/event.js"
import {chrome, gecko, ie, mac, presto, safari, webkit} from "../util/browser.js"
import {e_preventDefault} from "../util/event.js"
import { updateDisplaySimple } from "./update_display.js"
import { setScrollLeft, updateScrollTop } from "./scrolling.js"
import {updateDisplaySimple} from "./update_display.js"
import {setScrollLeft, updateScrollTop} from "./scrolling.js"
// Since the delta values reported on mouse wheel events are
// unstandardized between browsers and even browser versions, and
......@@ -23,7 +23,7 @@ let wheelSamples = 0, wheelPixelsPerUnit = null
if (ie) wheelPixelsPerUnit = -.53
else if (gecko) wheelPixelsPerUnit = 15
else if (chrome) wheelPixelsPerUnit = -.7
else if (safari) wheelPixelsPerUnit = -1/3
else if (safari) wheelPixelsPerUnit = -1 / 3
function wheelEventDelta(e) {
let dx = e.wheelDeltaX, dy = e.wheelDeltaY
......@@ -32,6 +32,7 @@ function wheelEventDelta(e) {
else if (dy == null) dy = e.wheelDelta
return {x: dx, y: dy}
}
export function wheelEventPixels(e) {
let delta = wheelEventDelta(e)
delta.x *= wheelPixelsPerUnit
......@@ -95,8 +96,10 @@ export function onScrollWheel(cm, e) {
if (wheelSamples < 20) {
if (display.wheelStartX == null) {
display.wheelStartX = scroll.scrollLeft; display.wheelStartY = scroll.scrollTop
display.wheelDX = dx; display.wheelDY = dy
display.wheelStartX = scroll.scrollLeft;
display.wheelStartY = scroll.scrollTop
display.wheelDX = dx;
display.wheelDY = dy
setTimeout(() => {
if (display.wheelStartX == null) return
let movedX = scroll.scrollLeft - display.wheelStartX
......@@ -109,7 +112,8 @@ export function onScrollWheel(cm, e) {
++wheelSamples
}, 200)
} else {
display.wheelDX += dx; display.wheelDY += dy
display.wheelDX += dx;
display.wheelDY += dy
}
}
}
import { addClass, elt, rmClass } from "../util/dom.js"
import { on } from "../util/event.js"
import { scrollGap, paddingVert } from "../measurement/position_measurement.js"
import { ie, ie_version, mac, mac_geMountainLion } from "../util/browser.js"
import { updateHeightsInViewport } from "./update_lines.js"
import { Delayed } from "../util/misc.js"
import {addClass, elt, rmClass} from "../util/dom.js"
import {on} from "../util/event.js"
import {paddingVert, scrollGap} from "../measurement/position_measurement.js"
import {ie, ie_version, mac, mac_geMountainLion} from "../util/browser.js"
import {updateHeightsInViewport} from "./update_lines.js"
import {Delayed} from "../util/misc.js"
import { setScrollLeft, updateScrollTop } from "./scrolling.js"
import {setScrollLeft, updateScrollTop} from "./scrolling.js"
// SCROLLBARS
......@@ -33,7 +33,8 @@ class NativeScrollbars {
let vert = this.vert = elt("div", [elt("div", null, null, "min-width: 1px")], "CodeMirror-vscrollbar")
let horiz = this.horiz = elt("div", [elt("div", null, null, "height: 100%; min-height: 1px")], "CodeMirror-hscrollbar")
vert.tabIndex = horiz.tabIndex = -1
place(vert); place(horiz)
place(vert);
place(horiz)
on(vert, "scroll", () => {
if (vert.clientHeight) scroll(vert.scrollTop, "vertical")
......@@ -104,6 +105,7 @@ class NativeScrollbars {
enableZeroWidthBar(bar, delay, type) {
bar.style.pointerEvents = "auto"
function maybeDisable() {
// To find out whether the scrollbar is still visible, we
// check whether the element under the pixel in the bottom
......@@ -117,6 +119,7 @@ class NativeScrollbars {
if (elt != bar) bar.style.pointerEvents = "none"
else delay.set(1000, maybeDisable)
}
delay.set(1000, maybeDisable)
}
......@@ -128,10 +131,18 @@ class NativeScrollbars {
}
class NullScrollbars {
update() { return {bottom: 0, right: 0} }
setScrollLeft() {}
setScrollTop() {}
clear() {}
update() {
return {bottom: 0, right: 0}
}
setScrollLeft() {
}
setScrollTop() {
}
clear() {
}
}
export function updateScrollbars(cm, measure) {
......@@ -142,7 +153,8 @@ export function updateScrollbars(cm, measure) {
if (startWidth != cm.display.barWidth && cm.options.lineWrapping)
updateHeightsInViewport(cm)
updateScrollbarsInner(cm, measureForScrollbars(cm))
startWidth = cm.display.barWidth; startHeight = cm.display.barHeight
startWidth = cm.display.barWidth;
startHeight = cm.display.barHeight
}
}
......
import { Pos } from "../line/pos.js"
import { cursorCoords, displayHeight, displayWidth, estimateCoords, paddingTop, paddingVert, scrollGap, textHeight } from "../measurement/position_measurement.js"
import { gecko, phantom } from "../util/browser.js"
import { elt } from "../util/dom.js"
import { signalDOMEvent } from "../util/event.js"
import { startWorker } from "./highlight_worker.js"
import { alignHorizontally } from "./line_numbers.js"
import { updateDisplaySimple } from "./update_display.js"
import {Pos} from "../line/pos.js"
import {
cursorCoords,
displayHeight,
displayWidth,
estimateCoords,
paddingTop,
paddingVert,
scrollGap,
textHeight
} from "../measurement/position_measurement.js"
import {gecko, phantom} from "../util/browser.js"
import {elt} from "../util/dom.js"
import {signalDOMEvent} from "../util/event.js"
import {startWorker} from "./highlight_worker.js"
import {alignHorizontally} from "./line_numbers.js"
import {updateDisplaySimple} from "./update_display.js"
// SCROLLING THINGS INTO VIEW
......@@ -46,10 +55,12 @@ export function scrollPosIntoView(cm, pos, end, margin) {
let changed = false
let coords = cursorCoords(cm, pos)
let endCoords = !end || end == pos ? coords : cursorCoords(cm, end)
rect = {left: Math.min(coords.left, endCoords.left),
rect = {
left: Math.min(coords.left, endCoords.left),
top: Math.min(coords.top, endCoords.top) - margin,
right: Math.max(coords.left, endCoords.left),
bottom: Math.max(coords.bottom, endCoords.bottom) + margin}
bottom: Math.max(coords.bottom, endCoords.bottom) + margin
}
let scrollPos = calculateScrollPos(cm, rect)
let startTop = cm.doc.scrollTop, startLeft = cm.doc.scrollLeft
if (scrollPos.scrollTop != null) {
......
import { Pos } from "../line/pos.js"
import { visualLine } from "../line/spans.js"
import { getLine } from "../line/utils_line.js"
import { charCoords, cursorCoords, displayWidth, paddingH, wrappedLineExtentChar } from "../measurement/position_measurement.js"
import { getOrder, iterateBidiSections } from "../util/bidi.js"
import { elt } from "../util/dom.js"
import { onBlur } from "./focus.js"
import {Pos} from "../line/pos.js"
import {visualLine} from "../line/spans.js"
import {getLine} from "../line/utils_line.js"
import {
charCoords,
cursorCoords,
displayWidth,
paddingH,
wrappedLineExtentChar
} from "../measurement/position_measurement.js"
import {getOrder, iterateBidiSections} from "../util/bidi.js"
import {elt} from "../util/dom.js"
import {onBlur} from "./focus.js"
export function updateSelection(cm) {
cm.display.input.showSelection(cm.display.input.prepareSelection())
......@@ -47,7 +53,9 @@ export function drawSelectionCursor(cm, head, output) {
}
}
function cmpCoords(a, b) { return a.top - b.top || a.left - b.left }
function cmpCoords(a, b) {
return a.top - b.top || a.left - b.left
}
// Draws the given range as a highlighted selection
function drawSelectionRange(cm, range, output) {
......@@ -70,6 +78,7 @@ function drawSelectionRange(cm, range, output) {
let lineObj = getLine(doc, line)
let lineLen = lineObj.text.length
let start, end
function coords(ch, bias) {
return charCoords(cm, Pos(line, ch), "div", lineObj, bias)
}
......
import { sawCollapsedSpans } from "../line/saw_special_spans.js"
import { heightAtLine, visualLineEndNo, visualLineNo } from "../line/spans.js"
import { getLine, lineNumberFor } from "../line/utils_line.js"
import { displayHeight, displayWidth, getDimensions, paddingVert, scrollGap } from "../measurement/position_measurement.js"
import { mac, webkit } from "../util/browser.js"
import { activeElt, removeChildren, contains } from "../util/dom.js"
import { hasHandler, signal } from "../util/event.js"
import { indexOf } from "../util/misc.js"
import {sawCollapsedSpans} from "../line/saw_special_spans.js"
import {heightAtLine, visualLineEndNo, visualLineNo} from "../line/spans.js"
import {getLine, lineNumberFor} from "../line/utils_line.js"
import {
displayHeight,
displayWidth,
getDimensions,
paddingVert,
scrollGap
} from "../measurement/position_measurement.js"
import {mac, webkit} from "../util/browser.js"
import {activeElt, contains, removeChildren} from "../util/dom.js"
import {hasHandler, signal} from "../util/event.js"
import {indexOf} from "../util/misc.js"
import { buildLineElement, updateLineForChanges } from "./update_line.js"
import { startWorker } from "./highlight_worker.js"
import { maybeUpdateLineNumberWidth } from "./line_numbers.js"
import { measureForScrollbars, updateScrollbars } from "./scrollbars.js"
import { updateSelection } from "./selection.js"
import { updateHeightsInViewport, visibleLines } from "./update_lines.js"
import { adjustView, countDirtyView, resetView } from "./view_tracking.js"
import {buildLineElement, updateLineForChanges} from "./update_line.js"
import {startWorker} from "./highlight_worker.js"
import {maybeUpdateLineNumberWidth} from "./line_numbers.js"
import {measureForScrollbars, updateScrollbars} from "./scrollbars.js"
import {updateSelection} from "./selection.js"
import {updateHeightsInViewport, visibleLines} from "./update_lines.js"
import {adjustView, countDirtyView, resetView} from "./view_tracking.js"
// DISPLAY DRAWING
......@@ -37,6 +43,7 @@ export class DisplayUpdate {
if (hasHandler(emitter, type))
this.events.push(arguments)
}
finish() {
for (let i = 0; i < this.events.length; i++)
signal.apply(null, this.events[i])
......@@ -163,7 +170,7 @@ export function updateDisplayIfNeeded(cm, update) {
export function postUpdateDisplay(cm, update) {
let viewport = update.viewport
for (let first = true;; first = false) {
for (let first = true; ; first = false) {
if (!first || !cm.options.lineWrapping || update.oldDisplayWidth == displayWidth(cm)) {
// Clip forced viewport to actual scrollable area.
if (viewport && viewport.top != null)
......@@ -188,7 +195,8 @@ export function postUpdateDisplay(cm, update) {
update.signal(cm, "update", cm)
if (cm.display.viewFrom != cm.display.reportedViewFrom || cm.display.viewTo != cm.display.reportedViewTo) {
update.signal(cm, "viewportChange", cm, cm.display.viewFrom, cm.display.viewTo)
cm.display.reportedViewFrom = cm.display.viewFrom; cm.display.reportedViewTo = cm.display.viewTo
cm.display.reportedViewFrom = cm.display.viewFrom;
cm.display.reportedViewTo = cm.display.viewTo
}
}
......
import { buildLineContent } from "../line/line_data.js"
import { lineNumberFor } from "../line/utils_line.js"
import { ie, ie_version } from "../util/browser.js"
import { elt, classTest } from "../util/dom.js"
import { signalLater } from "../util/operation_group.js"
import {buildLineContent} from "../line/line_data.js"
import {lineNumberFor} from "../line/utils_line.js"
import {ie, ie_version} from "../util/browser.js"
import {classTest, elt} from "../util/dom.js"
import {signalLater} from "../util/operation_group.js"
// When an aspect of a line changes, a string is added to
// lineView.changes. This updates the relevant part of the line's
......@@ -36,7 +36,10 @@ function updateLineBackground(cm, lineView) {
if (cls) cls += " CodeMirror-linebackground"
if (lineView.background) {
if (cls) lineView.background.className = cls
else { lineView.background.parentNode.removeChild(lineView.background); lineView.background = null }
else {
lineView.background.parentNode.removeChild(lineView.background);
lineView.background = null
}
} else if (cls) {
let wrap = ensureLineWrapped(lineView)
lineView.background = wrap.insertBefore(elt("div", null, cls), wrap.firstChild)
......@@ -157,7 +160,8 @@ function insertLineWidgetsFor(cm, line, lineView, dims, allowAbove) {
if (!line.widgets) return
let wrap = ensureLineWrapped(lineView)
for (let i = 0, ws = line.widgets; i < ws.length; ++i) {
let widget = ws[i], node = elt("div", [widget.node], "CodeMirror-linewidget" + (widget.className ? " " + widget.className : ""))
let widget = ws[i],
node = elt("div", [widget.node], "CodeMirror-linewidget" + (widget.className ? " " + widget.className : ""))
if (!widget.handleMouseEvents) node.setAttribute("cm-ignore-events", "true")
positionLineWidget(widget, node, lineView, dims)
cm.display.input.setUneditable(node)
......
import { heightAtLine } from "../line/spans.js"
import { getLine, lineAtHeight, updateLineHeight } from "../line/utils_line.js"
import { paddingTop, charWidth } from "../measurement/position_measurement.js"
import { ie, ie_version } from "../util/browser.js"
import {heightAtLine} from "../line/spans.js"
import {getLine, lineAtHeight, updateLineHeight} from "../line/utils_line.js"
import {charWidth, paddingTop} from "../measurement/position_measurement.js"
import {ie, ie_version} from "../util/browser.js"
// Read the actual heights of the rendered lines, and update their
// stored heights to match.
......
import { buildViewArray } from "../line/line_data.js"
import { sawCollapsedSpans } from "../line/saw_special_spans.js"
import { visualLineEndNo, visualLineNo } from "../line/spans.js"
import { findViewIndex } from "../measurement/position_measurement.js"
import { indexOf } from "../util/misc.js"
import {buildViewArray} from "../line/line_data.js"
import {sawCollapsedSpans} from "../line/saw_special_spans.js"
import {visualLineEndNo, visualLineNo} from "../line/spans.js"
import {findViewIndex} from "../measurement/position_measurement.js"
import {indexOf} from "../util/misc.js"
// Updates the display.view data structure for a given change to the
// document. From and to are in pre-change coordinates. Lendiff is
......@@ -110,7 +110,8 @@ function viewCuttingPoint(cm, oldN, newN, dir) {
} else {
diff = n - oldN
}
oldN += diff; newN += diff
oldN += diff;
newN += diff
}
while (visualLineNo(cm.doc, newN) != newN) {
if (index == (dir < 0 ? 0 : view.length - 1)) return null
......
import { Display } from "../display/Display.js"
import { onFocus, onBlur } from "../display/focus.js"
import { maybeUpdateLineNumberWidth } from "../display/line_numbers.js"
import { endOperation, operation, startOperation } from "../display/operations.js"
import { initScrollbars } from "../display/scrollbars.js"
import { onScrollWheel } from "../display/scroll_events.js"
import { setScrollLeft, updateScrollTop } from "../display/scrolling.js"
import { clipPos, Pos } from "../line/pos.js"
import { posFromMouse } from "../measurement/position_measurement.js"
import { eventInWidget } from "../measurement/widgets.js"
import {Display} from "../display/Display.js"
import {onBlur, onFocus} from "../display/focus.js"
import {maybeUpdateLineNumberWidth} from "../display/line_numbers.js"
import {endOperation, operation, startOperation} from "../display/operations.js"
import {initScrollbars} from "../display/scrollbars.js"
import {onScrollWheel} from "../display/scroll_events.js"
import {setScrollLeft, updateScrollTop} from "../display/scrolling.js"
import {clipPos, Pos} from "../line/pos.js"
import {posFromMouse} from "../measurement/position_measurement.js"
import {eventInWidget} from "../measurement/widgets.js"
import Doc from "../model/Doc.js"
import { attachDoc } from "../model/document_data.js"
import { Range } from "../model/selection.js"
import { extendSelection } from "../model/selection_updates.js"
import { ie, ie_version, mobile, webkit } from "../util/browser.js"
import { e_preventDefault, e_stop, on, signal, signalDOMEvent } from "../util/event.js"
import { copyObj, Delayed } from "../util/misc.js"
import { clearDragCursor, onDragOver, onDragStart, onDrop } from "./drop_events.js"
import { ensureGlobalHandlers } from "./global_events.js"
import { onKeyDown, onKeyPress, onKeyUp } from "./key_events.js"
import { clickInGutter, onContextMenu, onMouseDown } from "./mouse_events.js"
import { themeChanged } from "./utils.js"
import { defaults, optionHandlers, Init } from "./options.js"
import {attachDoc} from "../model/document_data.js"
import {Range} from "../model/selection.js"
import {extendSelection} from "../model/selection_updates.js"
import {ie, ie_version, mobile, webkit} from "../util/browser.js"
import {e_preventDefault, e_stop, on, signal, signalDOMEvent} from "../util/event.js"
import {copyObj, Delayed} from "../util/misc.js"
import {clearDragCursor, onDragOver, onDragStart, onDrop} from "./drop_events.js"
import {ensureGlobalHandlers} from "./global_events.js"
import {onKeyDown, onKeyPress, onKeyUp} from "./key_events.js"
import {clickInGutter, onContextMenu, onMouseDown} from "./mouse_events.js"
import {themeChanged} from "./utils.js"
import {defaults, Init, optionHandlers} from "./options.js"
// A CodeMirror instance represents an editor. This is the object
// that user code is usually dealing with.
......@@ -128,6 +128,7 @@ function registerEventHandlers(cm) {
// Used to suppress mouse event handling when a touch happens
let touchFinished, prevTouch = {end: 0}
function finishTouch() {
if (d.activeTouch) {
touchFinished = setTimeout(() => d.activeTouch = null, 1000)
......@@ -135,23 +136,28 @@ function registerEventHandlers(cm) {
prevTouch.end = +new Date
}
}
function isMouseLikeTouchEvent(e) {
if (e.touches.length != 1) return false
let touch = e.touches[0]
return touch.radiusX <= 1 && touch.radiusY <= 1
}
function farAway(touch, other) {
if (other.left == null) return true
let dx = other.left - touch.left, dy = other.top - touch.top
return dx * dx + dy * dy > 20 * 20
}
on(d.scroller, "touchstart", e => {
if (!signalDOMEvent(cm, e) && !isMouseLikeTouchEvent(e) && !clickInGutter(cm, e)) {
d.input.ensurePolled()
clearTimeout(touchFinished)
let now = +new Date
d.activeTouch = {start: now, moved: false,
prev: now - prevTouch.end <= 300 ? prevTouch : null}
d.activeTouch = {
start: now, moved: false,
prev: now - prevTouch.end <= 300 ? prevTouch : null
}
if (e.touches.length == 1) {
d.activeTouch.left = e.touches[0].pageX
d.activeTouch.top = e.touches[0].pageY
......@@ -198,11 +204,22 @@ function registerEventHandlers(cm) {
on(d.wrapper, "scroll", () => d.wrapper.scrollTop = d.wrapper.scrollLeft = 0)
d.dragFunctions = {
enter: e => {if (!signalDOMEvent(cm, e)) e_stop(e)},
over: e => {if (!signalDOMEvent(cm, e)) { onDragOver(cm, e); e_stop(e) }},
enter: e => {
if (!signalDOMEvent(cm, e)) e_stop(e)
},
over: e => {
if (!signalDOMEvent(cm, e)) {
onDragOver(cm, e);
e_stop(e)
}
},
start: e => onDragStart(cm, e),
drop: operation(cm, onDrop),
leave: e => {if (!signalDOMEvent(cm, e)) { clearDragCursor(cm) }}
leave: e => {
if (!signalDOMEvent(cm, e)) {
clearDragCursor(cm)
}
}
}
let inp = d.input.getField()
......
import { deleteNearSelection } from "./deleteNearSelection.js"
import { runInOp } from "../display/operations.js"
import { ensureCursorVisible } from "../display/scrolling.js"
import { endOfLine } from "../input/movement.js"
import { clipPos, Pos } from "../line/pos.js"
import { visualLine, visualLineEnd } from "../line/spans.js"
import { getLine, lineNo } from "../line/utils_line.js"
import { Range } from "../model/selection.js"
import { selectAll } from "../model/selection_updates.js"
import { countColumn, sel_dontScroll, sel_move, spaceStr } from "../util/misc.js"
import { getOrder } from "../util/bidi.js"
import {deleteNearSelection} from "./deleteNearSelection.js"
import {runInOp} from "../display/operations.js"
import {ensureCursorVisible} from "../display/scrolling.js"
import {endOfLine} from "../input/movement.js"
import {clipPos, Pos} from "../line/pos.js"
import {visualLine, visualLineEnd} from "../line/spans.js"
import {getLine, lineNo} from "../line/utils_line.js"
import {Range} from "../model/selection.js"
import {selectAll} from "../model/selection_updates.js"
import {countColumn, sel_dontScroll, sel_move, spaceStr} from "../util/misc.js"
import {getOrder} from "../util/bidi.js"
// Commands are parameter-less actions that can be performed on an
// editor, mostly used for keybindings.
......@@ -41,7 +41,7 @@ export let commands = {
delWrappedLineRight: cm => deleteNearSelection(cm, range => {
let top = cm.charCoords(range.head, "div").top + 5
let rightPos = cm.coordsChar({left: cm.display.lineDiv.offsetWidth + 100, top: top}, "div")
return {from: range.from(), to: rightPos }
return {from: range.from(), to: rightPos}
}),
undo: cm => cm.undo(),
redo: cm => cm.redo(),
......@@ -159,12 +159,14 @@ function lineStart(cm, lineN) {
if (visual != line) lineN = lineNo(visual)
return endOfLine(true, cm, visual, lineN, 1)
}
function lineEnd(cm, lineN) {
let line = getLine(cm.doc, lineN)
let visual = visualLineEnd(line)
if (visual != line) lineN = lineNo(visual)
return endOfLine(true, cm, line, lineN, -1)
}
function lineStartSmart(cm, pos) {
let start = lineStart(cm, pos.line)
let line = getLine(cm.doc, start.line)
......
import { runInOp } from "../display/operations.js"
import { ensureCursorVisible } from "../display/scrolling.js"
import { cmp } from "../line/pos.js"
import { replaceRange } from "../model/changes.js"
import { lst } from "../util/misc.js"
import {runInOp} from "../display/operations.js"
import {ensureCursorVisible} from "../display/scrolling.js"
import {cmp} from "../line/pos.js"
import {replaceRange} from "../model/changes.js"
import {lst} from "../util/misc.js"
// Helper for deleting text near the selection(s), used to implement
// backspace, delete, and similar functionality.
......
import { drawSelectionCursor } from "../display/selection.js"
import { operation } from "../display/operations.js"
import { clipPos } from "../line/pos.js"
import { posFromMouse } from "../measurement/position_measurement.js"
import { eventInWidget } from "../measurement/widgets.js"
import { makeChange, replaceRange } from "../model/changes.js"
import { changeEnd } from "../model/change_measurement.js"
import { simpleSelection } from "../model/selection.js"
import { setSelectionNoUndo, setSelectionReplaceHistory } from "../model/selection_updates.js"
import { ie, presto, safari } from "../util/browser.js"
import { elt, removeChildrenAndAdd } from "../util/dom.js"
import { e_preventDefault, e_stop, signalDOMEvent } from "../util/event.js"
import { indexOf } from "../util/misc.js"
import {drawSelectionCursor} from "../display/selection.js"
import {operation} from "../display/operations.js"
import {clipPos} from "../line/pos.js"
import {posFromMouse} from "../measurement/position_measurement.js"
import {eventInWidget} from "../measurement/widgets.js"
import {makeChange, replaceRange} from "../model/changes.js"
import {changeEnd} from "../model/change_measurement.js"
import {simpleSelection} from "../model/selection.js"
import {setSelectionNoUndo, setSelectionReplaceHistory} from "../model/selection_updates.js"
import {ie, presto, safari} from "../util/browser.js"
import {elt, removeChildrenAndAdd} from "../util/dom.js"
import {e_preventDefault, e_stop, signalDOMEvent} from "../util/event.js"
import {indexOf} from "../util/misc.js"
// Kludge to work around strange IE behavior where it'll sometimes
// re-fire a series of drag-related events right after the drop (#1551)
......@@ -33,10 +33,12 @@ export function onDrop(e) {
if (++read == n) {
operation(cm, () => {
pos = clipPos(cm.doc, pos)
let change = {from: pos, to: pos,
let change = {
from: pos, to: pos,
text: cm.doc.splitLines(
text.filter(t => t != null).join(cm.doc.lineSeparator())),
origin: "paste"}
origin: "paste"
}
makeChange(cm.doc, change)
setSelectionReplaceHistory(cm.doc, simpleSelection(clipPos(cm.doc, pos), clipPos(cm.doc, changeEnd(change))))
})()
......@@ -82,13 +84,16 @@ export function onDrop(e) {
cm.replaceSelection(text, "around", "paste")
cm.display.input.focus()
}
} catch (e) {
}
catch(e){}
}
}
export function onDragStart(cm, e) {
if (ie && (!cm.state.draggingText || +new Date - lastDrop < 100)) { e_stop(e); return }
if (ie && (!cm.state.draggingText || +new Date - lastDrop < 100)) {
e_stop(e);
return
}
if (signalDOMEvent(cm, e) || eventInWidget(cm.display, e)) return
e.dataTransfer.setData("Text", cm.getSelection())
......
import { CodeMirror } from "./CodeMirror.js"
import { activeElt } from "../util/dom.js"
import { off, on } from "../util/event.js"
import { copyObj } from "../util/misc.js"
import {CodeMirror} from "./CodeMirror.js"
import {activeElt} from "../util/dom.js"
import {off, on} from "../util/event.js"
import {copyObj} from "../util/misc.js"
export function fromTextArea(textarea, options) {
options = options ? copyObj(options) : {}
......@@ -18,7 +18,9 @@ export function fromTextArea(textarea, options) {
textarea.getAttribute("autofocus") != null && hasFocus == document.body
}
function save() {textarea.value = cm.getValue()}
function save() {
textarea.value = cm.getValue()
}
let realSubmit
if (textarea.form) {
......@@ -34,7 +36,8 @@ export function fromTextArea(textarea, options) {
form.submit()
form.submit = wrappedSubmit
}
} catch(e) {}
} catch (e) {
}
}
}
......
import { onBlur } from "../display/focus.js"
import { on } from "../util/event.js"
import {onBlur} from "../display/focus.js"
import {on} from "../util/event.js"
// These must be handled carefully, because naively registering a
// handler for each editor will cause the editors to never be
......@@ -18,11 +18,13 @@ function forEachCodeMirror(f) {
}
let globalsRegistered = false
export function ensureGlobalHandlers() {
if (globalsRegistered) return
registerGlobalHandlers()
globalsRegistered = true
}
function registerGlobalHandlers() {
// When the window resizes, we need to refresh active editors.
let resizeTimer
......@@ -35,6 +37,7 @@ function registerGlobalHandlers() {
// When the window loses focus, we want to show the editor as blurred
on(window, "blur", () => forEachCodeMirror(onBlur))
}
// Called when the window resizes
function onResize(cm) {
let d = cm.display
......
import { signalLater } from "../util/operation_group.js"
import { restartBlink } from "../display/selection.js"
import { isModifierKey, keyName, lookupKey } from "../input/keymap.js"
import { eventInWidget } from "../measurement/widgets.js"
import { ie, ie_version, mac, presto, gecko } from "../util/browser.js"
import { activeElt, addClass, rmClass } from "../util/dom.js"
import { e_preventDefault, off, on, signalDOMEvent } from "../util/event.js"
import { hasCopyEvent } from "../util/feature_detection.js"
import { Delayed, Pass } from "../util/misc.js"
import { commands } from "./commands.js"
import {signalLater} from "../util/operation_group.js"
import {restartBlink} from "../display/selection.js"
import {isModifierKey, keyName, lookupKey} from "../input/keymap.js"
import {eventInWidget} from "../measurement/widgets.js"
import {gecko, ie, ie_version, mac, presto} from "../util/browser.js"
import {activeElt, addClass, rmClass} from "../util/dom.js"
import {e_preventDefault, off, on, signalDOMEvent} from "../util/event.js"
import {hasCopyEvent} from "../util/feature_detection.js"
import {Delayed, Pass} from "../util/misc.js"
import {commands} from "./commands.js"
// Run a handler that was bound to a key.
function doHandleBinding(cm, bound, dropShift) {
......@@ -104,6 +104,7 @@ function handleCharBinding(cm, e, ch) {
}
let lastStoppedKey = null
export function onKeyDown(e) {
let cm = this
if (e.target && e.target != cm.display.input.getField()) return
......@@ -139,6 +140,7 @@ function showCrossHair(cm) {
off(document, "mouseover", up)
}
}
on(document, "keyup", up)
on(document, "mouseover", up)
}
......@@ -153,7 +155,11 @@ export function onKeyPress(e) {
if (e.target && e.target != cm.display.input.getField()) return
if (eventInWidget(cm.display, e) || signalDOMEvent(cm, e) || e.ctrlKey && !e.altKey || mac && e.metaKey) return
let keyCode = e.keyCode, charCode = e.charCode
if (presto && keyCode == lastStoppedKey) {lastStoppedKey = null; e_preventDefault(e); return}
if (presto && keyCode == lastStoppedKey) {
lastStoppedKey = null;
e_preventDefault(e);
return
}
if ((presto && (!e.which || e.which < 10)) && handleKeyBinding(cm, e)) return
let ch = String.fromCharCode(charCode == null ? keyCode : charCode)
// Some browsers fire keypress events for backspace
......
import { scrollbarModel } from "../display/scrollbars.js"
import { wheelEventPixels } from "../display/scroll_events.js"
import { keyMap, keyName, isModifierKey, lookupKey, normalizeKeyMap } from "../input/keymap.js"
import { keyNames } from "../input/keynames.js"
import { Line } from "../line/line_data.js"
import { cmp, Pos } from "../line/pos.js"
import { changeEnd } from "../model/change_measurement.js"
import {scrollbarModel} from "../display/scrollbars.js"
import {wheelEventPixels} from "../display/scroll_events.js"
import {isModifierKey, keyMap, keyName, lookupKey, normalizeKeyMap} from "../input/keymap.js"
import {keyNames} from "../input/keynames.js"
import {Line} from "../line/line_data.js"
import {cmp, Pos} from "../line/pos.js"
import {changeEnd} from "../model/change_measurement.js"
import Doc from "../model/Doc.js"
import { LineWidget } from "../model/line_widget.js"
import { SharedTextMarker, TextMarker } from "../model/mark_text.js"
import { copyState, extendMode, getMode, innerMode, mimeModes, modeExtensions, modes, resolveMode, startState } from "../modes.js"
import { addClass, contains, rmClass } from "../util/dom.js"
import { e_preventDefault, e_stop, e_stopPropagation, off, on, signal } from "../util/event.js"
import { splitLinesAuto } from "../util/feature_detection.js"
import { countColumn, findColumn, isWordCharBasic, Pass } from "../util/misc.js"
import {LineWidget} from "../model/line_widget.js"
import {SharedTextMarker, TextMarker} from "../model/mark_text.js"
import {
copyState,
extendMode,
getMode,
innerMode,
mimeModes,
modeExtensions,
modes,
resolveMode,
startState
} from "../modes.js"
import {addClass, contains, rmClass} from "../util/dom.js"
import {e_preventDefault, e_stop, e_stopPropagation, off, on, signal} from "../util/event.js"
import {splitLinesAuto} from "../util/feature_detection.js"
import {countColumn, findColumn, isWordCharBasic, Pass} from "../util/misc.js"
import StringStream from "../util/StringStream.js"
import { commands } from "./commands.js"
import {commands} from "./commands.js"
export function addLegacyProps(CodeMirror) {
CodeMirror.off = off
......
// EDITOR CONSTRUCTOR
import { CodeMirror } from "./CodeMirror.js"
export { CodeMirror } from "./CodeMirror.js"
import {CodeMirror} from "./CodeMirror.js"
import {eventMixin} from "../util/event.js"
import {indexOf} from "../util/misc.js"
import { eventMixin } from "../util/event.js"
import { indexOf } from "../util/misc.js"
import {defineOptions} from "./options.js"
import addEditorMethods from "./methods.js"
import Doc from "../model/Doc.js"
import ContentEditableInput from "../input/ContentEditableInput.js"
import TextareaInput from "../input/TextareaInput.js"
import {defineMIME, defineMode} from "../modes.js"
import {fromTextArea} from "./fromTextArea.js"
import {addLegacyProps} from "./legacy.js"
import { defineOptions } from "./options.js"
export {CodeMirror} from "./CodeMirror.js"
defineOptions(CodeMirror)
import addEditorMethods from "./methods.js"
addEditorMethods(CodeMirror)
import Doc from "../model/Doc.js"
// Set up methods on CodeMirror's prototype to redirect to the editor's document.
let dontDelegate = "iter insert remove copy getEditor constructor".split(" ")
for (let prop in Doc.prototype) if (Doc.prototype.hasOwnProperty(prop) && indexOf(dontDelegate, prop) < 0)
CodeMirror.prototype[prop] = (function(method) {
return function() {return method.apply(this.doc, arguments)}
CodeMirror.prototype[prop] = (function (method) {
return function () {
return method.apply(this.doc, arguments)
}
})(Doc.prototype[prop])
eventMixin(Doc)
// INPUT HANDLING
import ContentEditableInput from "../input/ContentEditableInput.js"
import TextareaInput from "../input/TextareaInput.js"
CodeMirror.inputStyles = {"textarea": TextareaInput, "contenteditable": ContentEditableInput}
// MODE DEFINITION AND QUERYING
import { defineMIME, defineMode } from "../modes.js"
// Extra arguments are stored as the mode's dependencies, which is
// used by (legacy) mechanisms like loadmode.js to automatically
// load a mode. (Preferred mechanism is the require/define calls.)
CodeMirror.defineMode = function(name/*, mode, …*/) {
CodeMirror.defineMode = function (name/*, mode, …*/) {
if (!CodeMirror.defaults.mode && name != "null") CodeMirror.defaults.mode = name
defineMode.apply(this, arguments)
}
......@@ -58,12 +59,8 @@ CodeMirror.defineDocExtension = (name, func) => {
Doc.prototype[name] = func
}
import { fromTextArea } from "./fromTextArea.js"
CodeMirror.fromTextArea = fromTextArea
import { addLegacyProps } from "./legacy.js"
addLegacyProps(CodeMirror)
CodeMirror.version = "5.58.1"
import { deleteNearSelection } from "./deleteNearSelection.js"
import { commands } from "./commands.js"
import { attachDoc } from "../model/document_data.js"
import { activeElt, addClass, rmClass } from "../util/dom.js"
import { eventMixin, signal } from "../util/event.js"
import { getLineStyles, getContextBefore, takeToken } from "../line/highlight.js"
import { indentLine } from "../input/indent.js"
import { triggerElectric } from "../input/input.js"
import { onKeyDown, onKeyPress, onKeyUp } from "./key_events.js"
import { onMouseDown } from "./mouse_events.js"
import { getKeyMap } from "../input/keymap.js"
import { endOfLine, moveLogically, moveVisually } from "../input/movement.js"
import { endOperation, methodOp, operation, runInOp, startOperation } from "../display/operations.js"
import { clipLine, clipPos, equalCursorPos, Pos } from "../line/pos.js"
import { charCoords, charWidth, clearCaches, clearLineMeasurementCache, coordsChar, cursorCoords, displayHeight, displayWidth, estimateLineHeights, fromCoordSystem, intoCoordSystem, scrollGap, textHeight } from "../measurement/position_measurement.js"
import { Range } from "../model/selection.js"
import { replaceOneSelection, skipAtomic } from "../model/selection_updates.js"
import { addToScrollTop, ensureCursorVisible, scrollIntoView, scrollToCoords, scrollToCoordsRange, scrollToRange } from "../display/scrolling.js"
import { heightAtLine } from "../line/spans.js"
import { updateGutterSpace } from "../display/update_display.js"
import { indexOf, insertSorted, isWordChar, sel_dontScroll, sel_move } from "../util/misc.js"
import { signalLater } from "../util/operation_group.js"
import { getLine, isLine, lineAtHeight } from "../line/utils_line.js"
import { regChange, regLineChange } from "../display/view_tracking.js"
import {deleteNearSelection} from "./deleteNearSelection.js"
import {commands} from "./commands.js"
import {attachDoc} from "../model/document_data.js"
import {activeElt, addClass, rmClass} from "../util/dom.js"
import {eventMixin, signal} from "../util/event.js"
import {getContextBefore, getLineStyles, takeToken} from "../line/highlight.js"
import {indentLine} from "../input/indent.js"
import {triggerElectric} from "../input/input.js"
import {onKeyDown, onKeyPress, onKeyUp} from "./key_events.js"
import {onMouseDown} from "./mouse_events.js"
import {getKeyMap} from "../input/keymap.js"
import {endOfLine, moveLogically, moveVisually} from "../input/movement.js"
import {endOperation, methodOp, operation, runInOp, startOperation} from "../display/operations.js"
import {clipLine, clipPos, equalCursorPos, Pos} from "../line/pos.js"
import {
charCoords,
charWidth,
clearCaches,
clearLineMeasurementCache,
coordsChar,
cursorCoords,
displayHeight,
displayWidth,
estimateLineHeights,
fromCoordSystem,
intoCoordSystem,
scrollGap,
textHeight
} from "../measurement/position_measurement.js"
import {Range} from "../model/selection.js"
import {replaceOneSelection, skipAtomic} from "../model/selection_updates.js"
import {
addToScrollTop,
ensureCursorVisible,
scrollIntoView,
scrollToCoords,
scrollToCoordsRange,
scrollToRange
} from "../display/scrolling.js"
import {heightAtLine} from "../line/spans.js"
import {updateGutterSpace} from "../display/update_display.js"
import {indexOf, insertSorted, isWordChar, sel_dontScroll, sel_move} from "../util/misc.js"
import {signalLater} from "../util/operation_group.js"
import {getLine, isLine, lineAtHeight} from "../line/utils_line.js"
import {regChange, regLineChange} from "../display/view_tracking.js"
// The publicly visible API. Note that methodOp(f) means
// 'wrap f in an operation, performed on its `this` parameter'.
......@@ -31,16 +52,19 @@ import { regChange, regLineChange } from "../display/view_tracking.js"
// CodeMirror.prototype, for backwards compatibility and
// convenience.
export default function(CodeMirror) {
export default function (CodeMirror) {
let optionHandlers = CodeMirror.optionHandlers
let helpers = CodeMirror.helpers = {}
CodeMirror.prototype = {
constructor: CodeMirror,
focus: function(){window.focus(); this.display.input.focus()},
focus: function () {
window.focus();
this.display.input.focus()
},
setOption: function(option, value) {
setOption: function (option, value) {
let options = this.options, old = options[option]
if (options[option] == value && option != "mode") return
options[option] = value
......@@ -49,13 +73,17 @@ export default function(CodeMirror) {
signal(this, "optionChange", this, option)
},
getOption: function(option) {return this.options[option]},
getDoc: function() {return this.doc},
getOption: function (option) {
return this.options[option]
},
getDoc: function () {
return this.doc
},
addKeyMap: function(map, bottom) {
addKeyMap: function (map, bottom) {
this.state.keyMaps[bottom ? "push" : "unshift"](getKeyMap(map))
},
removeKeyMap: function(map) {
removeKeyMap: function (map) {
let maps = this.state.keyMaps
for (let i = 0; i < maps.length; ++i)
if (maps[i] == map || maps[i].name == map) {
......@@ -64,17 +92,19 @@ export default function(CodeMirror) {
}
},
addOverlay: methodOp(function(spec, options) {
addOverlay: methodOp(function (spec, options) {
let mode = spec.token ? spec : CodeMirror.getMode(this.options, spec)
if (mode.startState) throw new Error("Overlays may not be stateful.")
insertSorted(this.state.overlays,
{mode: mode, modeSpec: spec, opaque: options && options.opaque,
priority: (options && options.priority) || 0},
{
mode: mode, modeSpec: spec, opaque: options && options.opaque,
priority: (options && options.priority) || 0
},
overlay => overlay.priority)
this.state.modeGen++
regChange(this)
}),
removeOverlay: methodOp(function(spec) {
removeOverlay: methodOp(function (spec) {
let overlays = this.state.overlays
for (let i = 0; i < overlays.length; ++i) {
let cur = overlays[i].modeSpec
......@@ -87,14 +117,14 @@ export default function(CodeMirror) {
}
}),
indentLine: methodOp(function(n, dir, aggressive) {
indentLine: methodOp(function (n, dir, aggressive) {
if (typeof dir != "string" && typeof dir != "number") {
if (dir == null) dir = this.options.smartIndent ? "smart" : "prev"
else dir = dir ? "add" : "subtract"
}
if (isLine(this.doc, n)) indentLine(this, n, dir, aggressive)
}),
indentSelection: methodOp(function(how) {
indentSelection: methodOp(function (how) {
let ranges = this.doc.sel.ranges, end = -1
for (let i = 0; i < ranges.length; i++) {
let range = ranges[i]
......@@ -117,41 +147,44 @@ export default function(CodeMirror) {
// Fetch the parser token for a given character. Useful for hacks
// that want to inspect the mode state (say, for completion).
getTokenAt: function(pos, precise) {
getTokenAt: function (pos, precise) {
return takeToken(this, pos, precise)
},
getLineTokens: function(line, precise) {
getLineTokens: function (line, precise) {
return takeToken(this, Pos(line), precise, true)
},
getTokenTypeAt: function(pos) {
getTokenTypeAt: function (pos) {
pos = clipPos(this.doc, pos)
let styles = getLineStyles(this, getLine(this.doc, pos.line))
let before = 0, after = (styles.length - 1) / 2, ch = pos.ch
let type
if (ch == 0) type = styles[2]
else for (;;) {
else for (; ;) {
let mid = (before + after) >> 1
if ((mid ? styles[mid * 2 - 1] : 0) >= ch) after = mid
else if (styles[mid * 2 + 1] < ch) before = mid + 1
else { type = styles[mid * 2 + 2]; break }
else {
type = styles[mid * 2 + 2];
break
}
}
let cut = type ? type.indexOf("overlay ") : -1
return cut < 0 ? type : cut == 0 ? null : type.slice(0, cut - 1)
},
getModeAt: function(pos) {
getModeAt: function (pos) {
let mode = this.doc.mode
if (!mode.innerMode) return mode
return CodeMirror.innerMode(mode, this.getTokenAt(pos).state).mode
},
getHelper: function(pos, type) {
getHelper: function (pos, type) {
return this.getHelpers(pos, type)[0]
},
getHelpers: function(pos, type) {
getHelpers: function (pos, type) {
let found = []
if (!helpers.hasOwnProperty(type)) return found
let help = helpers[type], mode = this.getModeAt(pos)
......@@ -175,13 +208,13 @@ export default function(CodeMirror) {
return found
},
getStateAfter: function(line, precise) {
getStateAfter: function (line, precise) {
let doc = this.doc
line = clipLine(doc, line == null ? doc.first + doc.size - 1: line)
line = clipLine(doc, line == null ? doc.first + doc.size - 1 : line)
return getContextBefore(this, line + 1, precise).state
},
cursorCoords: function(start, mode) {
cursorCoords: function (start, mode) {
let pos, range = this.doc.sel.primary()
if (start == null) pos = range.head
else if (typeof start == "object") pos = clipPos(this.doc, start)
......@@ -189,25 +222,28 @@ export default function(CodeMirror) {
return cursorCoords(this, pos, mode || "page")
},
charCoords: function(pos, mode) {
charCoords: function (pos, mode) {
return charCoords(this, clipPos(this.doc, pos), mode || "page")
},
coordsChar: function(coords, mode) {
coordsChar: function (coords, mode) {
coords = fromCoordSystem(this, coords, mode || "page")
return coordsChar(this, coords.left, coords.top)
},
lineAtHeight: function(height, mode) {
lineAtHeight: function (height, mode) {
height = fromCoordSystem(this, {top: height, left: 0}, mode || "page").top
return lineAtHeight(this.doc, height + this.display.viewOffset)
},
heightAtLine: function(line, mode, includeWidgets) {
heightAtLine: function (line, mode, includeWidgets) {
let end = false, lineObj
if (typeof line == "number") {
let last = this.doc.first + this.doc.size - 1
if (line < this.doc.first) line = this.doc.first
else if (line > last) { line = last; end = true }
else if (line > last) {
line = last;
end = true
}
lineObj = getLine(this.doc, line)
} else {
lineObj = line
......@@ -216,12 +252,18 @@ export default function(CodeMirror) {
(end ? this.doc.height - heightAtLine(lineObj) : 0)
},
defaultTextHeight: function() { return textHeight(this.display) },
defaultCharWidth: function() { return charWidth(this.display) },
defaultTextHeight: function () {
return textHeight(this.display)
},
defaultCharWidth: function () {
return charWidth(this.display)
},
getViewport: function() { return {from: this.display.viewFrom, to: this.display.viewTo}},
getViewport: function () {
return {from: this.display.viewFrom, to: this.display.viewTo}
},
addWidget: function(pos, node, scroll, vert, horiz) {
addWidget: function (pos, node, scroll, vert, horiz) {
let display = this.display
pos = cursorCoords(this, clipPos(this.doc, pos))
let top = pos.bottom, left = pos.left
......@@ -261,16 +303,21 @@ export default function(CodeMirror) {
triggerOnKeyUp: onKeyUp,
triggerOnMouseDown: methodOp(onMouseDown),
execCommand: function(cmd) {
execCommand: function (cmd) {
if (commands.hasOwnProperty(cmd))
return commands[cmd].call(null, this)
},
triggerElectric: methodOp(function(text) { triggerElectric(this, text) }),
triggerElectric: methodOp(function (text) {
triggerElectric(this, text)
}),
findPosH: function(from, amount, unit, visually) {
findPosH: function (from, amount, unit, visually) {
let dir = 1
if (amount < 0) { dir = -1; amount = -amount }
if (amount < 0) {
dir = -1;
amount = -amount
}
let cur = clipPos(this.doc, from)
for (let i = 0; i < amount; ++i) {
cur = findPosH(this.doc, cur, dir, unit, visually)
......@@ -279,7 +326,7 @@ export default function(CodeMirror) {
return cur
},
moveH: methodOp(function(dir, unit) {
moveH: methodOp(function (dir, unit) {
this.extendSelectionsBy(range => {
if (this.display.shift || this.doc.extend || range.empty())
return findPosH(this.doc, range.head, dir, unit, this.options.rtlMoveVisually)
......@@ -288,7 +335,7 @@ export default function(CodeMirror) {
}, sel_move)
}),
deleteH: methodOp(function(dir, unit) {
deleteH: methodOp(function (dir, unit) {
let sel = this.doc.sel, doc = this.doc
if (sel.somethingSelected())
doc.replaceSelection("", null, "+delete")
......@@ -299,9 +346,12 @@ export default function(CodeMirror) {
})
}),
findPosV: function(from, amount, unit, goalColumn) {
findPosV: function (from, amount, unit, goalColumn) {
let dir = 1, x = goalColumn
if (amount < 0) { dir = -1; amount = -amount }
if (amount < 0) {
dir = -1;
amount = -amount
}
let cur = clipPos(this.doc, from)
for (let i = 0; i < amount; ++i) {
let coords = cursorCoords(this, cur, "div")
......@@ -313,7 +363,7 @@ export default function(CodeMirror) {
return cur
},
moveV: methodOp(function(dir, unit) {
moveV: methodOp(function (dir, unit) {
let doc = this.doc, goals = []
let collapse = !this.display.shift && !doc.extend && doc.sel.somethingSelected()
doc.extendSelectionsBy(range => {
......@@ -332,7 +382,7 @@ export default function(CodeMirror) {
}),
// Find the word at the given position (as returned by coordsChar).
findWordAt: function(pos) {
findWordAt: function (pos) {
let doc = this.doc, line = getLine(doc, pos.line).text
let start = pos.ch, end = pos.ch
if (line) {
......@@ -349,7 +399,7 @@ export default function(CodeMirror) {
return new Range(Pos(pos.line, start), Pos(pos.line, end))
},
toggleOverwrite: function(value) {
toggleOverwrite: function (value) {
if (value != null && value == this.state.overwrite) return
if (this.state.overwrite = !this.state.overwrite)
addClass(this.display.cursorDiv, "CodeMirror-overwrite")
......@@ -358,19 +408,27 @@ export default function(CodeMirror) {
signal(this, "overwriteToggle", this, this.state.overwrite)
},
hasFocus: function() { return this.display.input.getField() == activeElt() },
isReadOnly: function() { return !!(this.options.readOnly || this.doc.cantEdit) },
hasFocus: function () {
return this.display.input.getField() == activeElt()
},
isReadOnly: function () {
return !!(this.options.readOnly || this.doc.cantEdit)
},
scrollTo: methodOp(function (x, y) { scrollToCoords(this, x, y) }),
getScrollInfo: function() {
scrollTo: methodOp(function (x, y) {
scrollToCoords(this, x, y)
}),
getScrollInfo: function () {
let scroller = this.display.scroller
return {left: scroller.scrollLeft, top: scroller.scrollTop,
return {
left: scroller.scrollLeft, top: scroller.scrollTop,
height: scroller.scrollHeight - scrollGap(this) - this.display.barHeight,
width: scroller.scrollWidth - scrollGap(this) - this.display.barWidth,
clientHeight: displayHeight(this), clientWidth: displayWidth(this)}
clientHeight: displayHeight(this), clientWidth: displayWidth(this)
}
},
scrollIntoView: methodOp(function(range, margin) {
scrollIntoView: methodOp(function (range, margin) {
if (range == null) {
range = {from: this.doc.sel.primary().head, to: null}
if (margin == null) margin = this.options.cursorScrollMargin
......@@ -389,7 +447,7 @@ export default function(CodeMirror) {
}
}),
setSize: methodOp(function(width, height) {
setSize: methodOp(function (width, height) {
let interpret = val => typeof val == "number" || /^\d+$/.test(String(val)) ? val + "px" : val
if (width != null) this.display.wrapper.style.width = interpret(width)
if (height != null) this.display.wrapper.style.height = interpret(height)
......@@ -397,18 +455,27 @@ export default function(CodeMirror) {
let lineNo = this.display.viewFrom
this.doc.iter(lineNo, this.display.viewTo, line => {
if (line.widgets) for (let i = 0; i < line.widgets.length; i++)
if (line.widgets[i].noHScroll) { regLineChange(this, lineNo, "widget"); break }
if (line.widgets[i].noHScroll) {
regLineChange(this, lineNo, "widget");
break
}
++lineNo
})
this.curOp.forceUpdate = true
signal(this, "refresh", this)
}),
operation: function(f){return runInOp(this, f)},
startOperation: function(){return startOperation(this)},
endOperation: function(){return endOperation(this)},
operation: function (f) {
return runInOp(this, f)
},
startOperation: function () {
return startOperation(this)
},
endOperation: function () {
return endOperation(this)
},
refresh: methodOp(function() {
refresh: methodOp(function () {
let oldHeight = this.display.cachedTextHeight
regChange(this)
this.curOp.forceUpdate = true
......@@ -420,7 +487,7 @@ export default function(CodeMirror) {
signal(this, "refresh", this)
}),
swapDoc: methodOp(function(doc) {
swapDoc: methodOp(function (doc) {
let old = this.doc
old.cm = null
// Cancel the current text selection if any (#5821)
......@@ -434,23 +501,31 @@ export default function(CodeMirror) {
return old
}),
phrase: function(phraseText) {
phrase: function (phraseText) {
let phrases = this.options.phrases
return phrases && Object.prototype.hasOwnProperty.call(phrases, phraseText) ? phrases[phraseText] : phraseText
},
getInputField: function(){return this.display.input.getField()},
getWrapperElement: function(){return this.display.wrapper},
getScrollerElement: function(){return this.display.scroller},
getGutterElement: function(){return this.display.gutters}
getInputField: function () {
return this.display.input.getField()
},
getWrapperElement: function () {
return this.display.wrapper
},
getScrollerElement: function () {
return this.display.scroller
},
getGutterElement: function () {
return this.display.gutters
}
}
eventMixin(CodeMirror)
CodeMirror.registerHelper = function(type, name, value) {
CodeMirror.registerHelper = function (type, name, value) {
if (!helpers.hasOwnProperty(type)) helpers[type] = CodeMirror[type] = {_global: []}
helpers[type][name] = value
}
CodeMirror.registerGlobalHelper = function(type, name, predicate, value) {
CodeMirror.registerGlobalHelper = function (type, name, predicate, value) {
CodeMirror.registerHelper(type, name, value)
helpers[type]._global.push({pred: predicate, val: value})
}
......@@ -470,12 +545,14 @@ function findPosH(doc, pos, dir, unit, visually) {
let origDir = dir
let lineObj = getLine(doc, pos.line)
let lineDir = visually && doc.direction == "rtl" ? -dir : dir
function findNextLine() {
let l = pos.line + lineDir
if (l < doc.first || l >= doc.first + doc.size) return false
pos = new Pos(l, pos.ch, pos.sticky)
return lineObj = getLine(doc, l)
}
function moveOnce(boundToLine) {
let next
if (unit == "codepoint") {
......@@ -506,7 +583,7 @@ function findPosH(doc, pos, dir, unit, visually) {
} else if (unit == "word" || unit == "group") {
let sawType = null, group = unit == "group"
let helper = doc.cm && doc.cm.getHelper(pos, "wordChars")
for (let first = true;; first = false) {
for (let first = true; ; first = false) {
if (dir < 0 && !moveOnce(!first)) break
let cur = lineObj.text.charAt(pos.ch) || "\n"
let type = isWordChar(cur, helper) ? "w"
......@@ -515,7 +592,11 @@ function findPosH(doc, pos, dir, unit, visually) {
: "p"
if (group && !first && !type) type = "s"
if (sawType && sawType != type) {
if (dir < 0) {dir = 1; moveOnce(); pos.sticky = "after"}
if (dir < 0) {
dir = 1;
moveOnce();
pos.sticky = "after"
}
break
}
......@@ -542,10 +623,13 @@ function findPosV(cm, pos, dir, unit) {
y = dir > 0 ? pos.bottom + 3 : pos.top - 3
}
let target
for (;;) {
for (; ;) {
target = coordsChar(cm, x, y)
if (!target.outside) break
if (dir < 0 ? y <= 0 : y >= doc.height) { target.hitSide = true; break }
if (dir < 0 ? y <= 0 : y >= doc.height) {
target.hitSide = true;
break
}
y += dir * 5
}
return target
......
import { delayBlurEvent, ensureFocus } from "../display/focus.js"
import { operation } from "../display/operations.js"
import { visibleLines } from "../display/update_lines.js"
import { clipPos, cmp, maxPos, minPos, Pos } from "../line/pos.js"
import { getLine, lineAtHeight } from "../line/utils_line.js"
import { posFromMouse } from "../measurement/position_measurement.js"
import { eventInWidget } from "../measurement/widgets.js"
import { normalizeSelection, Range, Selection } from "../model/selection.js"
import { extendRange, extendSelection, replaceOneSelection, setSelection } from "../model/selection_updates.js"
import { captureRightClick, chromeOS, ie, ie_version, mac, webkit, safari } from "../util/browser.js"
import { getOrder, getBidiPartAt } from "../util/bidi.js"
import { activeElt } from "../util/dom.js"
import { e_button, e_defaultPrevented, e_preventDefault, e_target, hasHandler, off, on, signal, signalDOMEvent } from "../util/event.js"
import { dragAndDrop } from "../util/feature_detection.js"
import { bind, countColumn, findColumn, sel_mouse } from "../util/misc.js"
import { addModifierNames } from "../input/keymap.js"
import { Pass } from "../util/misc.js"
import { dispatchKey } from "./key_events.js"
import { commands } from "./commands.js"
import {delayBlurEvent, ensureFocus} from "../display/focus.js"
import {operation} from "../display/operations.js"
import {visibleLines} from "../display/update_lines.js"
import {clipPos, cmp, maxPos, minPos, Pos} from "../line/pos.js"
import {getLine, lineAtHeight} from "../line/utils_line.js"
import {posFromMouse} from "../measurement/position_measurement.js"
import {eventInWidget} from "../measurement/widgets.js"
import {normalizeSelection, Range, Selection} from "../model/selection.js"
import {extendRange, extendSelection, replaceOneSelection, setSelection} from "../model/selection_updates.js"
import {captureRightClick, chromeOS, ie, ie_version, mac, safari, webkit} from "../util/browser.js"
import {getBidiPartAt, getOrder} from "../util/bidi.js"
import {activeElt} from "../util/dom.js"
import {
e_button,
e_defaultPrevented,
e_preventDefault,
e_target,
hasHandler,
off,
on,
signal,
signalDOMEvent
} from "../util/event.js"
import {dragAndDrop} from "../util/feature_detection.js"
import {bind, countColumn, findColumn, Pass, sel_mouse} from "../util/misc.js"
import {addModifierNames} from "../input/keymap.js"
import {dispatchKey} from "./key_events.js"
import {commands} from "./commands.js"
const DOUBLECLICK_DELAY = 400
......@@ -35,6 +44,7 @@ class PastClick {
}
let lastClick, lastDoubleClick
function clickRepeat(pos, button) {
let now = +new Date
if (lastDoubleClick && lastDoubleClick.compare(now, pos, button)) {
......@@ -159,12 +169,15 @@ function leftButtonStartDrag(cm, event, pos, behavior) {
extendSelection(cm.doc, pos, null, null, behavior.extend)
// Work around unexplainable focus problem in IE9 (#2127) and Chrome (#3081)
if ((webkit && !safari) || ie && ie_version == 9)
setTimeout(() => {display.wrapper.ownerDocument.body.focus({preventScroll: true}); display.input.focus()}, 20)
setTimeout(() => {
display.wrapper.ownerDocument.body.focus({preventScroll: true});
display.input.focus()
}, 20)
else
display.input.focus()
}
})
let mouseMove = function(e2) {
let mouseMove = function (e2) {
moved = moved || Math.abs(event.clientX - e2.clientX) + Math.abs(event.clientY - e2.clientY) >= 10
}
let dragStart = () => moved = true
......@@ -237,6 +250,7 @@ function leftButtonSelect(cm, event, start, behavior) {
}
let lastPos = start
function extendTo(pos) {
if (cmp(lastPos, pos) == 0) return
lastPos = pos
......@@ -291,7 +305,9 @@ function leftButtonSelect(cm, event, start, behavior) {
extendTo(cur)
let visible = visibleLines(display, doc)
if (cur.line >= visible.to || cur.line < visible.from)
setTimeout(operation(cm, () => {if (counter == curCount) extend(e)}), 150)
setTimeout(operation(cm, () => {
if (counter == curCount) extend(e)
}), 150)
} else {
let outside = e.clientY < editorSize.top ? -20 : e.clientY > editorSize.bottom ? 20 : 0
if (outside) setTimeout(operation(cm, () => {
......@@ -368,8 +384,12 @@ function gutterEvent(cm, e, type, prevent) {
mX = e.touches[0].clientX
mY = e.touches[0].clientY
} else {
try { mX = e.clientX; mY = e.clientY }
catch(e) { return false }
try {
mX = e.clientX;
mY = e.clientY
} catch (e) {
return false
}
}
if (mX >= Math.floor(cm.display.gutters.getBoundingClientRect().right)) return false
if (prevent) e_preventDefault(e)
......
import { onBlur } from "../display/focus.js"
import { getGutters, updateGutters } from "../display/gutters.js"
import { loadMode, resetModeState } from "../display/mode_state.js"
import { initScrollbars, updateScrollbars } from "../display/scrollbars.js"
import { updateSelection } from "../display/selection.js"
import { regChange } from "../display/view_tracking.js"
import { getKeyMap } from "../input/keymap.js"
import { defaultSpecialCharPlaceholder } from "../line/line_data.js"
import { Pos } from "../line/pos.js"
import { findMaxLine } from "../line/spans.js"
import { clearCaches, compensateForHScroll, estimateLineHeights } from "../measurement/position_measurement.js"
import { replaceRange } from "../model/changes.js"
import { mobile, windows } from "../util/browser.js"
import { addClass, rmClass } from "../util/dom.js"
import { off, on } from "../util/event.js"
import { themeChanged } from "./utils.js"
export let Init = {toString: function(){return "CodeMirror.Init"}}
import {onBlur} from "../display/focus.js"
import {getGutters, updateGutters} from "../display/gutters.js"
import {loadMode, resetModeState} from "../display/mode_state.js"
import {initScrollbars, updateScrollbars} from "../display/scrollbars.js"
import {updateSelection} from "../display/selection.js"
import {regChange} from "../display/view_tracking.js"
import {getKeyMap} from "../input/keymap.js"
import {defaultSpecialCharPlaceholder} from "../line/line_data.js"
import {Pos} from "../line/pos.js"
import {findMaxLine} from "../line/spans.js"
import {clearCaches, compensateForHScroll, estimateLineHeights} from "../measurement/position_measurement.js"
import {replaceRange} from "../model/changes.js"
import {mobile, windows} from "../util/browser.js"
import {addClass, rmClass} from "../util/dom.js"
import {off, on} from "../util/event.js"
import {themeChanged} from "./utils.js"
export let Init = {
toString: function () {
return "CodeMirror.Init"
}
}
export let defaults = {}
export let optionHandlers = {}
......@@ -27,7 +31,9 @@ export function defineOptions(CodeMirror) {
function option(name, deflt, handle, notOnInit) {
CodeMirror.defaults[name] = deflt
if (handle) optionHandlers[name] =
notOnInit ? (cm, val, old) => {if (old != Init) handle(cm, val, old)} : handle
notOnInit ? (cm, val, old) => {
if (old != Init) handle(cm, val, old)
} : handle
}
CodeMirror.defineOption = option
......@@ -57,7 +63,7 @@ export function defineOptions(CodeMirror) {
if (!val) return
let newBreaks = [], lineNo = cm.doc.first
cm.doc.iter(line => {
for (let pos = 0;;) {
for (let pos = 0; ;) {
let found = line.text.indexOf(val, pos)
if (found == -1) break
pos = found + val.length
......@@ -138,7 +144,9 @@ export function defineOptions(CodeMirror) {
cm.display.input.screenReaderLabelChanged(val)
})
option("disableInput", false, (cm, val) => {if (!val) cm.display.input.reset()}, true)
option("disableInput", false, (cm, val) => {
if (!val) cm.display.input.reset()
}, true)
option("dragDrop", true, dragDropChanged)
option("allowDropFileTypes", null)
......
import { clearCaches } from "../measurement/position_measurement.js"
import {clearCaches} from "../measurement/position_measurement.js"
export function themeChanged(cm) {
cm.display.wrapper.className = cm.display.wrapper.className.replace(/\s*cm-s-\S+/g, "") +
......
import { operation, runInOp } from "../display/operations.js"
import { prepareSelection } from "../display/selection.js"
import { regChange } from "../display/view_tracking.js"
import { applyTextInput, copyableRanges, disableBrowserMagic, handlePaste, hiddenTextarea, lastCopied, setLastCopied } from "./input.js"
import { cmp, maxPos, minPos, Pos } from "../line/pos.js"
import { getBetween, getLine, lineNo } from "../line/utils_line.js"
import { findViewForLine, findViewIndex, mapFromLineView, nodeAndOffsetInLineMap } from "../measurement/position_measurement.js"
import { replaceRange } from "../model/changes.js"
import { simpleSelection } from "../model/selection.js"
import { setSelection } from "../model/selection_updates.js"
import { getBidiPartAt, getOrder } from "../util/bidi.js"
import { android, chrome, gecko, ie_version } from "../util/browser.js"
import { contains, range, removeChildrenAndAdd, selectInput } from "../util/dom.js"
import { on, signalDOMEvent } from "../util/event.js"
import { Delayed, lst, sel_dontScroll } from "../util/misc.js"
import {operation, runInOp} from "../display/operations.js"
import {prepareSelection} from "../display/selection.js"
import {regChange} from "../display/view_tracking.js"
import {
applyTextInput,
copyableRanges,
disableBrowserMagic,
handlePaste,
hiddenTextarea,
lastCopied,
setLastCopied
} from "./input.js"
import {cmp, maxPos, minPos, Pos} from "../line/pos.js"
import {getBetween, getLine, lineNo} from "../line/utils_line.js"
import {
findViewForLine,
findViewIndex,
mapFromLineView,
nodeAndOffsetInLineMap
} from "../measurement/position_measurement.js"
import {replaceRange} from "../model/changes.js"
import {simpleSelection} from "../model/selection.js"
import {setSelection} from "../model/selection_updates.js"
import {getBidiPartAt, getOrder} from "../util/bidi.js"
import {android, chrome, gecko, ie_version} from "../util/browser.js"
import {contains, range, removeChildrenAndAdd, selectInput} from "../util/dom.js"
import {on, signalDOMEvent} from "../util/event.js"
import {Delayed, lst, sel_dontScroll} from "../util/misc.js"
// CONTENTEDITABLE INPUT STYLE
......@@ -103,13 +116,14 @@ export default class ContentEditableInput {
if (hadFocus == div) input.showPrimarySelection()
}, 50)
}
on(div, "copy", onCopyCut)
on(div, "cut", onCopyCut)
}
screenReaderLabelChanged(label) {
// Label for screenreaders, accessibility
if(label) {
if (label) {
this.div.setAttribute('aria-label', label)
} else {
this.div.removeAttribute('aria-label')
......@@ -164,8 +178,10 @@ export default class ContentEditableInput {
}
let old = sel.rangeCount && sel.getRangeAt(0), rng
try { rng = range(start.node, start.offset, end.offset, end.node) }
catch(e) {} // Our model of the DOM might be outdated, in which case the range we try to set can be impossible
try {
rng = range(start.node, start.offset, end.offset, end.node)
} catch (e) {
} // Our model of the DOM might be outdated, in which case the range we try to set can be impossible
if (rng) {
if (!gecko && cm.state.focused) {
sel.collapse(start.node, start.offset)
......@@ -199,8 +215,10 @@ export default class ContentEditableInput {
rememberSelection() {
let sel = this.getSelection()
this.lastAnchorNode = sel.anchorNode; this.lastAnchorOffset = sel.anchorOffset
this.lastFocusNode = sel.focusNode; this.lastFocusOffset = sel.focusOffset
this.lastAnchorNode = sel.anchorNode;
this.lastAnchorOffset = sel.anchorOffset
this.lastFocusNode = sel.focusNode;
this.lastFocusOffset = sel.focusOffset
}
selectionInEditor() {
......@@ -217,10 +235,18 @@ export default class ContentEditableInput {
this.div.focus()
}
}
blur() { this.div.blur() }
getField() { return this.div }
supportsTouch() { return true }
blur() {
this.div.blur()
}
getField() {
return this.div
}
supportsTouch() {
return true
}
receivedFocus() {
let input = this
......@@ -235,6 +261,7 @@ export default class ContentEditableInput {
input.polling.set(input.cm.options.pollInterval, poll)
}
}
this.polling.set(this.cm.options.pollInterval, poll)
}
......@@ -305,9 +332,15 @@ export default class ContentEditableInput {
let newText = cm.doc.splitLines(domTextBetween(cm, fromNode, toNode, fromLine, toLine))
let oldText = getBetween(cm.doc, Pos(fromLine, 0), Pos(toLine, getLine(cm.doc, toLine).text.length))
while (newText.length > 1 && oldText.length > 1) {
if (lst(newText) == lst(oldText)) { newText.pop(); oldText.pop(); toLine-- }
else if (newText[0] == oldText[0]) { newText.shift(); oldText.shift(); fromLine++ }
else break
if (lst(newText) == lst(oldText)) {
newText.pop();
oldText.pop();
toLine--
} else if (newText[0] == oldText[0]) {
newText.shift();
oldText.shift();
fromLine++
} else break
}
let cutFront = 0, cutEnd = 0
......@@ -343,9 +376,11 @@ export default class ContentEditableInput {
ensurePolled() {
this.forceCompositionEnd()
}
reset() {
this.forceCompositionEnd()
}
forceCompositionEnd() {
if (!this.composing) return
clearTimeout(this.readDOMTimeout)
......@@ -354,6 +389,7 @@ export default class ContentEditableInput {
this.div.blur()
this.div.focus()
}
readFromDOMSoon() {
if (this.readDOMTimeout != null) return
this.readDOMTimeout = setTimeout(() => {
......@@ -386,8 +422,11 @@ export default class ContentEditableInput {
this.div.contentEditable = String(val != "nocursor")
}
onContextMenu() {}
resetPosition() {}
onContextMenu() {
}
resetPosition() {
}
}
ContentEditableInput.prototype.needsContentAttribute = true
......@@ -414,11 +453,18 @@ function isInGutter(node) {
return false
}
function badPos(pos, bad) { if (bad) pos.bad = true; return pos }
function badPos(pos, bad) {
if (bad) pos.bad = true;
return pos
}
function domTextBetween(cm, from, to, fromLine, toLine) {
let text = "", closing = false, lineSep = cm.doc.lineSeparator(), extraLinebreak = false
function recognizeMarker(id) { return marker => marker.id == id }
function recognizeMarker(id) {
return marker => marker.id == id
}
function close() {
if (closing) {
text += lineSep
......@@ -426,12 +472,14 @@ function domTextBetween(cm, from, to, fromLine, toLine) {
closing = extraLinebreak = false
}
}
function addText(str) {
if (str) {
close()
text += str
}
}
function walk(node) {
if (node.nodeType == 1) {
let cmText = node.getAttribute("cm-text")
......@@ -460,7 +508,8 @@ function domTextBetween(cm, from, to, fromLine, toLine) {
addText(node.nodeValue.replace(/\u200b/g, "").replace(/\u00a0/g, " "))
}
}
for (;;) {
for (; ;) {
walk(from)
if (from == to) break
from = from.nextSibling
......@@ -474,9 +523,10 @@ function domToPos(cm, node, offset) {
if (node == cm.display.lineDiv) {
lineNode = cm.display.lineDiv.childNodes[offset]
if (!lineNode) return badPos(cm.clipPos(Pos(cm.display.viewTo - 1)), true)
node = null; offset = 0
node = null;
offset = 0
} else {
for (lineNode = node;; lineNode = lineNode.parentNode) {
for (lineNode = node; ; lineNode = lineNode.parentNode) {
if (!lineNode || lineNode == cm.display.lineDiv) return null
if (lineNode.parentNode && lineNode.parentNode == cm.display.lineDiv) break
}
......@@ -523,6 +573,7 @@ function locateNodeInLineView(lineView, node, offset) {
}
}
}
let found = find(textNode, topNode, offset)
if (found) return badPos(found, bad)
......
import { operation, runInOp } from "../display/operations.js"
import { prepareSelection } from "../display/selection.js"
import { applyTextInput, copyableRanges, handlePaste, hiddenTextarea, setLastCopied } from "./input.js"
import { cursorCoords, posFromMouse } from "../measurement/position_measurement.js"
import { eventInWidget } from "../measurement/widgets.js"
import { simpleSelection } from "../model/selection.js"
import { selectAll, setSelection } from "../model/selection_updates.js"
import { captureRightClick, ie, ie_version, ios, mac, mobile, presto, webkit } from "../util/browser.js"
import { activeElt, removeChildrenAndAdd, selectInput } from "../util/dom.js"
import { e_preventDefault, e_stop, off, on, signalDOMEvent } from "../util/event.js"
import { hasSelection } from "../util/feature_detection.js"
import { Delayed, sel_dontScroll } from "../util/misc.js"
import {operation, runInOp} from "../display/operations.js"
import {prepareSelection} from "../display/selection.js"
import {applyTextInput, copyableRanges, handlePaste, hiddenTextarea, setLastCopied} from "./input.js"
import {cursorCoords, posFromMouse} from "../measurement/position_measurement.js"
import {eventInWidget} from "../measurement/widgets.js"
import {simpleSelection} from "../model/selection.js"
import {selectAll, setSelection} from "../model/selection_updates.js"
import {captureRightClick, ie, ie_version, ios, mac, mobile, presto, webkit} from "../util/browser.js"
import {activeElt, removeChildrenAndAdd, selectInput} from "../util/dom.js"
import {e_preventDefault, e_stop, off, on, signalDOMEvent} from "../util/event.js"
import {hasSelection} from "../util/feature_detection.js"
import {Delayed, sel_dontScroll} from "../util/misc.js"
// TEXTAREA INPUT STYLE
......@@ -71,6 +71,7 @@ export default class TextareaInput {
}
if (e.type == "cut") cm.state.cutIncoming = +new Date
}
on(te, "cut", prepareCopyCut)
on(te, "copy", prepareCopyCut)
......@@ -120,7 +121,7 @@ export default class TextareaInput {
screenReaderLabelChanged(label) {
// Label for screenreaders, accessibility
if(label) {
if (label) {
this.textarea.setAttribute('aria-label', label)
} else {
this.textarea.removeAttribute('aria-label')
......@@ -172,24 +173,34 @@ export default class TextareaInput {
}
}
getField() { return this.textarea }
getField() {
return this.textarea
}
supportsTouch() { return false }
supportsTouch() {
return false
}
focus() {
if (this.cm.options.readOnly != "nocursor" && (!mobile || activeElt() != this.textarea)) {
try { this.textarea.focus() }
catch (e) {} // IE8 will throw if the textarea is display: none or not in DOM
try {
this.textarea.focus()
} catch (e) {
} // IE8 will throw if the textarea is display: none or not in DOM
}
}
blur() { this.textarea.blur() }
blur() {
this.textarea.blur()
}
resetPosition() {
this.wrapper.style.top = this.wrapper.style.left = 0
}
receivedFocus() { this.slowPoll() }
receivedFocus() {
this.slowPoll()
}
// Poll for input changes, using the normal rate of polling. This
// runs as long as the editor is focused.
......@@ -207,11 +218,18 @@ export default class TextareaInput {
fastPoll() {
let missed = false, input = this
input.pollingFast = true
function p() {
let changed = input.poll()
if (!changed && !missed) {missed = true; input.polling.set(60, p)}
else {input.pollingFast = false; input.slowPoll()}
if (!changed && !missed) {
missed = true;
input.polling.set(60, p)
} else {
input.pollingFast = false;
input.slowPoll()
}
}
input.polling.set(20, p)
}
......@@ -247,7 +265,10 @@ export default class TextareaInput {
if (cm.doc.sel == cm.display.selForContextMenu) {
let first = text.charCodeAt(0)
if (first == 0x200b && !prevInput) prevInput = "\u200b"
if (first == 0x21da) { this.reset(); return this.cm.execCommand("undo") }
if (first == 0x21da) {
this.reset();
return this.cm.execCommand("undo")
}
}
// Find the part of the input that is actually new
let same = 0, l = Math.min(prevInput.length, text.length)
......@@ -319,12 +340,14 @@ export default class TextareaInput {
te.value = "\u21da" // Used to catch context-menu undo
te.value = extval
input.prevInput = selected ? "" : "\u200b"
te.selectionStart = 1; te.selectionEnd = extval.length
te.selectionStart = 1;
te.selectionEnd = extval.length
// Re-set this, in case some other handler touched the
// selection in the meantime.
display.selForContextMenu = cm.doc.sel
}
}
function rehide() {
if (input.contextMenuPending != rehide) return
input.contextMenuPending = false
......@@ -369,7 +392,8 @@ export default class TextareaInput {
this.textarea.readOnly = !!val
}
setUneditable() {}
setUneditable() {
}
}
TextareaInput.prototype.needsContentAttribute = false
import { getContextBefore } from "../line/highlight.js"
import { Pos } from "../line/pos.js"
import { getLine } from "../line/utils_line.js"
import { replaceRange } from "../model/changes.js"
import { Range } from "../model/selection.js"
import { replaceOneSelection } from "../model/selection_updates.js"
import { countColumn, Pass, spaceStr } from "../util/misc.js"
import {getContextBefore} from "../line/highlight.js"
import {Pos} from "../line/pos.js"
import {getLine} from "../line/utils_line.js"
import {replaceRange} from "../model/changes.js"
import {Range} from "../model/selection.js"
import {replaceOneSelection} from "../model/selection_updates.js"
import {countColumn, Pass, spaceStr} from "../util/misc.js"
// Indent the given line. The how parameter can be "smart",
// "add"/null, "subtract", or "prev". When aggressive is false
......@@ -36,7 +36,7 @@ export function indentLine(cm, n, how, aggressive) {
}
}
if (how == "prev") {
if (n > doc.first) indentation = countColumn(getLine(doc, n-1).text, null, tabSize)
if (n > doc.first) indentation = countColumn(getLine(doc, n - 1).text, null, tabSize)
else indentation = 0
} else if (how == "add") {
indentation = curSpace + cm.options.indentUnit
......@@ -49,7 +49,10 @@ export function indentLine(cm, n, how, aggressive) {
let indentString = "", pos = 0
if (cm.options.indentWithTabs)
for (let i = Math.floor(indentation / tabSize); i; --i) {pos += tabSize; indentString += "\t"}
for (let i = Math.floor(indentation / tabSize); i; --i) {
pos += tabSize;
indentString += "\t"
}
if (pos < indentation) indentString += spaceStr(indentation - pos)
if (indentString != curSpaceString) {
......
import { runInOp } from "../display/operations.js"
import { ensureCursorVisible } from "../display/scrolling.js"
import { Pos } from "../line/pos.js"
import { getLine } from "../line/utils_line.js"
import { makeChange } from "../model/changes.js"
import { ios, webkit } from "../util/browser.js"
import { elt } from "../util/dom.js"
import { lst, map } from "../util/misc.js"
import { signalLater } from "../util/operation_group.js"
import { splitLinesAuto } from "../util/feature_detection.js"
import {runInOp} from "../display/operations.js"
import {ensureCursorVisible} from "../display/scrolling.js"
import {Pos} from "../line/pos.js"
import {getLine} from "../line/utils_line.js"
import {makeChange} from "../model/changes.js"
import {ios, webkit} from "../util/browser.js"
import {elt} from "../util/dom.js"
import {lst, map} from "../util/misc.js"
import {signalLater} from "../util/operation_group.js"
import {splitLinesAuto} from "../util/feature_detection.js"
import { indentLine } from "./indent.js"
import {indentLine} from "./indent.js"
// This will be set to a {lineWise: bool, text: [string]} object, so
// that, when pasting, we know what kind of selections the copied
......@@ -54,8 +54,10 @@ export function applyTextInput(cm, inserted, deleted, sel, origin) {
else if (paste && lastCopied && lastCopied.lineWise && lastCopied.text.join("\n") == textLines.join("\n"))
from = to = Pos(from.line, 0)
}
let changeEvent = {from: from, to: to, text: multiPaste ? multiPaste[i % multiPaste.length] : textLines,
origin: origin || (paste ? "paste" : cm.state.cutIncoming > recent ? "cut" : "+input")}
let changeEvent = {
from: from, to: to, text: multiPaste ? multiPaste[i % multiPaste.length] : textLines,
origin: origin || (paste ? "paste" : cm.state.cutIncoming > recent ? "cut" : "+input")
}
makeChange(cm.doc, changeEvent)
signalLater(cm, "inputRead", cm, changeEvent)
}
......
import { flipCtrlCmd, mac, presto } from "../util/browser.js"
import { map } from "../util/misc.js"
import {flipCtrlCmd, mac, presto} from "../util/browser.js"
import {map} from "../util/misc.js"
import { keyNames } from "./keynames.js"
import {keyNames} from "./keynames.js"
export let keyMap = {}
......@@ -35,13 +35,36 @@ keyMap.emacsy = {
"Ctrl-O": "openLine"
}
keyMap.macDefault = {
"Cmd-A": "selectAll", "Cmd-D": "deleteLine", "Cmd-Z": "undo", "Shift-Cmd-Z": "redo", "Cmd-Y": "redo",
"Cmd-Home": "goDocStart", "Cmd-Up": "goDocStart", "Cmd-End": "goDocEnd", "Cmd-Down": "goDocEnd", "Alt-Left": "goGroupLeft",
"Alt-Right": "goGroupRight", "Cmd-Left": "goLineLeft", "Cmd-Right": "goLineRight", "Alt-Backspace": "delGroupBefore",
"Ctrl-Alt-Backspace": "delGroupAfter", "Alt-Delete": "delGroupAfter", "Cmd-S": "save", "Cmd-F": "find",
"Cmd-G": "findNext", "Shift-Cmd-G": "findPrev", "Cmd-Alt-F": "replace", "Shift-Cmd-Alt-F": "replaceAll",
"Cmd-[": "indentLess", "Cmd-]": "indentMore", "Cmd-Backspace": "delWrappedLineLeft", "Cmd-Delete": "delWrappedLineRight",
"Cmd-U": "undoSelection", "Shift-Cmd-U": "redoSelection", "Ctrl-Up": "goDocStart", "Ctrl-Down": "goDocEnd",
"Cmd-A": "selectAll",
"Cmd-D": "deleteLine",
"Cmd-Z": "undo",
"Shift-Cmd-Z": "redo",
"Cmd-Y": "redo",
"Cmd-Home": "goDocStart",
"Cmd-Up": "goDocStart",
"Cmd-End": "goDocEnd",
"Cmd-Down": "goDocEnd",
"Alt-Left": "goGroupLeft",
"Alt-Right": "goGroupRight",
"Cmd-Left": "goLineLeft",
"Cmd-Right": "goLineRight",
"Alt-Backspace": "delGroupBefore",
"Ctrl-Alt-Backspace": "delGroupAfter",
"Alt-Delete": "delGroupAfter",
"Cmd-S": "save",
"Cmd-F": "find",
"Cmd-G": "findNext",
"Shift-Cmd-G": "findPrev",
"Cmd-Alt-F": "replace",
"Shift-Cmd-Alt-F": "replaceAll",
"Cmd-[": "indentLess",
"Cmd-]": "indentMore",
"Cmd-Backspace": "delWrappedLineLeft",
"Cmd-Delete": "delWrappedLineRight",
"Cmd-U": "undoSelection",
"Shift-Cmd-U": "redoSelection",
"Ctrl-Up": "goDocStart",
"Ctrl-Down": "goDocEnd",
"fallthrough": ["basic", "emacsy"]
}
keyMap["default"] = mac ? keyMap.macDefault : keyMap.pcDefault
......@@ -77,7 +100,10 @@ export function normalizeKeyMap(keymap) {
for (let keyname in keymap) if (keymap.hasOwnProperty(keyname)) {
let value = keymap[keyname]
if (/^(name|fallthrough|(de|at)tach)$/.test(keyname)) continue
if (value == "...") { delete keymap[keyname]; continue }
if (value == "...") {
delete keymap[keyname];
continue
}
let keys = map(keyname.split(" "), normalizeKeyName)
for (let i = 0; i < keys.length; i++) {
......
import { Pos } from "../line/pos.js"
import { prepareMeasureForLine, measureCharPrepared, wrappedLineExtentChar } from "../measurement/position_measurement.js"
import { getBidiPartAt, getOrder } from "../util/bidi.js"
import { findFirst, lst, skipExtendingChars } from "../util/misc.js"
import {Pos} from "../line/pos.js"
import {measureCharPrepared, prepareMeasureForLine, wrappedLineExtentChar} from "../measurement/position_measurement.js"
import {getBidiPartAt, getOrder} from "../util/bidi.js"
import {findFirst, lst, skipExtendingChars} from "../util/misc.js"
function moveCharLogically(line, ch, dir) {
let target = skipExtendingChars(line.text, ch + dir, dir)
......
import { countColumn } from "../util/misc.js"
import { copyState, innerMode, startState } from "../modes.js"
import {countColumn} from "../util/misc.js"
import {copyState, innerMode, startState} from "../modes.js"
import StringStream from "../util/StringStream.js"
import { getLine, lineNo } from "./utils_line.js"
import { clipPos } from "./pos.js"
import {getLine, lineNo} from "./utils_line.js"
import {clipPos} from "./pos.js"
class SavedContext {
constructor(state, lookAhead) {
......@@ -22,6 +22,13 @@ class Context {
this.baseTokenPos = 1
}
static fromSaved(doc, saved, line) {
if (saved instanceof SavedContext)
return new Context(doc, copyState(doc.mode, saved.state), line, saved.lookAhead)
else
return new Context(doc, copyState(doc.mode, saved), line)
}
lookAhead(n) {
let line = this.doc.getLine(this.line + n)
if (line != null && n > this.maxLookAhead) this.maxLookAhead = n
......@@ -33,8 +40,10 @@ class Context {
while (this.baseTokens[this.baseTokenPos] <= n)
this.baseTokenPos += 2
let type = this.baseTokens[this.baseTokenPos + 1]
return {type: type && type.replace(/( |^)overlay .*/, ""),
size: this.baseTokens[this.baseTokenPos] - n}
return {
type: type && type.replace(/( |^)overlay .*/, ""),
size: this.baseTokens[this.baseTokenPos] - n
}
}
nextLine() {
......@@ -42,13 +51,6 @@ class Context {
if (this.maxLookAhead > 0) this.maxLookAhead--
}
static fromSaved(doc, saved, line) {
if (saved instanceof SavedContext)
return new Context(doc, copyState(doc.mode, saved.state), line, saved.lookAhead)
else
return new Context(doc, copyState(doc.mode, saved), line)
}
save(copy) {
let state = copy !== false ? copyState(this.doc.mode, this.state) : this.state
return this.maxLookAhead > 0 ? new SavedContext(state, this.maxLookAhead) : state
......@@ -80,7 +82,7 @@ export function highlightLine(cm, line, context, forceToEnd) {
while (at < end) {
let i_end = st[i]
if (i_end > end)
st.splice(i, 1, end, st[i+1], i_end)
st.splice(i, 1, end, st[i + 1], i_end)
i += 2
at = Math.min(end, i_end)
}
......@@ -90,8 +92,8 @@ export function highlightLine(cm, line, context, forceToEnd) {
i = start + 2
} else {
for (; start < i; start += 2) {
let cur = st[start+1]
st[start+1] = (cur ? cur + " " : "") + "overlay " + style
let cur = st[start + 1]
st[start + 1] = (cur ? cur + " " : "") + "overlay " + style
}
}
}, lineClasses)
......@@ -168,7 +170,8 @@ function readToken(mode, stream, state, inner) {
class Token {
constructor(stream, type, state) {
this.start = stream.start; this.end = stream.pos
this.start = stream.start;
this.end = stream.pos
this.string = stream.current()
this.type = type || null
this.state = state
......@@ -191,7 +194,7 @@ export function takeToken(cm, pos, precise, asArray) {
}
function extractLineClasses(type, output) {
if (type) for (;;) {
if (type) for (; ;) {
let lineClass = type.match(/(?:^|\s+)line-(background-)?(\S+)/)
if (!lineClass) break
type = type.slice(0, lineClass.index) + type.slice(lineClass.index + lineClass[0].length)
......
import { getOrder } from "../util/bidi.js"
import { ie, ie_version, webkit } from "../util/browser.js"
import { elt, eltP, joinClasses } from "../util/dom.js"
import { eventMixin, signal } from "../util/event.js"
import { hasBadBidiRects, zeroWidthElement } from "../util/feature_detection.js"
import { lst, spaceStr } from "../util/misc.js"
import {getOrder} from "../util/bidi.js"
import {ie, ie_version, webkit} from "../util/browser.js"
import {elt, eltP, joinClasses} from "../util/dom.js"
import {eventMixin, signal} from "../util/event.js"
import {hasBadBidiRects, zeroWidthElement} from "../util/feature_detection.js"
import {lst, spaceStr} from "../util/misc.js"
import { getLineStyles } from "./highlight.js"
import { attachMarkedSpans, compareCollapsedMarkers, detachMarkedSpans, lineIsHidden, visualLineContinued } from "./spans.js"
import { getLine, lineNo, updateLineHeight } from "./utils_line.js"
import {getLineStyles} from "./highlight.js"
import {
attachMarkedSpans,
compareCollapsedMarkers,
detachMarkedSpans,
lineIsHidden,
visualLineContinued
} from "./spans.js"
import {getLine, lineNo, updateLineHeight} from "./utils_line.js"
// LINE DATA STRUCTURE
......@@ -20,8 +26,11 @@ export class Line {
this.height = estimateHeight ? estimateHeight(this) : 1
}
lineNo() { return lineNo(this) }
lineNo() {
return lineNo(this)
}
}
eventMixin(Line)
// Change the content (text, markers) of a line. Automatically
......@@ -48,6 +57,7 @@ export function cleanUpLine(line) {
// containing one or more styles) to a CSS style. This is cached,
// and also looks for line-wide styles.
let styleToClassCache = {}, styleToClassCacheWithMode = {}
function interpretTokenStyle(style, options) {
if (!style || /^\s*$/.test(style)) return null
let cache = options.addModeClass ? styleToClassCacheWithMode : styleToClassCache
......@@ -65,10 +75,12 @@ export function buildLineContent(cm, lineView) {
// is needed on Webkit to be able to get line-level bounding
// rectangles for it (in measureChar).
let content = eltP("span", null, null, webkit ? "padding-right: .1px" : null)
let builder = {pre: eltP("pre", [content], "CodeMirror-line"), content: content,
let builder = {
pre: eltP("pre", [content], "CodeMirror-line"), content: content,
col: 0, pos: 0, cm: cm,
trailingSpace: false,
splitSpaces: cm.getOption("lineWrapping")}
splitSpaces: cm.getOption("lineWrapping")
}
lineView.measure = {}
// Iterate over the logical lines that make up this visual line.
......@@ -213,7 +225,7 @@ function buildTokenBadBidi(inner, order) {
return (builder, text, style, startStyle, endStyle, css, attributes) => {
style = style ? style + " cm-force-border" : "cm-force-border"
let start = builder.pos, end = start + text.length
for (;;) {
for (; ;) {
// Find the part that overlaps with the start of this text
let part
for (let i = 0; i < order.length; i++) {
......@@ -250,18 +262,19 @@ function buildCollapsedSpan(builder, size, marker, ignoreWidget) {
function insertLineContent(line, builder, styles) {
let spans = line.markedSpans, allText = line.text, at = 0
if (!spans) {
for (let i = 1; i < styles.length; i+=2)
builder.addToken(builder, allText.slice(at, at = styles[i]), interpretTokenStyle(styles[i+1], builder.cm.options))
for (let i = 1; i < styles.length; i += 2)
builder.addToken(builder, allText.slice(at, at = styles[i]), interpretTokenStyle(styles[i + 1], builder.cm.options))
return
}
let len = allText.length, pos = 0, i = 1, text = "", style, css
let nextChange = 0, spanStyle, spanEndStyle, spanStartStyle, collapsed, attributes
for (;;) {
for (; ;) {
if (nextChange == pos) { // Update current marker set
spanStyle = spanEndStyle = spanStartStyle = css = ""
attributes = null
collapsed = null; nextChange = Infinity
collapsed = null;
nextChange = Infinity
let foundBookmarks = [], endStyles
for (let j = 0; j < spans.length; ++j) {
let sp = spans[j], m = sp.marker
......@@ -312,7 +325,11 @@ function insertLineContent(line, builder, styles) {
builder.addToken(builder, tokenText, style ? style + spanStyle : spanStyle,
spanStartStyle, pos + tokenText.length == nextChange ? spanEndStyle : "", css, attributes)
}
if (end >= upto) {text = text.slice(upto - pos); pos = upto; break}
if (end >= upto) {
text = text.slice(upto - pos);
pos = upto;
break
}
pos = end
spanStartStyle = ""
}
......
import { getLine } from "./utils_line.js"
import {getLine} from "./utils_line.js"
// A Pos instance represents a position within the text.
export function Pos(line, ch, sticky = null) {
......@@ -10,29 +10,46 @@ export function Pos(line, ch, sticky = null) {
// Compare two positions, return 0 if they are the same, a negative
// number when a is less, and a positive number otherwise.
export function cmp(a, b) { return a.line - b.line || a.ch - b.ch }
export function cmp(a, b) {
return a.line - b.line || a.ch - b.ch
}
export function equalCursorPos(a, b) {
return a.sticky == b.sticky && cmp(a, b) == 0
}
export function equalCursorPos(a, b) { return a.sticky == b.sticky && cmp(a, b) == 0 }
export function copyPos(x) {
return Pos(x.line, x.ch)
}
export function copyPos(x) {return Pos(x.line, x.ch)}
export function maxPos(a, b) { return cmp(a, b) < 0 ? b : a }
export function minPos(a, b) { return cmp(a, b) < 0 ? a : b }
export function maxPos(a, b) {
return cmp(a, b) < 0 ? b : a
}
export function minPos(a, b) {
return cmp(a, b) < 0 ? a : b
}
// Most of the external API clips given positions to make sure they
// actually exist within the document.
export function clipLine(doc, n) {return Math.max(doc.first, Math.min(n, doc.first + doc.size - 1))}
export function clipLine(doc, n) {
return Math.max(doc.first, Math.min(n, doc.first + doc.size - 1))
}
export function clipPos(doc, pos) {
if (pos.line < doc.first) return Pos(doc.first, 0)
let last = doc.first + doc.size - 1
if (pos.line > last) return Pos(last, getLine(doc, last).text.length)
return clipToLen(pos, getLine(doc, pos.line).text.length)
}
function clipToLen(pos, linelen) {
let ch = pos.ch
if (ch == null || ch > linelen) return Pos(pos.line, linelen)
else if (ch < 0) return Pos(pos.line, 0)
else return pos
}
export function clipPosArray(doc, array) {
let out = []
for (let i = 0; i < array.length; i++) out[i] = clipPos(doc, array[i])
......
import { indexOf, lst } from "../util/misc.js"
import {indexOf, lst} from "../util/misc.js"
import { cmp } from "./pos.js"
import { sawCollapsedSpans } from "./saw_special_spans.js"
import { getLine, isLine, lineNo } from "./utils_line.js"
import {cmp} from "./pos.js"
import {sawCollapsedSpans} from "./saw_special_spans.js"
import {getLine, isLine, lineNo} from "./utils_line.js"
// TEXTMARKER SPANS
export function MarkedSpan(marker, from, to) {
this.marker = marker
this.from = from; this.to = to
this.from = from;
this.to = to
}
// Search an array of spans for a span matching the given marker.
......@@ -18,6 +19,7 @@ export function getMarkedSpanFor(spans, marker) {
if (span.marker == marker) return span
}
}
// Remove a span from an array, returning undefined if no spans are
// left (we don't store arrays for lines without spans).
export function removeMarkedSpan(spans, span) {
......@@ -26,6 +28,7 @@ export function removeMarkedSpan(spans, span) {
if (spans[i] != span) (r || (r = [])).push(spans[i])
return r
}
// Add a span to a line.
export function addMarkedSpan(line, span) {
line.markedSpans = line.markedSpans ? line.markedSpans.concat([span]) : [span]
......@@ -48,6 +51,7 @@ function markedSpansBefore(old, startCh, isInsert) {
}
return nw
}
function markedSpansAfter(old, endCh, isInsert) {
let nw
if (old) for (let i = 0; i < old.length; ++i) {
......@@ -177,6 +181,7 @@ export function detachMarkedSpans(line) {
spans[i].marker.detachLine(line)
line.markedSpans = null
}
export function attachMarkedSpans(line, spans) {
if (!spans) return
for (let i = 0; i < spans.length; ++i)
......@@ -186,8 +191,13 @@ export function attachMarkedSpans(line, spans) {
// Helpers used when computing which overlapping collapsed span
// counts as the larger one.
function extraLeft(marker) { return marker.inclusiveLeft ? -1 : 0 }
function extraRight(marker) { return marker.inclusiveRight ? 1 : 0 }
function extraLeft(marker) {
return marker.inclusiveLeft ? -1 : 0
}
function extraRight(marker) {
return marker.inclusiveRight ? 1 : 0
}
// Returns a number indicating which of two overlapping collapsed
// spans is larger (and thus includes the other). Falls back to
......@@ -215,8 +225,14 @@ function collapsedSpanAtSide(line, start) {
}
return found
}
export function collapsedSpanAtStart(line) { return collapsedSpanAtSide(line, true) }
export function collapsedSpanAtEnd(line) { return collapsedSpanAtSide(line, false) }
export function collapsedSpanAtStart(line) {
return collapsedSpanAtSide(line, true)
}
export function collapsedSpanAtEnd(line) {
return collapsedSpanAtSide(line, false)
}
export function collapsedSpanAround(line, ch) {
let sps = sawCollapsedSpans && line.markedSpans, found
......@@ -309,6 +325,7 @@ export function lineIsHidden(doc, line) {
return true
}
}
function lineIsHiddenInner(doc, line, span) {
if (span.to == null) {
let end = span.marker.find(1, true)
......
import { indexOf } from "../util/misc.js"
import {indexOf} from "../util/misc.js"
// Find the line object corresponding to the given line number.
export function getLine(doc, n) {
......@@ -6,9 +6,12 @@ export function getLine(doc, n) {
if (n < 0 || n >= doc.size) throw new Error("There is no line " + (n + doc.first) + " in the document.")
let chunk = doc
while (!chunk.lines) {
for (let i = 0;; ++i) {
for (let i = 0; ; ++i) {
let child = chunk.children[i], sz = child.chunkSize()
if (n < sz) { chunk = child; break }
if (n < sz) {
chunk = child;
break
}
n -= sz
}
}
......@@ -28,10 +31,13 @@ export function getBetween(doc, start, end) {
})
return out
}
// Get the lines between from and to, as array of strings.
export function getLines(doc, from, to) {
let out = []
doc.iter(from, to, line => { out.push(line.text) }) // iter aborts when callback returns truthy value
doc.iter(from, to, line => {
out.push(line.text)
}) // iter aborts when callback returns truthy value
return out
}
......@@ -48,7 +54,7 @@ export function lineNo(line) {
if (line.parent == null) return null
let cur = line.parent, no = indexOf(cur.lines, line)
for (let chunk = cur.parent; chunk; cur = chunk, chunk = chunk.parent) {
for (let i = 0;; ++i) {
for (let i = 0; ; ++i) {
if (chunk.children[i] == cur) break
no += chunk.children[i].chunkSize()
}
......@@ -63,7 +69,10 @@ export function lineAtHeight(chunk, h) {
outer: do {
for (let i = 0; i < chunk.children.length; ++i) {
let child = chunk.children[i], ch = child.height
if (h < ch) { chunk = child; continue outer }
if (h < ch) {
chunk = child;
continue outer
}
h -= ch
n += child.chunkSize()
}
......@@ -78,7 +87,9 @@ export function lineAtHeight(chunk, h) {
return n + i
}
export function isLine(doc, l) {return l >= doc.first && l < doc.first + doc.size}
export function isLine(doc, l) {
return l >= doc.first && l < doc.first + doc.size
}
export function lineNumberFor(options, i) {
return String(options.lineNumberFormatter(i + options.firstLineNumber))
......
import { buildLineContent, LineView } from "../line/line_data.js"
import { clipPos, Pos } from "../line/pos.js"
import { collapsedSpanAround, heightAtLine, lineIsHidden, visualLine } from "../line/spans.js"
import { getLine, lineAtHeight, lineNo, updateLineHeight } from "../line/utils_line.js"
import { bidiOther, getBidiPartAt, getOrder } from "../util/bidi.js"
import { chrome, android, ie, ie_version } from "../util/browser.js"
import { elt, removeChildren, range, removeChildrenAndAdd } from "../util/dom.js"
import { e_target } from "../util/event.js"
import { hasBadZoomedRects } from "../util/feature_detection.js"
import { countColumn, findFirst, isExtendingChar, scrollerGap, skipExtendingChars } from "../util/misc.js"
import { updateLineForChanges } from "../display/update_line.js"
import { widgetHeight } from "./widgets.js"
import {buildLineContent, LineView} from "../line/line_data.js"
import {clipPos, Pos} from "../line/pos.js"
import {collapsedSpanAround, heightAtLine, lineIsHidden, visualLine} from "../line/spans.js"
import {getLine, lineAtHeight, lineNo, updateLineHeight} from "../line/utils_line.js"
import {bidiOther, getBidiPartAt, getOrder} from "../util/bidi.js"
import {android, chrome, ie, ie_version} from "../util/browser.js"
import {elt, range, removeChildren, removeChildrenAndAdd} from "../util/dom.js"
import {e_target} from "../util/event.js"
import {hasBadZoomedRects} from "../util/feature_detection.js"
import {countColumn, findFirst, isExtendingChar, scrollerGap, skipExtendingChars} from "../util/misc.js"
import {updateLineForChanges} from "../display/update_line.js"
import {widgetHeight} from "./widgets.js"
// POSITION MEASUREMENT
export function paddingTop(display) {return display.lineSpace.offsetTop}
export function paddingVert(display) {return display.mover.offsetHeight - display.lineSpace.offsetHeight}
export function paddingTop(display) {
return display.lineSpace.offsetTop
}
export function paddingVert(display) {
return display.mover.offsetHeight - display.lineSpace.offsetHeight
}
export function paddingH(display) {
if (display.cachedPaddingH) return display.cachedPaddingH
let e = removeChildrenAndAdd(display.measure, elt("pre", "x", "CodeMirror-line-like"))
......@@ -25,10 +31,14 @@ export function paddingH(display) {
return data
}
export function scrollGap(cm) { return scrollerGap - cm.display.nativeBarWidth }
export function scrollGap(cm) {
return scrollerGap - cm.display.nativeBarWidth
}
export function displayWidth(cm) {
return cm.display.scroller.clientWidth - scrollGap(cm) - cm.display.barWidth
}
export function displayHeight(cm) {
return cm.display.scroller.clientHeight - scrollGap(cm) - cm.display.barHeight
}
......@@ -139,9 +149,11 @@ export function measureCharPrepared(cm, prepared, ch, bias, varHeight) {
found = measureCharInner(cm, prepared, ch, bias)
if (!found.bogus) prepared.cache[key] = found
}
return {left: found.left, right: found.right,
return {
left: found.left, right: found.right,
top: varHeight ? found.rtop : found.top,
bottom: varHeight ? found.rbottom : found.bottom}
bottom: varHeight ? found.rbottom : found.bottom
}
}
let nullRect = {left: 0, right: 0, top: 0, bottom: 0}
......@@ -154,7 +166,8 @@ export function nodeAndOffsetInLineMap(map, ch, bias) {
mStart = map[i]
mEnd = map[i + 1]
if (ch < mStart) {
start = 0; end = 1
start = 0;
end = 1
collapse = "left"
} else if (ch < mEnd) {
start = ch - mStart
......@@ -236,11 +249,16 @@ function measureCharInner(cm, prepared, ch, bias) {
for (; i < heights.length - 1; i++)
if (mid < heights[i]) break
let top = i ? heights[i - 1] : 0, bot = heights[i]
let result = {left: (collapse == "right" ? rect.right : rect.left) - prepared.rect.left,
let result = {
left: (collapse == "right" ? rect.right : rect.left) - prepared.rect.left,
right: (collapse == "left" ? rect.left : rect.right) - prepared.rect.left,
top: top, bottom: bot}
top: top, bottom: bot
}
if (!rect.left && !rect.right) result.bogus = true
if (!cm.options.singleCursorHeightPerLine) { result.rtop = rtop; result.rbottom = rbot }
if (!cm.options.singleCursorHeightPerLine) {
result.rtop = rtop;
result.rbottom = rbot
}
return result
}
......@@ -253,8 +271,10 @@ function maybeUpdateRectForZooming(measure, rect) {
return rect
let scaleX = screen.logicalXDPI / screen.deviceXDPI
let scaleY = screen.logicalYDPI / screen.deviceYDPI
return {left: rect.left * scaleX, right: rect.right * scaleX,
top: rect.top * scaleY, bottom: rect.bottom * scaleY}
return {
left: rect.left * scaleX, right: rect.right * scaleX,
top: rect.top * scaleY, bottom: rect.bottom * scaleY
}
}
export function clearLineMeasurementCacheFor(lineView) {
......@@ -287,6 +307,7 @@ function pageScrollX() {
if (chrome && android) return -(document.body.getBoundingClientRect().left - parseInt(getComputedStyle(document.body).marginLeft))
return window.pageXOffset || (document.documentElement || document.body).scrollLeft
}
function pageScrollY() {
if (chrome && android) return -(document.body.getBoundingClientRect().top - parseInt(getComputedStyle(document.body).marginTop))
return window.pageYOffset || (document.documentElement || document.body).scrollTop
......@@ -306,7 +327,8 @@ function widgetTopHeight(lineObj) {
export function intoCoordSystem(cm, lineObj, rect, context, includeWidgets) {
if (!includeWidgets) {
let height = widgetTopHeight(lineObj)
rect.top += height; rect.bottom += height
rect.top += height;
rect.bottom += height
}
if (context == "line") return rect
if (!context) context = "local"
......@@ -317,9 +339,11 @@ export function intoCoordSystem(cm, lineObj, rect, context, includeWidgets) {
let lOff = cm.display.lineSpace.getBoundingClientRect()
yOff += lOff.top + (context == "window" ? 0 : pageScrollY())
let xOff = lOff.left + (context == "window" ? 0 : pageScrollX())
rect.left += xOff; rect.right += xOff
rect.left += xOff;
rect.right += xOff
}
rect.top += yOff; rect.bottom += yOff
rect.top += yOff;
rect.bottom += yOff
return rect
}
......@@ -366,11 +390,13 @@ export function charCoords(cm, pos, context, lineObj, bias) {
export function cursorCoords(cm, pos, context, lineObj, preparedMeasure, varHeight) {
lineObj = lineObj || getLine(cm.doc, pos.line)
if (!preparedMeasure) preparedMeasure = prepareMeasureForLine(cm, lineObj)
function get(ch, right) {
let m = measureCharPrepared(cm, preparedMeasure, ch, right ? "right" : "left", varHeight)
if (right) m.left = m.right; else m.right = m.left
return intoCoordSystem(cm, lineObj, m, context)
}
let order = getOrder(lineObj, cm.doc.direction), ch = pos.ch, sticky = pos.sticky
if (ch >= lineObj.text.length) {
ch = lineObj.text.length
......@@ -385,6 +411,7 @@ export function cursorCoords(cm, pos, context, lineObj, preparedMeasure, varHeig
let part = order[partPos], right = part.level == 1
return get(invert ? ch - 1 : ch, right != invert)
}
let partPos = getBidiPartAt(order, ch, sticky)
let other = bidiOther
let val = getBidi(ch, partPos, sticky == "before")
......@@ -428,7 +455,7 @@ export function coordsChar(cm, x, y) {
if (x < 0) x = 0
let lineObj = getLine(doc, lineN)
for (;;) {
for (; ;) {
let found = coordsCharInner(cm, lineObj, lineN, x, y)
let collapsed = collapsedSpanAround(lineObj, found.ch + (found.xRel > 0 || found.outside > 0 ? 1 : 0))
if (!collapsed) return found
......@@ -488,7 +515,8 @@ function coordsCharInner(cm, lineObj, lineNo, x, y) {
let chAround = null, boxAround = null
let ch = findFirst(ch => {
let box = measureCharPrepared(cm, preparedMeasure, ch)
box.top += widgetHeight; box.bottom += widgetHeight
box.top += widgetHeight;
box.bottom += widgetHeight
if (!boxIsAfter(box, x, y, false)) return false
if (box.top <= y && box.left <= x) {
chAround = ch
......@@ -581,6 +609,7 @@ function coordsBidiPartWrapped(cm, lineObj, _lineNo, preparedMeasure, order, x,
}
let measureText
// Compute the default text height.
export function textHeight(display) {
if (display.cachedTextHeight != null) return display.cachedTextHeight
......@@ -622,11 +651,13 @@ export function getDimensions(cm) {
left[id] = n.offsetLeft + n.clientLeft + gutterLeft
width[id] = n.clientWidth
}
return {fixedPos: compensateForHScroll(d),
return {
fixedPos: compensateForHScroll(d),
gutterTotalWidth: d.gutters.offsetWidth,
gutterLeft: left,
gutterWidth: width,
wrapperWidth: d.wrapper.clientWidth}
wrapperWidth: d.wrapper.clientWidth
}
}
// Computes display.scroller.scrollLeft + display.gutters.offsetWidth,
......@@ -676,8 +707,12 @@ export function posFromMouse(cm, e, liberal, forRect) {
let x, y, space = display.lineSpace.getBoundingClientRect()
// Fails unpredictably on IE[67] when mouse is dragged around quickly.
try { x = e.clientX - space.left; y = e.clientY - space.top }
catch (e) { return null }
try {
x = e.clientX - space.left;
y = e.clientY - space.top
} catch (e) {
return null
}
let coords = coordsChar(cm, x, y), line
if (forRect && coords.xRel > 0 && (line = getLine(cm.doc, coords.line).text).length == coords.ch) {
let colDiff = countColumn(line, line.length, cm.options.tabSize) - line.length
......
import { contains, elt, removeChildrenAndAdd } from "../util/dom.js"
import { e_target } from "../util/event.js"
import {contains, elt, removeChildrenAndAdd} from "../util/dom.js"
import {e_target} from "../util/event.js"
export function widgetHeight(widget) {
if (widget.height != null) return widget.height
......
import CodeMirror from "../edit/CodeMirror.js"
import { docMethodOp } from "../display/operations.js"
import { Line } from "../line/line_data.js"
import { clipPos, clipPosArray, Pos } from "../line/pos.js"
import { visualLine } from "../line/spans.js"
import { getBetween, getLine, getLines, isLine, lineNo } from "../line/utils_line.js"
import { classTest } from "../util/dom.js"
import { splitLinesAuto } from "../util/feature_detection.js"
import { createObj, map, isEmpty, sel_dontScroll } from "../util/misc.js"
import { ensureCursorVisible, scrollToCoords } from "../display/scrolling.js"
import { changeLine, makeChange, makeChangeFromHistory, replaceRange } from "./changes.js"
import { computeReplacedSel } from "./change_measurement.js"
import { BranchChunk, LeafChunk } from "./chunk.js"
import { directionChanged, linkedDocs, updateDoc } from "./document_data.js"
import { copyHistoryArray, History } from "./history.js"
import { addLineWidget } from "./line_widget.js"
import { copySharedMarkers, detachSharedMarkers, findSharedMarkers, markText } from "./mark_text.js"
import { normalizeSelection, Range, simpleSelection } from "./selection.js"
import { extendSelection, extendSelections, setSelection, setSelectionReplaceHistory, setSimpleSelection } from "./selection_updates.js"
import {docMethodOp} from "../display/operations.js"
import {Line} from "../line/line_data.js"
import {clipPos, clipPosArray, Pos} from "../line/pos.js"
import {visualLine} from "../line/spans.js"
import {getBetween, getLine, getLines, isLine, lineNo} from "../line/utils_line.js"
import {classTest} from "../util/dom.js"
import {splitLinesAuto} from "../util/feature_detection.js"
import {createObj, isEmpty, map, sel_dontScroll} from "../util/misc.js"
import {ensureCursorVisible, scrollToCoords} from "../display/scrolling.js"
import {changeLine, makeChange, makeChangeFromHistory, replaceRange} from "./changes.js"
import {computeReplacedSel} from "./change_measurement.js"
import {BranchChunk, LeafChunk} from "./chunk.js"
import {directionChanged, linkedDocs, updateDoc} from "./document_data.js"
import {copyHistoryArray, History} from "./history.js"
import {addLineWidget} from "./line_widget.js"
import {copySharedMarkers, detachSharedMarkers, findSharedMarkers, markText} from "./mark_text.js"
import {normalizeSelection, Range, simpleSelection} from "./selection.js"
import {
extendSelection,
extendSelections,
setSelection,
setSelectionReplaceHistory,
setSimpleSelection
} from "./selection_updates.js"
let nextDocId = 0
let Doc = function(text, mode, firstLine, lineSep, direction) {
let Doc = function (text, mode, firstLine, lineSep, direction) {
if (!(this instanceof Doc)) return new Doc(text, mode, firstLine, lineSep, direction)
if (firstLine == null) firstLine = 0
......@@ -50,62 +56,81 @@ Doc.prototype = createObj(BranchChunk.prototype, {
// argument, it calls that for each line in the document. With
// three, it iterates over the range given by the first two (with
// the second being non-inclusive).
iter: function(from, to, op) {
iter: function (from, to, op) {
if (op) this.iterN(from - this.first, to - from, op)
else this.iterN(this.first, this.first + this.size, from)
},
// Non-public interface for adding and removing lines.
insert: function(at, lines) {
insert: function (at, lines) {
let height = 0
for (let i = 0; i < lines.length; ++i) height += lines[i].height
this.insertInner(at - this.first, lines, height)
},
remove: function(at, n) { this.removeInner(at - this.first, n) },
remove: function (at, n) {
this.removeInner(at - this.first, n)
},
// From here, the methods are part of the public interface. Most
// are also available from CodeMirror (editor) instances.
getValue: function(lineSep) {
getValue: function (lineSep) {
let lines = getLines(this, this.first, this.first + this.size)
if (lineSep === false) return lines
return lines.join(lineSep || this.lineSeparator())
},
setValue: docMethodOp(function(code) {
setValue: docMethodOp(function (code) {
let top = Pos(this.first, 0), last = this.first + this.size - 1
makeChange(this, {from: top, to: Pos(last, getLine(this, last).text.length),
text: this.splitLines(code), origin: "setValue", full: true}, true)
makeChange(this, {
from: top, to: Pos(last, getLine(this, last).text.length),
text: this.splitLines(code), origin: "setValue", full: true
}, true)
if (this.cm) scrollToCoords(this.cm, 0, 0)
setSelection(this, simpleSelection(top), sel_dontScroll)
}),
replaceRange: function(code, from, to, origin) {
replaceRange: function (code, from, to, origin) {
from = clipPos(this, from)
to = to ? clipPos(this, to) : from
replaceRange(this, code, from, to, origin)
},
getRange: function(from, to, lineSep) {
getRange: function (from, to, lineSep) {
let lines = getBetween(this, clipPos(this, from), clipPos(this, to))
if (lineSep === false) return lines
return lines.join(lineSep || this.lineSeparator())
},
getLine: function(line) {let l = this.getLineHandle(line); return l && l.text},
getLine: function (line) {
let l = this.getLineHandle(line);
return l && l.text
},
getLineHandle: function(line) {if (isLine(this, line)) return getLine(this, line)},
getLineNumber: function(line) {return lineNo(line)},
getLineHandle: function (line) {
if (isLine(this, line)) return getLine(this, line)
},
getLineNumber: function (line) {
return lineNo(line)
},
getLineHandleVisualStart: function(line) {
getLineHandleVisualStart: function (line) {
if (typeof line == "number") line = getLine(this, line)
return visualLine(line)
},
lineCount: function() {return this.size},
firstLine: function() {return this.first},
lastLine: function() {return this.first + this.size - 1},
lineCount: function () {
return this.size
},
firstLine: function () {
return this.first
},
lastLine: function () {
return this.first + this.size - 1
},
clipPos: function(pos) {return clipPos(this, pos)},
clipPos: function (pos) {
return clipPos(this, pos)
},
getCursor: function(start) {
getCursor: function (start) {
let range = this.sel.primary(), pos
if (start == null || start == "head") pos = range.head
else if (start == "anchor") pos = range.anchor
......@@ -113,26 +138,30 @@ Doc.prototype = createObj(BranchChunk.prototype, {
else pos = range.from()
return pos
},
listSelections: function() { return this.sel.ranges },
somethingSelected: function() {return this.sel.somethingSelected()},
listSelections: function () {
return this.sel.ranges
},
somethingSelected: function () {
return this.sel.somethingSelected()
},
setCursor: docMethodOp(function(line, ch, options) {
setCursor: docMethodOp(function (line, ch, options) {
setSimpleSelection(this, clipPos(this, typeof line == "number" ? Pos(line, ch || 0) : line), null, options)
}),
setSelection: docMethodOp(function(anchor, head, options) {
setSelection: docMethodOp(function (anchor, head, options) {
setSimpleSelection(this, clipPos(this, anchor), clipPos(this, head || anchor), options)
}),
extendSelection: docMethodOp(function(head, other, options) {
extendSelection: docMethodOp(function (head, other, options) {
extendSelection(this, clipPos(this, head), other && clipPos(this, other), options)
}),
extendSelections: docMethodOp(function(heads, options) {
extendSelections: docMethodOp(function (heads, options) {
extendSelections(this, clipPosArray(this, heads), options)
}),
extendSelectionsBy: docMethodOp(function(f, options) {
extendSelectionsBy: docMethodOp(function (f, options) {
let heads = map(this.sel.ranges, f)
extendSelections(this, clipPosArray(this, heads), options)
}),
setSelections: docMethodOp(function(ranges, primary, options) {
setSelections: docMethodOp(function (ranges, primary, options) {
if (!ranges.length) return
let out = []
for (let i = 0; i < ranges.length; i++)
......@@ -141,13 +170,13 @@ Doc.prototype = createObj(BranchChunk.prototype, {
if (primary == null) primary = Math.min(ranges.length - 1, this.sel.primIndex)
setSelection(this, normalizeSelection(this.cm, out, primary), options)
}),
addSelection: docMethodOp(function(anchor, head, options) {
addSelection: docMethodOp(function (anchor, head, options) {
let ranges = this.sel.ranges.slice(0)
ranges.push(new Range(clipPos(this, anchor), clipPos(this, head || anchor)))
setSelection(this, normalizeSelection(this.cm, ranges, ranges.length - 1), options)
}),
getSelection: function(lineSep) {
getSelection: function (lineSep) {
let ranges = this.sel.ranges, lines
for (let i = 0; i < ranges.length; i++) {
let sel = getBetween(this, ranges[i].from(), ranges[i].to())
......@@ -156,7 +185,7 @@ Doc.prototype = createObj(BranchChunk.prototype, {
if (lineSep === false) return lines
else return lines.join(lineSep || this.lineSeparator())
},
getSelections: function(lineSep) {
getSelections: function (lineSep) {
let parts = [], ranges = this.sel.ranges
for (let i = 0; i < ranges.length; i++) {
let sel = getBetween(this, ranges[i].from(), ranges[i].to())
......@@ -165,13 +194,13 @@ Doc.prototype = createObj(BranchChunk.prototype, {
}
return parts
},
replaceSelection: function(code, collapse, origin) {
replaceSelection: function (code, collapse, origin) {
let dup = []
for (let i = 0; i < this.sel.ranges.length; i++)
dup[i] = code
this.replaceSelections(dup, collapse, origin || "+input")
},
replaceSelections: docMethodOp(function(code, collapse, origin) {
replaceSelections: docMethodOp(function (code, collapse, origin) {
let changes = [], sel = this.sel
for (let i = 0; i < sel.ranges.length; i++) {
let range = sel.ranges[i]
......@@ -183,29 +212,41 @@ Doc.prototype = createObj(BranchChunk.prototype, {
if (newSel) setSelectionReplaceHistory(this, newSel)
else if (this.cm) ensureCursorVisible(this.cm)
}),
undo: docMethodOp(function() {makeChangeFromHistory(this, "undo")}),
redo: docMethodOp(function() {makeChangeFromHistory(this, "redo")}),
undoSelection: docMethodOp(function() {makeChangeFromHistory(this, "undo", true)}),
redoSelection: docMethodOp(function() {makeChangeFromHistory(this, "redo", true)}),
undo: docMethodOp(function () {
makeChangeFromHistory(this, "undo")
}),
redo: docMethodOp(function () {
makeChangeFromHistory(this, "redo")
}),
undoSelection: docMethodOp(function () {
makeChangeFromHistory(this, "undo", true)
}),
redoSelection: docMethodOp(function () {
makeChangeFromHistory(this, "redo", true)
}),
setExtending: function(val) {this.extend = val},
getExtending: function() {return this.extend},
setExtending: function (val) {
this.extend = val
},
getExtending: function () {
return this.extend
},
historySize: function() {
historySize: function () {
let hist = this.history, done = 0, undone = 0
for (let i = 0; i < hist.done.length; i++) if (!hist.done[i].ranges) ++done
for (let i = 0; i < hist.undone.length; i++) if (!hist.undone[i].ranges) ++undone
return {undo: done, redo: undone}
},
clearHistory: function() {
clearHistory: function () {
this.history = new History(this.history.maxGeneration)
linkedDocs(this, doc => doc.history = this.history, true)
},
markClean: function() {
markClean: function () {
this.cleanGeneration = this.changeGeneration(true)
},
changeGeneration: function(forceSplit) {
changeGeneration: function (forceSplit) {
if (forceSplit)
this.history.lastOp = this.history.lastSelOp = this.history.lastOrigin = null
return this.history.generation
......@@ -214,17 +255,19 @@ Doc.prototype = createObj(BranchChunk.prototype, {
return this.history.generation == (gen || this.cleanGeneration)
},
getHistory: function() {
return {done: copyHistoryArray(this.history.done),
undone: copyHistoryArray(this.history.undone)}
getHistory: function () {
return {
done: copyHistoryArray(this.history.done),
undone: copyHistoryArray(this.history.undone)
}
},
setHistory: function(histData) {
setHistory: function (histData) {
let hist = this.history = new History(this.history.maxGeneration)
hist.done = copyHistoryArray(histData.done.slice(0), null, true)
hist.undone = copyHistoryArray(histData.undone.slice(0), null, true)
},
setGutterMarker: docMethodOp(function(line, gutterID, value) {
setGutterMarker: docMethodOp(function (line, gutterID, value) {
return changeLine(this, line, "gutter", line => {
let markers = line.gutterMarkers || (line.gutterMarkers = {})
markers[gutterID] = value
......@@ -233,7 +276,7 @@ Doc.prototype = createObj(BranchChunk.prototype, {
})
}),
clearGutter: docMethodOp(function(gutterID) {
clearGutter: docMethodOp(function (gutterID) {
this.iter(line => {
if (line.gutterMarkers && line.gutterMarkers[gutterID]) {
changeLine(this, line, "gutter", () => {
......@@ -245,7 +288,7 @@ Doc.prototype = createObj(BranchChunk.prototype, {
})
}),
lineInfo: function(line) {
lineInfo: function (line) {
let n
if (typeof line == "number") {
if (!isLine(this, line)) return null
......@@ -256,12 +299,14 @@ Doc.prototype = createObj(BranchChunk.prototype, {
n = lineNo(line)
if (n == null) return null
}
return {line: n, handle: line, text: line.text, gutterMarkers: line.gutterMarkers,
return {
line: n, handle: line, text: line.text, gutterMarkers: line.gutterMarkers,
textClass: line.textClass, bgClass: line.bgClass, wrapClass: line.wrapClass,
widgets: line.widgets}
widgets: line.widgets
}
},
addLineClass: docMethodOp(function(handle, where, cls) {
addLineClass: docMethodOp(function (handle, where, cls) {
return changeLine(this, handle, where == "gutter" ? "gutter" : "class", line => {
let prop = where == "text" ? "textClass"
: where == "background" ? "bgClass"
......@@ -272,7 +317,7 @@ Doc.prototype = createObj(BranchChunk.prototype, {
return true
})
}),
removeLineClass: docMethodOp(function(handle, where, cls) {
removeLineClass: docMethodOp(function (handle, where, cls) {
return changeLine(this, handle, where == "gutter" ? "gutter" : "class", line => {
let prop = where == "text" ? "textClass"
: where == "background" ? "bgClass"
......@@ -290,23 +335,27 @@ Doc.prototype = createObj(BranchChunk.prototype, {
})
}),
addLineWidget: docMethodOp(function(handle, node, options) {
addLineWidget: docMethodOp(function (handle, node, options) {
return addLineWidget(this, handle, node, options)
}),
removeLineWidget: function(widget) { widget.clear() },
removeLineWidget: function (widget) {
widget.clear()
},
markText: function(from, to, options) {
markText: function (from, to, options) {
return markText(this, clipPos(this, from), clipPos(this, to), options, options && options.type || "range")
},
setBookmark: function(pos, options) {
let realOpts = {replacedWith: options && (options.nodeType == null ? options.widget : options),
setBookmark: function (pos, options) {
let realOpts = {
replacedWith: options && (options.nodeType == null ? options.widget : options),
insertLeft: options && options.insertLeft,
clearWhenEmpty: false, shared: options && options.shared,
handleMouseEvents: options && options.handleMouseEvents}
handleMouseEvents: options && options.handleMouseEvents
}
pos = clipPos(this, pos)
return markText(this, pos, pos, realOpts, "bookmark")
},
findMarksAt: function(pos) {
findMarksAt: function (pos) {
pos = clipPos(this, pos)
let markers = [], spans = getLine(this, pos.line).markedSpans
if (spans) for (let i = 0; i < spans.length; ++i) {
......@@ -317,8 +366,9 @@ Doc.prototype = createObj(BranchChunk.prototype, {
}
return markers
},
findMarks: function(from, to, filter) {
from = clipPos(this, from); to = clipPos(this, to)
findMarks: function (from, to, filter) {
from = clipPos(this, from);
to = clipPos(this, to)
let found = [], lineNo = from.line
this.iter(from.line, to.line + 1, line => {
let spans = line.markedSpans
......@@ -334,7 +384,7 @@ Doc.prototype = createObj(BranchChunk.prototype, {
})
return found
},
getAllMarks: function() {
getAllMarks: function () {
let markers = []
this.iter(line => {
let sps = line.markedSpans
......@@ -344,11 +394,14 @@ Doc.prototype = createObj(BranchChunk.prototype, {
return markers
},
posFromIndex: function(off) {
posFromIndex: function (off) {
let ch, lineNo = this.first, sepSize = this.lineSeparator().length
this.iter(line => {
let sz = line.text.length + sepSize
if (sz > off) { ch = off; return true }
if (sz > off) {
ch = off;
return true
}
off -= sz
++lineNo
})
......@@ -365,10 +418,11 @@ Doc.prototype = createObj(BranchChunk.prototype, {
return index
},
copy: function(copyHistory) {
copy: function (copyHistory) {
let doc = new Doc(getLines(this, this.first, this.first + this.size),
this.modeOption, this.first, this.lineSep, this.direction)
doc.scrollTop = this.scrollTop; doc.scrollLeft = this.scrollLeft
doc.scrollTop = this.scrollTop;
doc.scrollLeft = this.scrollLeft
doc.sel = this.sel
doc.extend = false
if (copyHistory) {
......@@ -378,19 +432,20 @@ Doc.prototype = createObj(BranchChunk.prototype, {
return doc
},
linkedDoc: function(options) {
linkedDoc: function (options) {
if (!options) options = {}
let from = this.first, to = this.first + this.size
if (options.from != null && options.from > from) from = options.from
if (options.to != null && options.to < to) to = options.to
let copy = new Doc(getLines(this, from, to), options.mode || this.modeOption, from, this.lineSep, this.direction)
if (options.sharedHist) copy.history = this.history
;(this.linked || (this.linked = [])).push({doc: copy, sharedHist: options.sharedHist})
;
(this.linked || (this.linked = [])).push({doc: copy, sharedHist: options.sharedHist})
copy.linked = [{doc: this, isParent: true, sharedHist: options.sharedHist}]
copySharedMarkers(copy, findSharedMarkers(this))
return copy
},
unlinkDoc: function(other) {
unlinkDoc: function (other) {
if (other instanceof CodeMirror) other = other.doc
if (this.linked) for (let i = 0; i < this.linked.length; ++i) {
let link = this.linked[i]
......@@ -409,16 +464,24 @@ Doc.prototype = createObj(BranchChunk.prototype, {
other.history.undone = copyHistoryArray(this.history.undone, splitIds)
}
},
iterLinkedDocs: function(f) {linkedDocs(this, f)},
iterLinkedDocs: function (f) {
linkedDocs(this, f)
},
getMode: function() {return this.mode},
getEditor: function() {return this.cm},
getMode: function () {
return this.mode
},
getEditor: function () {
return this.cm
},
splitLines: function(str) {
splitLines: function (str) {
if (this.lineSep) return str.split(this.lineSep)
return splitLinesAuto(str)
},
lineSeparator: function() { return this.lineSep || "\n" },
lineSeparator: function () {
return this.lineSep || "\n"
},
setDirection: docMethodOp(function (dir) {
if (dir != "rtl") dir = "ltr"
......
import { cmp, Pos } from "../line/pos.js"
import { lst } from "../util/misc.js"
import {cmp, Pos} from "../line/pos.js"
import {lst} from "../util/misc.js"
import { normalizeSelection, Range, Selection } from "./selection.js"
import {normalizeSelection, Range, Selection} from "./selection.js"
// Compute the position of the end of a change (its 'to' property
// refers to the pre-change end).
......
import { retreatFrontier } from "../line/highlight.js"
import { startWorker } from "../display/highlight_worker.js"
import { operation } from "../display/operations.js"
import { regChange, regLineChange } from "../display/view_tracking.js"
import { clipLine, clipPos, cmp, Pos } from "../line/pos.js"
import { sawReadOnlySpans } from "../line/saw_special_spans.js"
import { lineLength, removeReadOnlyRanges, stretchSpansOverChange, visualLine } from "../line/spans.js"
import { getBetween, getLine, lineNo } from "../line/utils_line.js"
import { estimateHeight } from "../measurement/position_measurement.js"
import { hasHandler, signal, signalCursorActivity } from "../util/event.js"
import { indexOf, lst, map, sel_dontScroll } from "../util/misc.js"
import { signalLater } from "../util/operation_group.js"
import { changeEnd, computeSelAfterChange } from "./change_measurement.js"
import { isWholeLineUpdate, linkedDocs, updateDoc } from "./document_data.js"
import { addChangeToHistory, historyChangeFromChange, mergeOldSpans, pushSelectionToHistory } from "./history.js"
import { Range, Selection } from "./selection.js"
import { setSelection, setSelectionNoUndo, skipAtomic } from "./selection_updates.js"
import {retreatFrontier} from "../line/highlight.js"
import {startWorker} from "../display/highlight_worker.js"
import {operation} from "../display/operations.js"
import {regChange, regLineChange} from "../display/view_tracking.js"
import {clipLine, clipPos, cmp, Pos} from "../line/pos.js"
import {sawReadOnlySpans} from "../line/saw_special_spans.js"
import {lineLength, removeReadOnlyRanges, stretchSpansOverChange, visualLine} from "../line/spans.js"
import {getBetween, getLine, lineNo} from "../line/utils_line.js"
import {estimateHeight} from "../measurement/position_measurement.js"
import {hasHandler, signal, signalCursorActivity} from "../util/event.js"
import {indexOf, lst, map, sel_dontScroll} from "../util/misc.js"
import {signalLater} from "../util/operation_group.js"
import {changeEnd, computeSelAfterChange} from "./change_measurement.js"
import {isWholeLineUpdate, linkedDocs, updateDoc} from "./document_data.js"
import {addChangeToHistory, historyChangeFromChange, mergeOldSpans, pushSelectionToHistory} from "./history.js"
import {Range, Selection} from "./selection.js"
import {setSelection, setSelectionNoUndo, skipAtomic} from "./selection_updates.js"
// UPDATING
......@@ -105,7 +105,7 @@ export function makeChangeFromHistory(doc, type, allowSelectionOnly) {
if (i == source.length) return
hist.lastOrigin = hist.lastSelOrigin = null
for (;;) {
for (; ;) {
event = source.pop()
if (event.ranges) {
pushSelectionToHistory(event, dest)
......@@ -187,13 +187,17 @@ function makeChangeSingleDoc(doc, change, selAfter, spans) {
if (change.from.line < doc.first) {
let shift = change.text.length - 1 - (doc.first - change.from.line)
shiftDoc(doc, shift)
change = {from: Pos(doc.first, 0), to: Pos(change.to.line + shift, change.to.ch),
text: [lst(change.text)], origin: change.origin}
change = {
from: Pos(doc.first, 0), to: Pos(change.to.line + shift, change.to.ch),
text: [lst(change.text)], origin: change.origin
}
}
let last = doc.lastLine()
if (change.to.line > last) {
change = {from: change.from, to: Pos(last, getLine(doc, last).text.length),
text: [change.text[0]], origin: change.origin}
change = {
from: change.from, to: Pos(last, getLine(doc, last).text.length),
text: [change.text[0]], origin: change.origin
}
}
change.removed = getBetween(doc, change.from, change.to)
......@@ -296,7 +300,10 @@ function rebaseHistArray(array, from, to, diff) {
for (let i = 0; i < array.length; ++i) {
let sub = array[i], ok = true
if (sub.ranges) {
if (!sub.copied) { sub = array[i] = sub.deepCopy(); sub.copied = true }
if (!sub.copied) {
sub = array[i] = sub.deepCopy();
sub.copied = true
}
for (let j = 0; j < sub.ranges.length; j++) {
rebaseHistSelSingle(sub.ranges[j].anchor, from, to, diff)
rebaseHistSelSingle(sub.ranges[j].head, from, to, diff)
......
import { cleanUpLine } from "../line/line_data.js"
import { indexOf } from "../util/misc.js"
import { signalLater } from "../util/operation_group.js"
import {cleanUpLine} from "../line/line_data.js"
import {indexOf} from "../util/misc.js"
import {signalLater} from "../util/operation_group.js"
// The document is represented as a BTree consisting of leaves, with
// chunk of lines in them, and branches, with up to ten leaves or
......@@ -27,7 +27,9 @@ export function LeafChunk(lines) {
}
LeafChunk.prototype = {
chunkSize() { return this.lines.length },
chunkSize() {
return this.lines.length
},
// Remove the n lines at offset 'at'.
removeInner(at, n) {
......@@ -65,7 +67,8 @@ export function BranchChunk(children) {
let size = 0, height = 0
for (let i = 0; i < children.length; ++i) {
let ch = children[i]
size += ch.chunkSize(); height += ch.height
size += ch.chunkSize();
height += ch.height
ch.parent = this
}
this.size = size
......@@ -74,7 +77,9 @@ export function BranchChunk(children) {
}
BranchChunk.prototype = {
chunkSize() { return this.size },
chunkSize() {
return this.size
},
removeInner(at, n) {
this.size -= n
......@@ -84,7 +89,10 @@ BranchChunk.prototype = {
let rm = Math.min(n, sz - at), oldHeight = child.height
child.removeInner(at, rm)
this.height -= oldHeight - child.height
if (sz == rm) { this.children.splice(i--, 1); child.parent = null }
if (sz == rm) {
this.children.splice(i--, 1);
child.parent = null
}
if ((n -= rm) == 0) break
at = 0
} else at -= sz
......
import { loadMode } from "../display/mode_state.js"
import { runInOp } from "../display/operations.js"
import { regChange } from "../display/view_tracking.js"
import { Line, updateLine } from "../line/line_data.js"
import { findMaxLine } from "../line/spans.js"
import { getLine } from "../line/utils_line.js"
import { estimateLineHeights } from "../measurement/position_measurement.js"
import { addClass, rmClass } from "../util/dom.js"
import { lst } from "../util/misc.js"
import { signalLater } from "../util/operation_group.js"
import {loadMode} from "../display/mode_state.js"
import {runInOp} from "../display/operations.js"
import {regChange} from "../display/view_tracking.js"
import {Line, updateLine} from "../line/line_data.js"
import {findMaxLine} from "../line/spans.js"
import {getLine} from "../line/utils_line.js"
import {estimateLineHeights} from "../measurement/position_measurement.js"
import {addClass, rmClass} from "../util/dom.js"
import {lst} from "../util/misc.js"
import {signalLater} from "../util/operation_group.js"
// DOCUMENT DATA STRUCTURE
......@@ -21,11 +21,15 @@ export function isWholeLineUpdate(doc, change) {
// Perform a change on the document data structure.
export function updateDoc(doc, change, markedSpans, estimateHeight) {
function spansFor(n) {return markedSpans ? markedSpans[n] : null}
function spansFor(n) {
return markedSpans ? markedSpans[n] : null
}
function update(line, text, spans) {
updateLine(line, text, spans, estimateHeight)
signalLater(line, "change", line, change)
}
function linesFor(start, end) {
let result = []
for (let i = start; i < end; ++i)
......@@ -83,6 +87,7 @@ export function linkedDocs(doc, f, sharedHistOnly) {
propagate(rel.doc, doc, shared)
}
}
propagate(doc, null, true)
}
......
import { cmp, copyPos } from "../line/pos.js"
import { stretchSpansOverChange } from "../line/spans.js"
import { getBetween } from "../line/utils_line.js"
import { signal } from "../util/event.js"
import { indexOf, lst } from "../util/misc.js"
import {cmp, copyPos} from "../line/pos.js"
import {stretchSpansOverChange} from "../line/spans.js"
import {getBetween} from "../line/utils_line.js"
import {signal} from "../util/event.js"
import {indexOf, lst} from "../util/misc.js"
import { changeEnd } from "./change_measurement.js"
import { linkedDocs } from "./document_data.js"
import { Selection } from "./selection.js"
import {changeEnd} from "./change_measurement.js"
import {linkedDocs} from "./document_data.js"
import {Selection} from "./selection.js"
export function History(startGen) {
// Arrays of change events and selections. Doing something adds an
// event to done and clears undo. Undoing moves events from done
// to undone, redoing moves them in the other direction.
this.done = []; this.undone = []
this.done = [];
this.undone = []
this.undoDepth = Infinity
// Used to track when changes can be merged into a single undo
// event
......@@ -85,8 +86,10 @@ export function addChangeToHistory(doc, change, selAfter, opId) {
let before = lst(hist.done)
if (!before || !before.ranges)
pushSelectionToHistory(doc.sel, hist.done)
cur = {changes: [historyChangeFromChange(doc, change)],
generation: hist.generation}
cur = {
changes: [historyChangeFromChange(doc, change)],
generation: hist.generation
}
hist.done.push(cur)
while (hist.done.length > hist.undoDepth) {
hist.done.shift()
......@@ -159,8 +162,9 @@ function removeClearedSpans(spans) {
if (!spans) return null
let out
for (let i = 0; i < spans.length; ++i) {
if (spans[i].marker.explicitlyCleared) { if (!out) out = spans.slice(0, i) }
else if (out) out.push(spans[i])
if (spans[i].marker.explicitlyCleared) {
if (!out) out = spans.slice(0, i)
} else if (out) out.push(spans[i])
}
return !out ? spans : out.length ? out : null
}
......
import { runInOp } from "../display/operations.js"
import { addToScrollTop } from "../display/scrolling.js"
import { regLineChange } from "../display/view_tracking.js"
import { heightAtLine, lineIsHidden } from "../line/spans.js"
import { lineNo, updateLineHeight } from "../line/utils_line.js"
import { widgetHeight } from "../measurement/widgets.js"
import { changeLine } from "./changes.js"
import { eventMixin } from "../util/event.js"
import { signalLater } from "../util/operation_group.js"
import {runInOp} from "../display/operations.js"
import {addToScrollTop} from "../display/scrolling.js"
import {regLineChange} from "../display/view_tracking.js"
import {heightAtLine, lineIsHidden} from "../line/spans.js"
import {lineNo, updateLineHeight} from "../line/utils_line.js"
import {widgetHeight} from "../measurement/widgets.js"
import {changeLine} from "./changes.js"
import {eventMixin} from "../util/event.js"
import {signalLater} from "../util/operation_group.js"
// Line widgets are block elements displayed above or below a line.
......@@ -49,6 +49,7 @@ export class LineWidget {
}
}
}
eventMixin(LineWidget)
function adjustScrollWhenAboveVisible(cm, line, diff) {
......
import { eltP } from "../util/dom.js"
import { eventMixin, hasHandler, on } from "../util/event.js"
import { endOperation, operation, runInOp, startOperation } from "../display/operations.js"
import { clipPos, cmp, Pos } from "../line/pos.js"
import { lineNo, updateLineHeight } from "../line/utils_line.js"
import { clearLineMeasurementCacheFor, findViewForLine, textHeight } from "../measurement/position_measurement.js"
import { seeReadOnlySpans, seeCollapsedSpans } from "../line/saw_special_spans.js"
import { addMarkedSpan, conflictingCollapsedRange, getMarkedSpanFor, lineIsHidden, lineLength, MarkedSpan, removeMarkedSpan, visualLine } from "../line/spans.js"
import { copyObj, indexOf, lst } from "../util/misc.js"
import { signalLater } from "../util/operation_group.js"
import { widgetHeight } from "../measurement/widgets.js"
import { regChange, regLineChange } from "../display/view_tracking.js"
import {eltP} from "../util/dom.js"
import {eventMixin, hasHandler, on} from "../util/event.js"
import {endOperation, operation, runInOp, startOperation} from "../display/operations.js"
import {clipPos, cmp, Pos} from "../line/pos.js"
import {lineNo, updateLineHeight} from "../line/utils_line.js"
import {clearLineMeasurementCacheFor, findViewForLine, textHeight} from "../measurement/position_measurement.js"
import {seeCollapsedSpans, seeReadOnlySpans} from "../line/saw_special_spans.js"
import {
addMarkedSpan,
conflictingCollapsedRange,
getMarkedSpanFor,
lineIsHidden,
lineLength,
MarkedSpan,
removeMarkedSpan,
visualLine
} from "../line/spans.js"
import {copyObj, indexOf, lst} from "../util/misc.js"
import {signalLater} from "../util/operation_group.js"
import {widgetHeight} from "../measurement/widgets.js"
import {regChange, regLineChange} from "../display/view_tracking.js"
import { linkedDocs } from "./document_data.js"
import { addChangeToHistory } from "./history.js"
import { reCheckSelection } from "./selection_updates.js"
import {linkedDocs} from "./document_data.js"
import {addChangeToHistory} from "./history.js"
import {reCheckSelection} from "./selection_updates.js"
// TEXTMARKERS
......@@ -147,6 +156,7 @@ export class TextMarker {
}
}
}
eventMixin(TextMarker)
// Create a marker, wire it up to the right lines, and
......@@ -245,6 +255,7 @@ export class SharedTextMarker {
return this.primary.find(side, lineObj)
}
}
eventMixin(SharedTextMarker)
function markTextShared(doc, from, to, options, type) {
......
import { cmp, copyPos, equalCursorPos, maxPos, minPos } from "../line/pos.js"
import { indexOf } from "../util/misc.js"
import {cmp, copyPos, equalCursorPos, maxPos, minPos} from "../line/pos.js"
import {indexOf} from "../util/misc.js"
// Selection objects are immutable. A new one is created every time
// the selection changes. A selection is one or more non-overlapping
......@@ -12,7 +12,9 @@ export class Selection {
this.primIndex = primIndex
}
primary() { return this.ranges[this.primIndex] }
primary() {
return this.ranges[this.primIndex]
}
equals(other) {
if (other == this) return true
......@@ -50,12 +52,21 @@ export class Selection {
export class Range {
constructor(anchor, head) {
this.anchor = anchor; this.head = head
this.anchor = anchor;
this.head = head
}
from() {
return minPos(this.anchor, this.head)
}
from() { return minPos(this.anchor, this.head) }
to() { return maxPos(this.anchor, this.head) }
empty() { return this.head.line == this.anchor.line && this.head.ch == this.anchor.ch }
to() {
return maxPos(this.anchor, this.head)
}
empty() {
return this.head.line == this.anchor.line && this.head.ch == this.anchor.ch
}
}
// Take an unsorted, potentially overlapping set of ranges, and
......
import { signalLater } from "../util/operation_group.js"
import { ensureCursorVisible } from "../display/scrolling.js"
import { clipPos, cmp, Pos } from "../line/pos.js"
import { getLine } from "../line/utils_line.js"
import { hasHandler, signal, signalCursorActivity } from "../util/event.js"
import { lst, sel_dontScroll } from "../util/misc.js"
import {signalLater} from "../util/operation_group.js"
import {ensureCursorVisible} from "../display/scrolling.js"
import {clipPos, cmp, Pos} from "../line/pos.js"
import {getLine} from "../line/utils_line.js"
import {hasHandler, signal, signalCursorActivity} from "../util/event.js"
import {lst, sel_dontScroll} from "../util/misc.js"
import { addSelectionToHistory } from "./history.js"
import { normalizeSelection, Range, Selection, simpleSelection } from "./selection.js"
import {addSelectionToHistory} from "./history.js"
import {normalizeSelection, Range, Selection, simpleSelection} from "./selection.js"
// The 'scroll' parameter given to many of these indicated whether
// the new cursor position should be scrolled into view after
......@@ -68,7 +68,7 @@ export function setSimpleSelection(doc, anchor, head, options) {
function filterSelectionChange(doc, sel, options) {
let obj = {
ranges: sel.ranges,
update: function(ranges) {
update: function (ranges) {
this.ranges = []
for (let i = 0; i < ranges.length; i++)
this.ranges[i] = new Range(clipPos(doc, ranges[i].anchor),
......@@ -163,7 +163,10 @@ function skipAtomicInner(doc, pos, oldPos, dir, mayClear) {
signal(m, "beforeCursorEnter")
if (m.explicitlyCleared) {
if (!line.markedSpans) break
else {--i; continue}
else {
--i;
continue
}
}
}
if (!m.atomic) continue
......
import { copyObj, createObj } from "./util/misc.js"
import {copyObj, createObj} from "./util/misc.js"
// Known modes, by name and by MIME
export let modes = {}, mimeModes = {}
......@@ -61,6 +61,7 @@ export function getMode(options, spec) {
// This can be used to attach properties to mode objects from
// outside the actual mode definition.
export let modeExtensions = {}
export function extendMode(mode, properties) {
let exts = modeExtensions.hasOwnProperty(mode) ? modeExtensions[mode] : (modeExtensions[mode] = {})
copyObj(properties, exts)
......
import { countColumn } from "./misc.js"
import {countColumn} from "./misc.js"
// STRING STREAM
......@@ -15,36 +15,63 @@ class StringStream {
this.lineOracle = lineOracle
}
eol() {return this.pos >= this.string.length}
sol() {return this.pos == this.lineStart}
peek() {return this.string.charAt(this.pos) || undefined}
eol() {
return this.pos >= this.string.length
}
sol() {
return this.pos == this.lineStart
}
peek() {
return this.string.charAt(this.pos) || undefined
}
next() {
if (this.pos < this.string.length)
return this.string.charAt(this.pos++)
}
eat(match) {
let ch = this.string.charAt(this.pos)
let ok
if (typeof match == "string") ok = ch == match
else ok = ch && (match.test ? match.test(ch) : match(ch))
if (ok) {++this.pos; return ch}
if (ok) {
++this.pos;
return ch
}
}
eatWhile(match) {
let start = this.pos
while (this.eat(match)){}
while (this.eat(match)) {
}
return this.pos > start
}
eatSpace() {
let start = this.pos
while (/[\s\u00a0]/.test(this.string.charAt(this.pos))) ++this.pos
return this.pos > start
}
skipToEnd() {this.pos = this.string.length}
skipToEnd() {
this.pos = this.string.length
}
skipTo(ch) {
let found = this.string.indexOf(ch, this.pos)
if (found > -1) {this.pos = found; return true}
if (found > -1) {
this.pos = found;
return true
}
}
backUp(n) {
this.pos -= n
}
backUp(n) {this.pos -= n}
column() {
if (this.lastColumnPos < this.start) {
this.lastColumnValue = countColumn(this.string, this.start, this.tabSize, this.lastColumnPos, this.lastColumnValue)
......@@ -52,10 +79,12 @@ class StringStream {
}
return this.lastColumnValue - (this.lineStart ? countColumn(this.string, this.lineStart, this.tabSize) : 0)
}
indentation() {
return countColumn(this.string, null, this.tabSize) -
(this.lineStart ? countColumn(this.string, this.lineStart, this.tabSize) : 0)
}
match(pattern, consume, caseInsensitive) {
if (typeof pattern == "string") {
let cased = str => caseInsensitive ? str.toLowerCase() : str
......@@ -71,16 +100,25 @@ class StringStream {
return match
}
}
current(){return this.string.slice(this.start, this.pos)}
current() {
return this.string.slice(this.start, this.pos)
}
hideFirstChars(n, inner) {
this.lineStart += n
try { return inner() }
finally { this.lineStart -= n }
try {
return inner()
} finally {
this.lineStart -= n
}
}
lookAhead(n) {
let oracle = this.lineOracle
return oracle && oracle.lookAhead(n)
}
baseToken() {
let oracle = this.lineOracle
return oracle && oracle.baseToken(this.pos)
......
import { lst } from "./misc.js"
import {lst} from "./misc.js"
// BIDI HELPERS
......@@ -16,6 +16,7 @@ export function iterateBidiSections(order, from, to, f) {
}
export let bidiOther = null
export function getBidiPartAt(order, ch, sticky) {
let found
bidiOther = null
......@@ -57,11 +58,12 @@ export function getBidiPartAt(order, ch, sticky) {
// Returns null if characters are ordered as they appear
// (left-to-right), or an array of sections ({from, to, level}
// objects) in the order in which they occur visually.
let bidiOrdering = (function() {
let bidiOrdering = (function () {
// Character types for codepoints 0 to 0xff
let lowTypes = "bbbbbbbbbtstwsbbbbbbbbbbbbbbssstwNN%%%NNNNNN,N,N1111111111NNNNNNNLLLLLLLLLLLLLLLLLLLLLLLLLLNNNNNNLLLLLLLLLLLLLLLLLLLLLLLLLLNNNNbbbbbbsbbbbbbbbbbbbbbbbbbbbbbbbbb,N%%%%NNNNLNNNNN%%11NLNNN1LNNNNNLLLLLLLLLLLLLLLLLLLLLLLNLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLN"
// Character types for codepoints 0x600 to 0x6f9
let arabicTypes = "nnnnnnNNr%%r,rNNmmmmmmmmmmmrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrmmmmmmmmmmmmmmmmmmmmmnnnnnnnnnn%nnrrrmrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrmmmmmmmnNmmmmmmrrmmNmmmmrr1111111111"
function charType(code) {
if (code <= 0xf7) return lowTypes.charAt(code)
else if (0x590 <= code && code <= 0x5f4) return "R"
......@@ -77,10 +79,11 @@ let bidiOrdering = (function() {
function BidiSpan(level, from, to) {
this.level = level
this.from = from; this.to = to
this.from = from;
this.to = to
}
return function(str, direction) {
return function (str, direction) {
let outerType = direction == "ltr" ? "L" : "R"
if (str.length == 0 || direction == "ltr" && !bidiRE.test(str)) return false
......@@ -106,7 +109,10 @@ let bidiOrdering = (function() {
for (let i = 0, cur = outerType; i < len; ++i) {
let type = types[i]
if (type == "1" && cur == "r") types[i] = "n"
else if (isStrong.test(type)) { cur = type; if (type == "r") types[i] = "R" }
else if (isStrong.test(type)) {
cur = type;
if (type == "r") types[i] = "R"
}
}
// W4. A single European separator between two European numbers
......@@ -114,8 +120,8 @@ let bidiOrdering = (function() {
// two numbers of the same type changes to that type.
for (let i = 1, prev = types[0]; i < len - 1; ++i) {
let type = types[i]
if (type == "+" && prev == "1" && types[i+1] == "1") types[i] = "1"
else if (type == "," && prev == types[i+1] &&
if (type == "+" && prev == "1" && types[i + 1] == "1") types[i] = "1"
else if (type == "," && prev == types[i + 1] &&
(prev == "1" || prev == "n")) types[i] = prev
prev = type
}
......@@ -129,8 +135,9 @@ let bidiOrdering = (function() {
if (type == ",") types[i] = "N"
else if (type == "%") {
let end
for (end = i + 1; end < len && types[end] == "%"; ++end) {}
let replace = (i && types[i-1] == "!") || (end < len && types[end] == "1") ? "1" : "N"
for (end = i + 1; end < len && types[end] == "%"; ++end) {
}
let replace = (i && types[i - 1] == "!") || (end < len && types[end] == "1") ? "1" : "N"
for (let j = i; j < end; ++j) types[j] = replace
i = end - 1
}
......@@ -154,8 +161,9 @@ let bidiOrdering = (function() {
for (let i = 0; i < len; ++i) {
if (isNeutral.test(types[i])) {
let end
for (end = i + 1; end < len && isNeutral.test(types[end]); ++end) {}
let before = (i ? types[i-1] : outerType) == "L"
for (end = i + 1; end < len && isNeutral.test(types[end]); ++end) {
}
let before = (i ? types[i - 1] : outerType) == "L"
let after = (end < len ? types[end] : outerType) == "L"
let replace = before == after ? (before ? "L" : "R") : outerType
for (let j = i; j < end; ++j) types[j] = replace
......@@ -172,16 +180,22 @@ let bidiOrdering = (function() {
for (let i = 0; i < len;) {
if (countsAsLeft.test(types[i])) {
let start = i
for (++i; i < len && countsAsLeft.test(types[i]); ++i) {}
for (++i; i < len && countsAsLeft.test(types[i]); ++i) {
}
order.push(new BidiSpan(0, start, i))
} else {
let pos = i, at = order.length, isRTL = direction == "rtl" ? 1 : 0
for (++i; i < len && types[i] != "L"; ++i) {}
for (++i; i < len && types[i] != "L"; ++i) {
}
for (let j = pos; j < i;) {
if (countsAsNum.test(types[j])) {
if (pos < j) { order.splice(at, 0, new BidiSpan(1, pos, j)); at += isRTL }
if (pos < j) {
order.splice(at, 0, new BidiSpan(1, pos, j));
at += isRTL
}
let nstart = j
for (++j; j < i && countsAsNum.test(types[j]); ++j) {}
for (++j; j < i && countsAsNum.test(types[j]); ++j) {
}
order.splice(at, 0, new BidiSpan(2, nstart, j))
at += isRTL
pos = j
......
......@@ -27,7 +27,10 @@ export let windows = /win/i.test(platform)
let presto_version = presto && userAgent.match(/Version\/(\d*\.\d*)/)
if (presto_version) presto_version = Number(presto_version[1])
if (presto_version && presto_version >= 15) { presto = false; webkit = true }
if (presto_version && presto_version >= 15) {
presto = false;
webkit = true
}
// Some browsers use the wrong event properties to signal cmd/ctrl on OS X
export let flipCtrlCmd = mac && (qtwebkit || presto && (presto_version == null || presto_version < 12.11))
export let captureRightClick = gecko || (ie && ie_version >= 9)
import { ie, ios } from "./browser.js"
import {ie, ios} from "./browser.js"
export function classTest(cls) { return new RegExp("(^|\\s)" + cls + "(?:$|\\s)\\s*") }
export function classTest(cls) {
return new RegExp("(^|\\s)" + cls + "(?:$|\\s)\\s*")
}
export let rmClass = function(node, cls) {
export let rmClass = function (node, cls) {
let current = node.className
let match = classTest(cls).exec(current)
if (match) {
......@@ -29,6 +31,7 @@ export function elt(tag, content, className, style) {
else if (content) for (let i = 0; i < content.length; ++i) e.appendChild(content[i])
return e
}
// wrapper for elt, which removes the elt from the accessibility tree
export function eltP(tag, content, className, style) {
let e = elt(tag, content, className, style)
......@@ -37,16 +40,19 @@ export function eltP(tag, content, className, style) {
}
export let range
if (document.createRange) range = function(node, start, end, endNode) {
if (document.createRange) range = function (node, start, end, endNode) {
let r = document.createRange()
r.setEnd(endNode || node, end)
r.setStart(node, start)
return r
}
else range = function(node, start, end) {
else range = function (node, start, end) {
let r = document.body.createTextRange()
try { r.moveToElementText(node.parentNode) }
catch(e) { return r }
try {
r.moveToElementText(node.parentNode)
} catch (e) {
return r
}
r.collapse(true)
r.moveEnd("character", end)
r.moveStart("character", start)
......@@ -71,7 +77,7 @@ export function activeElt() {
let activeElement
try {
activeElement = document.activeElement
} catch(e) {
} catch (e) {
activeElement = document.body || null
}
while (activeElement && activeElement.shadowRoot && activeElement.shadowRoot.activeElement)
......@@ -83,6 +89,7 @@ export function addClass(node, cls) {
let current = node.className
if (!classTest(cls).test(current)) node.className += (current ? " " : "") + cls
}
export function joinClasses(a, b) {
let as = a.split(" ")
for (let i = 0; i < as.length; i++)
......@@ -90,8 +97,18 @@ export function joinClasses(a, b) {
return b
}
export let selectInput = function(node) { node.select() }
export let selectInput = function (node) {
node.select()
}
if (ios) // Mobile Safari apparently has a bug where select() is broken.
selectInput = function(node) { node.selectionStart = 0; node.selectionEnd = node.value.length }
selectInput = function (node) {
node.selectionStart = 0;
node.selectionEnd = node.value.length
}
else if (ie) // Suppress mysterious IE10 errors
selectInput = function(node) { try { node.select() } catch(_e) {} }
selectInput = function (node) {
try {
node.select()
} catch (_e) {
}
}
import { mac } from "./browser.js"
import { indexOf } from "./misc.js"
import {mac} from "./browser.js"
import {indexOf} from "./misc.js"
// EVENT HANDLING
......@@ -8,7 +8,7 @@ import { indexOf } from "./misc.js"
const noHandlers = []
export let on = function(emitter, type, f) {
export let on = function (emitter, type, f) {
if (emitter.addEventListener) {
emitter.addEventListener(type, f, false)
} else if (emitter.attachEvent) {
......@@ -50,7 +50,11 @@ export function signal(emitter, type /*, values...*/) {
// and preventDefault-ing the event in that handler.
export function signalDOMEvent(cm, e, override) {
if (typeof e == "string")
e = {type: e, preventDefault: function() { this.defaultPrevented = true }}
e = {
type: e, preventDefault: function () {
this.defaultPrevented = true
}
}
signal(cm, override || e.type, cm, e)
return e_defaultPrevented(e) || e.codemirrorIgnore
}
......@@ -70,8 +74,12 @@ export function hasHandler(emitter, type) {
// Add on and off methods to a constructor's prototype, to make
// registering events on such objects more convenient.
export function eventMixin(ctor) {
ctor.prototype.on = function(type, f) {on(this, type, f)}
ctor.prototype.off = function(type, f) {off(this, type, f)}
ctor.prototype.on = function (type, f) {
on(this, type, f)
}
ctor.prototype.off = function (type, f) {
off(this, type, f)
}
}
// Due to the fact that we still support jurassic IE versions, some
......@@ -81,16 +89,25 @@ export function e_preventDefault(e) {
if (e.preventDefault) e.preventDefault()
else e.returnValue = false
}
export function e_stopPropagation(e) {
if (e.stopPropagation) e.stopPropagation()
else e.cancelBubble = true
}
export function e_defaultPrevented(e) {
return e.defaultPrevented != null ? e.defaultPrevented : e.returnValue == false
}
export function e_stop(e) {e_preventDefault(e); e_stopPropagation(e)}
export function e_target(e) {return e.target || e.srcElement}
export function e_stop(e) {
e_preventDefault(e);
e_stopPropagation(e)
}
export function e_target(e) {
return e.target || e.srcElement
}
export function e_button(e) {
let b = e.which
if (b == null) {
......
import { elt, range, removeChildren, removeChildrenAndAdd } from "./dom.js"
import { ie, ie_version } from "./browser.js"
import {elt, range, removeChildren, removeChildrenAndAdd} from "./dom.js"
import {ie, ie_version} from "./browser.js"
// Detect drag-and-drop
export let dragAndDrop = function() {
export let dragAndDrop = function () {
// There is *some* kind of drag-and-drop support in IE6-8, but I
// couldn't get it to work yet.
if (ie && ie_version < 9) return false
......@@ -11,6 +11,7 @@ export let dragAndDrop = function() {
}()
let zwspSupported
export function zeroWidthElement(measure) {
if (zwspSupported == null) {
let test = elt("span", "\u200b")
......@@ -26,6 +27,7 @@ export function zeroWidthElement(measure) {
// Feature-detect IE's crummy client rect reporting for bidi text
let badBidiRects
export function hasBadBidiRects(measure) {
if (badBidiRects != null) return badBidiRects
let txt = removeChildrenAndAdd(measure, document.createTextNode("A\u062eA"))
......@@ -57,12 +59,17 @@ export let splitLinesAuto = "\n\nb".split(/\n/).length != 3 ? string => {
} : string => string.split(/\r\n?|\n/)
export let hasSelection = window.getSelection ? te => {
try { return te.selectionStart != te.selectionEnd }
catch(e) { return false }
try {
return te.selectionStart != te.selectionEnd
} catch (e) {
return false
}
} : te => {
let range
try {range = te.ownerDocument.selection.createRange()}
catch(e) {}
try {
range = te.ownerDocument.selection.createRange()
} catch (e) {
}
if (!range || range.parentElement() != te) return false
return range.compareEndPoints("StartToEnd", range) != 0
}
......@@ -75,6 +82,7 @@ export let hasCopyEvent = (() => {
})()
let badZoomedRects = null
export function hasBadZoomedRects(measure) {
if (badZoomedRects != null) return badZoomedRects
let node = removeChildrenAndAdd(measure, elt("span", "x"))
......
export function bind(f) {
let args = Array.prototype.slice.call(arguments, 1)
return function(){return f.apply(null, args)}
return function () {
return f.apply(null, args)
}
}
export function copyObj(obj, target, overwrite) {
......@@ -18,7 +20,7 @@ export function countColumn(string, end, tabSize, startIndex, startValue) {
end = string.search(/[^\s\u00a0]/)
if (end == -1) end = string.length
}
for (let i = startIndex || 0, n = startValue || 0;;) {
for (let i = startIndex || 0, n = startValue || 0; ;) {
let nextTab = string.indexOf("\t", i)
if (nextTab < 0 || nextTab >= end)
return n + (end - i)
......@@ -35,6 +37,7 @@ export class Delayed {
this.time = 0
this.handler = bind(this.onTimeout, this)
}
onTimeout(self) {
self.id = 0
if (self.time <= +new Date) {
......@@ -43,6 +46,7 @@ export class Delayed {
setTimeout(self.handler, self.time - +new Date)
}
}
set(ms, f) {
this.f = f
const time = +new Date + ms
......@@ -65,7 +69,11 @@ export let scrollerGap = 50
// Returned or thrown by various protocols to signal 'I'm not
// handling this'.
export let Pass = {toString: function(){return "CodeMirror.Pass"}}
export let Pass = {
toString: function () {
return "CodeMirror.Pass"
}
}
// Reused option objects for setSelection & friends
export let sel_dontScroll = {scroll: false}, sel_mouse = {origin: "*mouse"}, sel_move = {origin: "+move"}
......@@ -73,7 +81,7 @@ export let sel_dontScroll = {scroll: false}, sel_mouse = {origin: "*mouse"}, sel
// The inverse of countColumn -- find the offset that corresponds to
// a particular column.
export function findColumn(string, goal, tabSize) {
for (let pos = 0, col = 0;;) {
for (let pos = 0, col = 0; ;) {
let nextTab = string.indexOf("\t", pos)
if (nextTab == -1) nextTab = string.length
let skipped = nextTab - pos
......@@ -87,13 +95,16 @@ export function findColumn(string, goal, tabSize) {
}
let spaceStrs = [""]
export function spaceStr(n) {
while (spaceStrs.length <= n)
spaceStrs.push(lst(spaceStrs) + " ")
return spaceStrs[n]
}
export function lst(arr) { return arr[arr.length-1] }
export function lst(arr) {
return arr[arr.length - 1]
}
export function map(array, f) {
let out = []
......@@ -107,7 +118,8 @@ export function insertSorted(array, value, score) {
array.splice(pos, 0, value)
}
function nothing() {}
function nothing() {
}
export function createObj(base, props) {
let inst
......@@ -122,10 +134,12 @@ export function createObj(base, props) {
}
let nonASCIISingleCaseWordChar = /[\u00df\u0587\u0590-\u05f4\u0600-\u06ff\u3040-\u309f\u30a0-\u30ff\u3400-\u4db5\u4e00-\u9fcc\uac00-\ud7af]/
export function isWordCharBasic(ch) {
return /\w/.test(ch) || ch > "\x80" &&
(ch.toUpperCase() != ch.toLowerCase() || nonASCIISingleCaseWordChar.test(ch))
}
export function isWordChar(ch, helper) {
if (!helper) return isWordCharBasic(ch)
if (helper.source.indexOf("\\w") > -1 && isWordCharBasic(ch)) return true
......@@ -143,7 +157,10 @@ export function isEmpty(obj) {
// since some scripts/fonts/browsers also treat other configurations
// of code points as a group.
let extendingChars = /[\u0300-\u036f\u0483-\u0489\u0591-\u05bd\u05bf\u05c1\u05c2\u05c4\u05c5\u05c7\u0610-\u061a\u064b-\u065e\u0670\u06d6-\u06dc\u06de-\u06e4\u06e7\u06e8\u06ea-\u06ed\u0711\u0730-\u074a\u07a6-\u07b0\u07eb-\u07f3\u0816-\u0819\u081b-\u0823\u0825-\u0827\u0829-\u082d\u0900-\u0902\u093c\u0941-\u0948\u094d\u0951-\u0955\u0962\u0963\u0981\u09bc\u09be\u09c1-\u09c4\u09cd\u09d7\u09e2\u09e3\u0a01\u0a02\u0a3c\u0a41\u0a42\u0a47\u0a48\u0a4b-\u0a4d\u0a51\u0a70\u0a71\u0a75\u0a81\u0a82\u0abc\u0ac1-\u0ac5\u0ac7\u0ac8\u0acd\u0ae2\u0ae3\u0b01\u0b3c\u0b3e\u0b3f\u0b41-\u0b44\u0b4d\u0b56\u0b57\u0b62\u0b63\u0b82\u0bbe\u0bc0\u0bcd\u0bd7\u0c3e-\u0c40\u0c46-\u0c48\u0c4a-\u0c4d\u0c55\u0c56\u0c62\u0c63\u0cbc\u0cbf\u0cc2\u0cc6\u0ccc\u0ccd\u0cd5\u0cd6\u0ce2\u0ce3\u0d3e\u0d41-\u0d44\u0d4d\u0d57\u0d62\u0d63\u0dca\u0dcf\u0dd2-\u0dd4\u0dd6\u0ddf\u0e31\u0e34-\u0e3a\u0e47-\u0e4e\u0eb1\u0eb4-\u0eb9\u0ebb\u0ebc\u0ec8-\u0ecd\u0f18\u0f19\u0f35\u0f37\u0f39\u0f71-\u0f7e\u0f80-\u0f84\u0f86\u0f87\u0f90-\u0f97\u0f99-\u0fbc\u0fc6\u102d-\u1030\u1032-\u1037\u1039\u103a\u103d\u103e\u1058\u1059\u105e-\u1060\u1071-\u1074\u1082\u1085\u1086\u108d\u109d\u135f\u1712-\u1714\u1732-\u1734\u1752\u1753\u1772\u1773\u17b7-\u17bd\u17c6\u17c9-\u17d3\u17dd\u180b-\u180d\u18a9\u1920-\u1922\u1927\u1928\u1932\u1939-\u193b\u1a17\u1a18\u1a56\u1a58-\u1a5e\u1a60\u1a62\u1a65-\u1a6c\u1a73-\u1a7c\u1a7f\u1b00-\u1b03\u1b34\u1b36-\u1b3a\u1b3c\u1b42\u1b6b-\u1b73\u1b80\u1b81\u1ba2-\u1ba5\u1ba8\u1ba9\u1c2c-\u1c33\u1c36\u1c37\u1cd0-\u1cd2\u1cd4-\u1ce0\u1ce2-\u1ce8\u1ced\u1dc0-\u1de6\u1dfd-\u1dff\u200c\u200d\u20d0-\u20f0\u2cef-\u2cf1\u2de0-\u2dff\u302a-\u302f\u3099\u309a\ua66f-\ua672\ua67c\ua67d\ua6f0\ua6f1\ua802\ua806\ua80b\ua825\ua826\ua8c4\ua8e0-\ua8f1\ua926-\ua92d\ua947-\ua951\ua980-\ua982\ua9b3\ua9b6-\ua9b9\ua9bc\uaa29-\uaa2e\uaa31\uaa32\uaa35\uaa36\uaa43\uaa4c\uaab0\uaab2-\uaab4\uaab7\uaab8\uaabe\uaabf\uaac1\uabe5\uabe8\uabed\udc00-\udfff\ufb1e\ufe00-\ufe0f\ufe20-\ufe26\uff9e\uff9f]/
export function isExtendingChar(ch) { return ch.charCodeAt(0) >= 768 && extendingChars.test(ch) }
export function isExtendingChar(ch) {
return ch.charCodeAt(0) >= 768 && extendingChars.test(ch)
}
// Returns a number from the range [`0`; `str.length`] unless `pos` is outside that range.
export function skipExtendingChars(str, pos, dir) {
......@@ -158,7 +175,7 @@ export function findFirst(pred, from, to) {
// At any point we are certain `to` satisfies `pred`, don't know
// whether `from` does.
let dir = from > to ? -1 : 1
for (;;) {
for (; ;) {
if (from == to) return from
let midF = (from + to) / 2, mid = dir < 0 ? Math.ceil(midF) : Math.floor(midF)
if (mid == from) return pred(mid) ? from : to
......
import { getHandlers } from "./event.js"
import {getHandlers} from "./event.js"
let operationGroup = null
......@@ -33,8 +33,9 @@ export function finishOperation(op, endCb) {
let group = op.ownsGroup
if (!group) return
try { fireCallbacksForOps(group) }
finally {
try {
fireCallbacksForOps(group)
} finally {
operationGroup = null
endCb(group)
}
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment