This repository has been archived by the owner on Mar 13, 2023. It is now read-only.
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
New implementation of Brackets plugin
- Loading branch information
Showing
8 changed files
with
371 additions
and
1,308 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,160 +1,196 @@ | ||
define(['./emmet'], function(emmet) { | ||
var require = emmet.require; | ||
var _ = require('_'); | ||
/** | ||
* Emmet Editor interface implementation for Brackets. | ||
* Interface is optimized for multiple cursor usage: authors | ||
* should run acttion multiple times and update `selectionIndex` | ||
* property on each iteration. | ||
*/ | ||
define(function(require, exports, module) { | ||
var emmet = require('emmet/emmet'); | ||
var utils = require('emmet/utils/common'); | ||
var editorUtils = require('emmet/utils/editor'); | ||
var actionUtils = require('emmet/utils/action'); | ||
var tabStops = require('emmet/assets/tabStops'); | ||
|
||
var modeMap = { | ||
"text/html": "html", | ||
"htmlmixed": "html", | ||
"text/x-brackets-html": "html", | ||
"application/xml": "xml", | ||
"text/xsl": "xsl", | ||
"text/css": "css", | ||
"text/x-less": "less", | ||
"text/x-scss": "scss", | ||
"text/x-sass": "sass", | ||
"php": "html" | ||
}; | ||
|
||
return { | ||
context: null, | ||
filePath: null, | ||
setupContext: function (context, filePath) { | ||
this.context = context; | ||
this.filePath = filePath; | ||
var indentation = "\t"; | ||
if (!context.getOption("indentWithTabs")) { | ||
indentation = require("utils").repeatString(" ", context.getOption("indentUnit")); | ||
} | ||
|
||
require("resources").setVariable("indentation", indentation); | ||
}, | ||
|
||
getSelectionRange: function () { | ||
var caretPos = this.getCaretPos(); | ||
return { | ||
start: caretPos, | ||
end: caretPos + this.getSelection().length | ||
}; | ||
}, | ||
|
||
createSelection: function (start, end) { | ||
if (start == end) { | ||
this.context.setCursor(this.context.posFromIndex(start)); | ||
} else { | ||
this.context.setSelection(this.context.posFromIndex(start), this.context.posFromIndex(end)); | ||
} | ||
}, | ||
|
||
getCurrentLineRange: function () { | ||
var caret = this.context.getCursor(true); | ||
return { | ||
start: this.context.indexFromPos({line: caret.line, ch: 0}), | ||
end: this.context.indexFromPos({line: caret.line, ch: this.context.getLine(caret.line).length}) | ||
}; | ||
}, | ||
|
||
getCaretPos: function () { | ||
return this.context.indexFromPos(this.context.getCursor(true)); | ||
}, | ||
|
||
setCaretPos: function (pos) { | ||
this.createSelection(pos, pos); | ||
}, | ||
|
||
getCurrentLine: function () { | ||
return this.context.getLine(this.context.getCursor(true).line) || ""; | ||
}, | ||
|
||
replaceContent: function (value, start, end, noIndent) { | ||
if (_.isUndefined(end)) | ||
end = _.isUndefined(start) ? value.length : start; | ||
if (_.isUndefined(start)) start = 0; | ||
var utils = require("utils"); | ||
|
||
// indent new value | ||
if (!noIndent) { | ||
value = utils.padString(value, utils.getLinePaddingFromPosition(this.getContent(), start)); | ||
} | ||
|
||
// find new caret position | ||
var tabstopData = require("tabStops").extract(value, { | ||
escape: function (ch) { | ||
return ch; | ||
} | ||
}); | ||
value = tabstopData.text; | ||
var firstTabStop = tabstopData.tabstops[0]; | ||
|
||
if (firstTabStop) { | ||
firstTabStop.start += start; | ||
firstTabStop.end += start; | ||
} else { | ||
firstTabStop = { | ||
start: value.length + start, | ||
end: value.length + start | ||
}; | ||
} | ||
|
||
// do a compound change to record all changes into single undo event | ||
var that = this; | ||
var op = this.context.operation || this.context.compoundChange; | ||
op.call(this.context, function() { | ||
that.context.replaceRange(value, that.context.posFromIndex(start), that.context.posFromIndex(end)); | ||
that.createSelection(firstTabStop.start, firstTabStop.end); | ||
}); | ||
}, | ||
|
||
getContent: function () { | ||
return this.context.getValue(); | ||
}, | ||
|
||
getCMSyntax: function() { | ||
var syntax = this.context.getOption("mode"); | ||
return syntax in modeMap ? modeMap[syntax] : syntax; | ||
}, | ||
|
||
getSyntax: function () { | ||
var syntax = this.getCMSyntax(); | ||
return require('actionUtils').detectSyntax(this, syntax); | ||
}, | ||
|
||
/** | ||
* Returns current output profile name (@see emmet#setupProfile) | ||
* @return {String} | ||
*/ | ||
getProfileName: function () { | ||
if (this.context.getOption("profile")) | ||
return this.context.getOption("profile"); | ||
|
||
return require('actionUtils').detectProfile(this); | ||
}, | ||
|
||
/** | ||
* Ask user to enter something | ||
* @param {String} title Dialog title | ||
* @return {String} Entered data | ||
* @since 0.65 | ||
*/ | ||
prompt: function (title) { | ||
return prompt(title); | ||
}, | ||
|
||
/** | ||
* Returns current selection | ||
* @return {String} | ||
* @since 0.65 | ||
*/ | ||
getSelection: function () { | ||
return this.context.getSelection() || ""; | ||
}, | ||
|
||
/** | ||
* Returns current editor"s file path | ||
* @return {String} | ||
* @since 0.65 | ||
*/ | ||
getFilePath: function () { | ||
return this.filePath; | ||
} | ||
}; | ||
var Editor = brackets.getModule('editor/Editor').Editor; | ||
|
||
/** | ||
* Normalizes text before it goes to editor: replaces indentation | ||
* and newlines with ones used in editor | ||
* @param {String} text Text to normalize | ||
* @param {Editor} editor Brackets editor instance | ||
* @return {String} | ||
*/ | ||
function normalize(text, editor) { | ||
var indentation = '\t'; | ||
if (!Editor.getUseTabChar()) { | ||
indentation = ''; | ||
var units = Editor.getSpaceUnits(); | ||
while (units--) { | ||
indentation += ' '; | ||
} | ||
} | ||
|
||
return editorUtils.normalize(text, { | ||
indentation: indentation, | ||
newline: '\n' | ||
}); | ||
} | ||
|
||
return { | ||
editor: null, | ||
selectionIndex: 0, | ||
modeMap: { | ||
'text/html': 'html', | ||
'application/xml': 'xml', | ||
'text/xsl': 'xsl', | ||
'text/css': 'css', | ||
'text/x-less': 'less', | ||
'text/x-scss': 'scss', | ||
'text/x-sass': 'sass' | ||
}, | ||
|
||
setup: function(editor, selIndex) { | ||
this.editor = editor; | ||
this.selectionIndex = selIndex || 0; | ||
}, | ||
|
||
_convertRange: function(sel) { | ||
return { | ||
start: this.editor.indexFromPos(sel.start), | ||
end: this.editor.indexFromPos(sel.end) | ||
}; | ||
}, | ||
|
||
_currentLineRange: function() { | ||
var sel = this.editor.getSelections()[this.selectionIndex]; | ||
return this.editor.convertToLineSelections([sel])[0].selectionForEdit; | ||
}, | ||
|
||
_posFromIndex: function(index) { | ||
// XXX: shouldn’t use private editor._codeMirror here, | ||
// Brackets must provide `posFromIndex()` method alias | ||
return this.editor._codeMirror.posFromIndex(index); | ||
}, | ||
|
||
/** | ||
* Returns list of selections for current CodeMirror instance. | ||
* @return {Array} | ||
*/ | ||
selectionList: function() { | ||
return this.editor.getSelections().map(this._convertRange, this); | ||
}, | ||
|
||
getCaretPos: function() { | ||
return this.getSelectionRange().start; | ||
}, | ||
|
||
setCaretPos: function(pos) { | ||
this.createSelection(pos); | ||
}, | ||
|
||
/** | ||
* Returns current selection range (for current selection index) | ||
* @return {Object} | ||
*/ | ||
getSelectionRange: function() { | ||
return this.selectionList()[this.selectionIndex]; | ||
}, | ||
|
||
createSelection: function(start, end) { | ||
end = end || start; | ||
|
||
var sels = this.editor.getSelections(); | ||
sels[this.selectionIndex] = { | ||
start: this._posFromIndex(start), | ||
end: this._posFromIndex(end) | ||
}; | ||
this.editor.setSelections(sels); | ||
}, | ||
|
||
/** | ||
* Returns current selection | ||
* @return {String} | ||
*/ | ||
getSelection: function() { | ||
var sel = this.editor.getSelections()[this.selectionIndex]; | ||
return this.editor.document.getRange(sel.start, sel.end); | ||
}, | ||
|
||
getCurrentLineRange: function() { | ||
return this._convertRange(this._currentLineRange()); | ||
}, | ||
|
||
getCurrentLine: function() { | ||
var lineRange = this._currentLineRange(); | ||
return this.editor.document.getRange(lineRange.start, lineRange.end); | ||
}, | ||
|
||
getContent: function() { | ||
return this.editor.document.getText(); | ||
}, | ||
|
||
replaceContent: function(value, start, end, noIndent) { | ||
if (typeof end == 'undefined') { | ||
end = (typeof start == 'undefined') ? this.getContent().length : start; | ||
} | ||
if (typeof start == 'undefined') { | ||
start = 0; | ||
} | ||
|
||
// indent new value | ||
if (!noIndent) { | ||
value = utils.padString(value, utils.getLinePaddingFromPosition(this.getContent(), start)); | ||
} | ||
|
||
// find new caret position | ||
var tabstopData = tabStops.extract(value, { | ||
escape: function(ch) { | ||
return ch; | ||
} | ||
}); | ||
value = tabstopData.text; | ||
|
||
var firstTabStop = tabstopData.tabstops[0] || {start: value.length, end: value.length}; | ||
firstTabStop.start += start; | ||
firstTabStop.end += start; | ||
|
||
this.editor.document.replaceRange(value, this._posFromIndex(start), this._posFromIndex(end)); | ||
this.createSelection(firstTabStop.start, firstTabStop.end); | ||
}, | ||
|
||
getSyntax: function() { | ||
var sel = this.editor.getSelections()[this.selectionIndex]; | ||
var mode = this.editor.getModeForRange(sel.start, sel.end).name; | ||
return this.modeMap[mode] || mode; | ||
}, | ||
|
||
/** | ||
* Returns current output profile name (@see emmet#setupProfile) | ||
* @return {String} | ||
*/ | ||
getProfileName: function() { | ||
return actionUtils.detectProfile(this); | ||
}, | ||
|
||
/** | ||
* Ask user to enter something | ||
* @param {String} title Dialog title | ||
* @return {String} Entered data | ||
*/ | ||
prompt: function(title) { | ||
return prompt(title); | ||
}, | ||
|
||
/** | ||
* Returns current editor's file path | ||
* @return {String} | ||
*/ | ||
getFilePath: function() { | ||
if (this.editor.document.isUntitled()) { | ||
return null; | ||
} | ||
|
||
return this.editor.document.file.fullPath; | ||
} | ||
}; | ||
}); |
Oops, something went wrong.