Skip to content

Commit

Permalink
Support language detection for codemirror and ace editors.
Browse files Browse the repository at this point in the history
  • Loading branch information
Daniel Perez committed Feb 9, 2016
1 parent 97080c0 commit 45d00f5
Show file tree
Hide file tree
Showing 14 changed files with 172 additions and 76 deletions.
2 changes: 2 additions & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@
"devDependencies": {
"babel-core": "^6.4.5",
"babel-loader": "^6.2.1",
"string-replace-loader": "^1.0.0",
"webpack": "^1.12.12"
},
"scripts": {
Expand All @@ -15,6 +16,7 @@
"dev": "./node_modules/.bin/webpack --watch"
},
"dependencies": {
"codemirror": "^5.11.0",
"uuid": "^2.0.1"
}
}
17 changes: 13 additions & 4 deletions src/content-script-tools/text-syncer.js
Original file line number Diff line number Diff line change
@@ -1,9 +1,9 @@
const NORMAL_CLOSE_CODE = 1000;

class TextSyncer {
linkElem(title, handler) {
linkElem(title, handler, options) {
const port = chrome.runtime.connect();
this.register(port, title, handler);
this.register(port, title, handler, options);
port.onMessage.addListener(this.makeMessageListener(handler));
const textChangeListener = this.makeTextChangeListener(port, handler);
handler.bindChange(textChangeListener, false);
Expand Down Expand Up @@ -40,9 +40,18 @@ class TextSyncer {
};
}

register(port, title, handler) {
register(port, title, handler, options) {
options = options || {};
handler.getValue().then((text) => {
this.post(port, 'register', {title: title, text: text});
const payload = {title: title, text: text};
let extension = options.extension;
if (extension) {
if (extension[0] !== '.') {
extension = `.${extension}`;
}
payload.extension = extension;
}
this.post(port, 'register', payload);
});
}

Expand Down
4 changes: 2 additions & 2 deletions src/content-script.js
Original file line number Diff line number Diff line change
Expand Up @@ -15,8 +15,8 @@ function run() {

const handler = new Handler(activeElement, contentEvents);

handler.load().then(() => {
textSyncer.linkElem(title, handler);
handler.load().then((options) => {
textSyncer.linkElem(title, handler, options);
});
}

Expand Down
34 changes: 1 addition & 33 deletions src/handlers/ace.js
Original file line number Diff line number Diff line change
@@ -1,48 +1,16 @@
import InjectorHandler from './injector';
import injectedHandlerFactory from './injected-factory';
import BaseInjectedHandler from './base-injected';

const name = 'ace';
const aceClassName = 'ace_text-input';

class AceHandler extends InjectorHandler {
constructor(elem, contentEvents) {
super(elem, contentEvents, name);
}
}

class InjectedAceHandler extends BaseInjectedHandler {
constructor(elem, uuid) {
super(elem, uuid);
this.silenced = false;
}

load() {
this.editor = ace.edit(this.elem.parentElement);
this.editor.$blockScrolling = Infinity;
}

getValue() {
return this.editor.getValue();
}

setValue(text) {
this.executeSilenced(() => this.editor.setValue(text, 1));
}

bindChange(f) {
this.editor.on('change', this.wrapSilence(f));
}

unbindChange(f) {
this.editor.off('change', f);
super(elem, contentEvents, 'ace');
}
}

AceHandler.canHandle = function (elem) {
return elem.classList.contains(aceClassName);
};

injectedHandlerFactory.registerHandler(name, InjectedAceHandler);

export default AceHandler;
30 changes: 1 addition & 29 deletions src/handlers/codemirror.js
Original file line number Diff line number Diff line change
@@ -1,34 +1,8 @@
import InjectorHandler from './injector';
import injectedHandlerFactory from './injected-factory';
import BaseInjectedHandler from './base-injected';

const name = 'codeMirror';

class CodeMirrorHandler extends InjectorHandler {
constructor(elem, contentEvents) {
super(elem, contentEvents, name);
}
}

class InjectedCodeMirrorHandler extends BaseInjectedHandler {
load() {
this.editor = this.elem.parentElement.parentElement.CodeMirror;
}

getValue() {
return this.editor.getValue();
}

setValue(text) {
this.executeSilenced(() => this.editor.setValue(text));
}

bindChange(f) {
this.editor.on('change', this.wrapSilence(f));
}

unbindChange(f) {
this.editor.off('change', f);
super(elem, contentEvents, 'codemirror');
}
}

Expand All @@ -38,6 +12,4 @@ CodeMirrorHandler.canHandle = function (elem) {
&& elem.parentElement.parentElement.classList.contains('CodeMirror');
};

injectedHandlerFactory.registerHandler(name, InjectedCodeMirrorHandler);

export default CodeMirrorHandler;
1 change: 0 additions & 1 deletion src/handlers/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -11,4 +11,3 @@ handlerFactory.registerHandler(ContentEditableHandler);
handlerFactory.registerHandler(TextareaHandler);

export {handlerFactory as handlerFactory};
export {default as injectedHandlerFactory} from './injected-factory';
64 changes: 64 additions & 0 deletions src/handlers/injected/ace.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,64 @@
import BaseInjectedHandler from './base';

class InjectedAceHandler extends BaseInjectedHandler {
constructor(elem, uuid) {
super(elem, uuid);
this.silenced = false;
}

load() {
return new Promise((resolve) => {
this.editor = ace.edit(this.elem.parentElement);
this.editor.$blockScrolling = Infinity;
if (!ace.config || !ace.config.loadModule) {
return resolve();
}
ace.config.loadModule('ace/ext/modelist', (m) => {
this.modes = m.modes;
this.loaded = true;
resolve();
});
// NOTE: no callback when loadModule fails, so add a timeout
setTimeout(() => {
if (!this.loaded) {
resolve();
}
}, 3000);
});
}

getExtension() {
if (!this.modes) {
return null;
}
const session = this.editor.getSession();
const currentMode = session && session.getMode() && session.getMode().$id;
if (!currentMode) {
return null;
}
for (const mode of this.modes) {
if (mode.mode === currentMode) {
return mode.extensions.split('|')[0];
}
}
return null;
}

getValue() {
return this.editor.getValue();
}

setValue(text) {
this.executeSilenced(() => this.editor.setValue(text, 1));
}

bindChange(f) {
this.editor.on('change', this.wrapSilence(f));
}

unbindChange(f) {
this.editor.off('change', f);
}
}

export default InjectedAceHandler;
23 changes: 20 additions & 3 deletions src/handlers/base-injected.js → src/handlers/injected/base.js
Original file line number Diff line number Diff line change
Expand Up @@ -4,12 +4,17 @@ export default class BaseInjectedHandler {
constructor(elem, uuid) {
this.elem = elem;
this.uuid = uuid;
this.load();
this.bindChange(() => this.postToInjector('change'));
}

setup() {
return this.load().then((res) => {
this.bindChange(() => this.postToInjector('change'));
return res;
});
}

load() {
// implement in subclass when needed
return Promise.resolve();
}

handleMessage(data) {
Expand Down Expand Up @@ -45,6 +50,18 @@ export default class BaseInjectedHandler {
this.silenced = false;
}

postReady() {
const payload = {};
const extension = this.getExtension();
if (extension) {
payload.extension = extension;
}
this.postToInjector('ready', payload);
}

getExtension() {
}

wrapSilence(f) {
return () => {
if (!this.silenced) {
Expand Down
49 changes: 49 additions & 0 deletions src/handlers/injected/codemirror.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
import BaseInjectedHandler from './base';
import 'codemirror/mode/meta';
import CodeMirror from 'dummy-codemirror';

// NOTE: keep modes which could conflict or which do not resolve here
const commonModes = {
css: 'css',
htmlmixed: 'html',
html: 'html',
javascript: 'js'
};

class InjectedCodeMirrorHandler extends BaseInjectedHandler {
load() {
this.editor = this.elem.parentElement.parentElement.CodeMirror;
return Promise.resolve();
}

getValue() {
return this.editor.getValue();
}

setValue(text) {
this.executeSilenced(() => this.editor.setValue(text));
}

bindChange(f) {
this.editor.on('change', this.wrapSilence(f));
}

unbindChange(f) {
this.editor.off('change', f);
}

getExtension() {
const currentModeName = this.editor.getMode().name;
if (commonModes[currentModeName]) {
return commonModes[currentModeName];
}
for (const mode of CodeMirror.modeInfo) {
if (mode.mode === currentModeName && mode.ext) {
return mode.ext[0];
}
}
return null;
}
}

export default InjectedCodeMirrorHandler;
File renamed without changes.
9 changes: 9 additions & 0 deletions src/handlers/injected/index.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
import injectedHandlerFactory from './factory';

import InjectedAceHandler from './ace';
import InjectedCodeMirrorHandler from './codemirror';

injectedHandlerFactory.registerHandler('ace', InjectedAceHandler);
injectedHandlerFactory.registerHandler('codemirror', InjectedCodeMirrorHandler);

export {injectedHandlerFactory as injectedHandlerFactory};
8 changes: 5 additions & 3 deletions src/injected.js
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import {injectedHandlerFactory} from './handlers';
import {injectedHandlerFactory} from './handlers/injected';

const handlers = [];

Expand All @@ -14,8 +14,10 @@ window.addEventListener('message', function (message) {
return;
}
const handler = new Handler(document.activeElement, message.data.uuid);
handlers.push(handler);
handler.postToInjector('ready');
handler.setup().then(() => {
handlers.push(handler);
handler.postReady();
});
} else {
for (const handler of handlers) {
handler.handleMessage(message.data);
Expand Down
1 change: 1 addition & 0 deletions src/shims/codemirror.js
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
module.exports = {};
6 changes: 5 additions & 1 deletion webpack.config.js
Original file line number Diff line number Diff line change
Expand Up @@ -17,11 +17,15 @@ module.exports = {
test: /\.js$/,
loader: 'babel?presets[]=es2015',
exclude: /node_modules/
}, {
test: /codemirror\/mode\/meta/,
loader: 'string-replace?search=../lib/codemirror,replace=dummy-codemirror'
}]
},
resolve: {
alias: {
'ac-util': path.join(__dirname, 'src', 'util')
'ac-util': path.join(__dirname, 'src', 'util'),
'dummy-codemirror': path.join(__dirname, 'src', 'shims', 'codemirror')
}
},
externals: {
Expand Down

0 comments on commit 45d00f5

Please sign in to comment.