Anyone have any best practices for converting extensions from other editors? I don’t know enough about the problem domain to get started on my own, but I can do grunt work to help port things given some starting point.
By no means a proper port, but I hacked this nugget together last night so I could reanimate a few Textmate scripts. Converting Nova’s Range coordinate system to a row-column coordinate system is essentially this from @apexskier’s TypeScript extension. (@logan, some built-in Range ↔︎ row-column conversion would be a spectacular feature. It seems that just about every extension I write needs that to interact with things outside of Nova.)
// Compute TM_* environment variables to run scripts written for
// Textmate bundles.
// https://macromates.com/manual/en/environment_variables
function tmVars(editor) {
let vars = {};
// shell commands which are (indirectly) triggered from a bundle item
// which could be a Command, Drag Command, Macro, or Snippet) will have
// this variable pointing to the Support folder of the bundle that ran
// the item, if such a folder exists. In addition, $TM_BUNDLE_SUPPORT/bin
// will be added to the path.
vars.TM_BUNDLE_SUPPORT = nova.extension.path;
vars.PATH = [
nova.environment.PATH,
nova.path.join(vars.TM_BUNDLE_SUPPORT, "bin"),
].join(":");
// textual content of the current line.
let clr = editor.getLineRangeForRange(
new Range(editor.selectedRange.start, editor.selectedRange.start)
);
vars.TM_CURRENT_LINE = editor.getTextInRange(clr).replace(/\n$/, "");
// the word in which the caret is located.
// vars.TM_CURRENT_WORD = TODO
if (editor.document.path) {
// the folder of the current document (may not be set).
vars.TM_DIRECTORY = nova.path.dirname(editor.document.path);
// path (including file name) for the current document (may not be set).
vars.TM_FILEPATH = editor.document.path;
}
let l = RangeToLspRange(editor.document, editor.selectedRange);
// the index in the current line which marks the caret’s location. This index
// is zero-based and takes the utf-8 encoding of the line (e.g. read as
// TM_CURRENT_LINE) into account.
vars.TM_LINE_INDEX = `${l.start.character}`;
// the carets line position (counting from 1).
vars.TM_LINE_NUMBER = `${l.start.line + 1}`;
// the top-level folder in the project drawer (may not be set).
if (nova.workspace.path) {
vars.TM_PROJECT_DIRECTORY = nova.workspace.path;
}
// the scope that the caret is inside. See scope selectors for information about scopes.
// vars.TM_SCOPE = TODO
// vars.TM_SELECTED_FILES = TODO
// vars.TM_SELECTED_FILE = TODO
// full content of the selection (may not be set).
if (editor.selectedText) {
vars.TM_SELECTED_TEXT = editor.selectedText;
}
// this will have the value YES if the user has enabled soft tabs, otherwise it has the value NO.
vars.TM_SOFT_TABS = editor.softTabs ? "YES" : "NO";
// the TextMate application bundle contains a support folder with several
// items which are used by some of the default commands (for example
// CocoaDialog, Markdown, the SCM commit window, Textile, tidy, etc.).
vars.TM_SUPPORT_PATH = nova.path.join(
nova.environment.HOME,
"Library/Application Support/TextMate/Managed/Bundles/Bundle Support.tmbundle/Support/shared"
);
vars.PATH = [
nova.environment.PATH,
nova.path.join(vars.TM_SUPPORT_PATH, "bin"),
].join(":");
// the tab size as shown in the status bar.
vars.TM_TAB_SIZE = `${editor.tabLength}`;
// console.log(JSON.stringify(vars));
return vars;
}
+1 to this. I’ve implemented a different form of this in three different places - for LSP, eslint, and jest
Agreed. We are actively looking at implementing line / column access in the extension API—as Nova does track this information internally. I can’t make a guarantee which version it will appear in, but I’m planning soon.