How to publish a few snippets in a single file Sublime Text 3? - code-snippets

How to publish a few snippets in a single file Sublime Text 3?
I am use this code, but do not work. I have error!
<snippets>
<snippet>
<content><![CDATA[
.............................
]]></content>
<tabTrigger>span</tabTrigger>
<scope>text.html</scope>
<description>span</description>
</snippet>
<snippet>
<content><![CDATA[
.............................
]]></content>
<tabTrigger>img</tabTrigger>
<scope>text.html</scope>
<description>img</description>
</snippet>
</snippets>

For multiple snippets, you'll need to either make multiple .sublime-snippet files, or set up a custom completions list. Yours might look something like this:
{
"scope": "text.html - source, punctuation.definition.tag.begin",
"completions":
[
{ "trigger": "myspan", "contents": "<span class=\"$1\" id=\"$2\">$0</span>" },
{ "trigger": "myimg", "contents": "<img alt=\"$1\" src=\"$2\" class=\"$3\" id=\"$4\" />$0" },
]
}
Save it in your Packages/User directory as HTML.sublime-completions (you can access Packages by going to Preferences -> Browse Packages...). Now, when you're coding and type span and hit CtrlSpace to bring up autocomplete (if it doesn't appear automatically, you'll see the following:
The top option is Sublime's built-in completion, which just gives <span>|</span> where | is the cursor. I have the Tag package installed, so it adds another option that basically does the same thing as Sublime's. The third option is our new completion. You'll need to arrow down to select your completion, but the good news is that the next time you type span it'll be selected, so you can just hit Tab and go on to fill in the options.
I hope this helps, please let me know if you have any questions.

Related

Repeat cursor text in a custom Emmet snippet

I want to create a snippet where the the text I type at the first cursor position gets used multiple times. I thought I could do it this way by repeating a cursor position number but it just treats the other ones as independent cursor positions. I don't see anything in the documentation about this.
Here is an example of what I would like to work:
<template id=${1}>
This is a ${1}!
</template>
<style>
#${1} {${2}}
</style>
Here is my Emmet snippets file used in VS Code:
{
"html": {
"snippets": {
"test": "test[id=${1}]>{This is a ${1}.}"
}
}
}
When I use the 'test' snippet it puts the cursor at the first ${1} but anything I type is not reflected in the second ${1} it stays empty.
It depends on editor you’re using. If your editor fully supports text fields (like Sublime Text, VSCode) the example above should work, all ${N} with same N should be connected.

Inserting date command into snippet sublime text 3

I am trying to create a snippet for starting a LaTex document where the packages and everything is filled in, but I also want it to automatically fill in the date. I have the insertdate package, but I did not understand how to use it. I have the snippet code:
<snippet>
<content><![CDATA[
\documentclass{article}
\usepackage{amsmath}
\title{${1:title}}
\author{...}
\date{[Todays date here automatically]}
\begin{document}
\maketitle
$0
\end{document}
]]></content>
<!-- Optional: Set a tabTrigger to define how to trigger the snippet -->
<tabTrigger>document</tabTrigger> -->
<!-- Optional: Set a scope to limit where the snippet will trigger -->
<scope>text.tex.latex</scope> -->
</snippet>
It's not directly possible to expand a snippet in the traditional way (i.e. documentTab in this case) and have one or more of the fields in the snippet dynamically update; for that you would need extra glue plugin code.
The InsertDate package provides a variety of ways to insert a date into your document, but that extra glue is not provided. An example of a way to do that is outlined below.
All of the examples below assume the following snippet content in a file named Packages/User/NewLatexDocument.sublime-snippet; in the following examples, replace that path with the appropriate path and file name of your snippet.
<snippet>
<content><![CDATA[
\documentclass{article}
\usepackage{amsmath}
\title{${2:title}}
\author{...}
\date{${1:date}}
\begin{document}
\maketitle
$0
\end{document}
]]></content>
<description>Create new LaTeX Document</description>
<tabTrigger>document</tabTrigger>
<scope>text.tex.latex</scope>
</snippet>
This is a version of the one you provided in your question, but note that the version in your question is broken because the <tabTrigger> and <scope> lines are terminated with the --> comment sequence, which is not valid.
Of particular interest here is that the snippet fields have been re-ordered so that the date field is the first one and the title is the second one, for reasons we'll see in a moment.
It's also important to know where your User package is; if you're not sure, you can find the location by selecting Preferences > Browse Packages... from the menu. On MacOS, the menu location is Sublime Text > Preferences > Browse Packages... instead.
Method 1: Expanding the snippet via the tab trigger (no extra plugin)
It's possible to invoke the snippet with the tab trigger (here the word document), but as mentioned above there's no direct way to get the InsertDate package to insert the date for you.
What you can do however is use one of the default key bindings to trigger a manual date insertion. An example of that would be pressing F5, which displays a panel of date options and then inserts one. From there you can press Tab to continue on in the snippet.
See the documentation for the package for all of the various keys that you can use here or to get a feel for how you could make your own custom binding to insert in the format of your choice directly.
This isn't directly what you want to do, but it's also the method that requires the least amount of work to set up.
Method 2: Expanding the snippet via a macro
By using a Sublime macro, you can take a single action (key press, command palette entry, etc) and have both actions happen automatically; expand the snippet and insert the date.
To do that, you would save the following contents into a sublime-macro in your User package. The name doesn't matter so long as you remember what you used because you're going to need it in a moment. In my examples below, the filename is Packages/User/NewLatexDocument.sublime-macro.
[
{ "command": "insert_snippet", "args": {"name": "Packages/User/NewLatexDocument.sublime-snippet"} },
{ "command": "insert_date", "args": {"format": "%x"} },
{ "command": "next_field" },
]
Make sure that the filename in the insert_snippet argument matches the name of your snippet. You probably also want to change the date format to whatever format you prefer.
When you invoke the macro, it inserts the snippet, then triggers the insert_date function to insert the date (this is why the date field is first) and skips to the next field, at which point you can start manually filling out the rest of the snippet.
You can see this in action by selecting Tools > Macros > User > NewLatexDocument from the menu; the final item will be the name of your sublime-macro file.
Now you can create a key binding that triggers the macro for you so you don't have to select it from the menu. As above, remember to ensure that the file name of the sublime-macro is the file you created.
{
"keys": ["ctrl+alt+shift+d"],
"command": "run_macro_file",
"args": {
"file": "res://Packages/User/NewLatexDocument.sublime-macro"
}
},
Alternatively, you can create a sublime-commands file in your User package (for example, MyCustomCommands.sublime-commands) with the following contents, which will make the command available in the Command Palette:
[
{
"caption": "Create new Latex Document",
"command": "run_macro_file",
"args": {
"file": "res://Packages/User/NewLatexDocument.sublime-macro"
}
},
]
Method 3: Expanding the snippet via the tab trigger (w/extra plugin)
This example is the closest to what you want to do but it requires a little bit more work. Additionally note that it requires you to enter the full expansion text (e.g. document) and won't trigger on a partial match if that happens to be the best completion.
To start with, make sure you've created the macro from Method 2, as this method uses that same macro but triggers it in a different way.
Next, select Tools > Developer > New Plugin... from the menu and replace the text of the default plugin with the following code, then save it as a .py file; in my example I named the file new_latex_document.py:
import sublime
import sublime_plugin
class NewLatexDocumentCommand(sublime_plugin.TextCommand):
def run(self, edit):
# Get the last word of the line the cursor is on
point = self.view.sel()[0].b
line = self.view.substr(self.view.line(point))
word = line.split()[-1]
# Remove the trigger word
self.view.replace(edit, sublime.Region(point, point - len(word)), "")
# Run the macro
self.view.run_command("run_macro_file", {
"file": "res://Packages/User/NewLatexDocument.sublime-macro"
})
This creates a command named new_latex_document (named based on the name of the class, not the name of the file you save the plugin in) which, when you invoke it, will erase the last word on the line where the cursor is sitting and then run the macro from method 2 above.
Now you need to add the following key binding to your custom keys:
{
"keys": ["tab"],
"command": "new_latex_document",
"context": [
{ "key": "selector", "operator": "equal", "operand": "text.tex.latex"},
{ "key": "preceding_text", "operator": "regex_match", "operand": "^.*document$", "match_all": true },
{ "key": "following_text", "operator": "regex_match", "operand": "^$", "match_all": true },
{ "key": "num_selections", "operator": "equal", "operand": 1},
{ "key": "selection_empty", "operator": "equal", "operand": "true", "match_all": true },
]
},
Broken down, this say that the Tab key should run our new command, but only in files of type text.tex.latex, where the text prior to the cursor location is the word document with no text following it, and when there is only a single cursor in the document with an empty selection.
Now when you enter the word document followed by Tab and these conditions are satisfied, the custom command runs, removing the trigger word document and then expanding the macro out to insert the snippet and the date.
Note that in this case, the tabTrigger and the scope in the sublime-snippet file are ignored, which is why the key binding needs to explicitly set them.
This plugin could be enhanced if desired; for example instead of running the run_macro_file command, it could just execute each of the commands from the macro directly, saving a step.
If you had many such expansions you wanted to do with a date, it's probably better to use an even more customized plugin; using an on_query_completions handler for example would allow the triggering to happen without requiring the key bindings.
That's a more advanced topic, however.

Firefox WebExtention won't load

So, I tried to load my add-on using the about:debugging page in Firefox. But, it simply wouldn't load. Is there somewhere where an error would be logged that I could find it?
Here is my manifest.JSON code:
{
"description": "Adds a stickfigure",
"manifest_version": 2,
"name": "StickMan",
"version": "1.0",
"icons": {
"48": "icons/StickMan-48.png"
},
"applications": {
"gecko": {
"id": "extention#stick.man",
"strict_min_version": "45.0"
}
},
"permissions": [
"activeTab"
],
"background": {
"scripts": ["StickManUpdate.js"]
},
"browser_action": {
"default_icon": {
"48": "icons/StickManButton.png"
},
"default_title": "Call StickMan",
},
}
I hope that this helps other frustrated add-on creators.
Thanks in advance
The lack of loading issue is that you have multiple syntax errors in the JSON of your manifest.json file. In your manifest.json file the lines at the end of the file:
"default_title": "Call StickMan",
},
}
Should not have the extra , (which would indicate you are going to have another property in the Object):
"default_title": "Call StickMan"
}
}
If you were using the Firefox Developer Edition, the fact that you had these errors would have been obvious:
However, even if you are running Firefox 47.0.1 and had merely used the Browser Console (keyboard shortcut: Ctrl-Shift-J), as suggested in the comments, you would have seen the error:
A promise chain failed to handle a rejection. Did you forget to '.catch', or did you forget to 'return'?
See https://developer.mozilla.org/Mozilla/JavaScript_code_modules/Promise.jsm/Promise
Date: Sun Jul 17 2016 11:11:22 GMT-0700 (Pacific Standard Time)
Full Message: SyntaxError: JSON.parse: expected double-quoted property name at line 33 column 2 of the JSON data
Full Stack: readJSON/</<#resource://gre/modules/Extension.jsm:628:19
NetUtil_asyncFetch/<.onStopRequest#resource://gre/modules/NetUtil.jsm:128:17
While a bit cryptic, it still shows the line number of the first issue:
Full Message: SyntaxError: JSON.parse: expected double-quoted property name at line 33 column 2 of the JSON data
The error produced in the Browser Console of Firefox Developer Edition is a bit easier to parse as to what the issue is:
SyntaxError: JSON.parse: expected double-quoted property name at line 33 column 2 of the JSON data
Stack trace:
readJSON/</<#resource://gre/modules/Extension.jsm:859:19
NetUtil_asyncFetch/<.onStopRequest#resource://gre/modules/NetUtil.jsm:128:17
WebExtensions Development:
The WebExtensions API is currently in development. If you are developing a WebExtension, you should be using either Firefox Nightly, or Firefox Developer Edition in order to test your code.
More on your code:
Syntax error:
In addition to the above syntax errors, you have more issues. I did not attempt to resolve all of them, but did get sucked into fixing enough so that the add-on was functional. The next reported error, a syntax error, is in your StickManUpdate.js file on the code:
browser.tabs.sendMessage(
message: "End";
);
You have multiple issues here. Please see the tabs.sendMessage() documentation. You are missing the required tabId parameter. In addition, you appear to be mixing-up the difference between having an Object being passed as a parameter containing properties which are the information passed to the method versus a list of parameters which are other native types passed to a method. Note: It is not uncommon for there to be both a list of parameters of various native or non-native types and an Object containing properties which are data passed to the method.
Assuming browserAction is defined:
You use methods of browserAction in multiple locations where it should be browser.browserAction. browserAction by itself is not defined. Alternately, you could use browserAction as a shortcut by defining it like: var browserAction = browser.browserAction;.
Use of browserAction.getTitle() as if it is synchronous when in reality it is asynchronous:
You make a call to browserAction.getTitle() to get the value of the title. The value of the title is only available in the callback function, which you do not supply. This implies a lack of understanding of asynchronous programming. You might want to review some questions on that subject like:
Why isn't a global variable set immediately after defining a callback/listener function (asynchronous messaging, port.on)
Why is my variable unaltered after I modify it inside of a function? - Asynchronous code reference
How do I return the response from an asynchronous call?
Wrong parameter type supplied to browserAction.setTitle():
This appears to, again, be confusion as to the difference between parameters of other native types and a parameter that is an Object (which may be an Object literal) which contains properties which are the information passed to the method. Admittedly, WebExtensions appear to almost arbitrarily mix using actual parameters and Objects with the properties functioning as parameters when passing information to methods. It appears that being careful as to which is being used in a particular method will be required.
Not having various functions specify the ID for the tab:
In multiple calls to various methods, you do not pass the tabId when you should. You are adding your StickMan canvas to a single tab per mouse click. You should be passing the tab ID for calls to multiple methods.
Assigning to document.body.innerHTML in stickman.js:
In general, assigning to innerHTML at any time should be avoided, if possible. It is a bad idea under most circumstances. In most instances, it may cause the entire DOM to be re-evaluated. For doing what you desire, adding HTML in text format to the DOM at the end of the HTML for an element, there is a specific function which is better/faster: insertAdjacentHTML(). Your code:
document.body.innerHTML+= '<canvas id="StickManCanvas0000000" width="100" height="200"></canvas>';
Could be written as:
document.body.insertAdjacentHTML("beforeend", '<canvas id="StickManCanvas0000000" width="100" height="200"></canvas>');
However, it is still a bad idea to use insertAdjacentHTML() here. There is a significant stigma attached to using either insertAdjacentHTML() or assigning to innerHTML. Using either will result in your add-on receiving additional scrutiny when submitted to AMO for distribution. This is mostly because there are real security issues with using either methodology for changing the DOM. The security issues are when what is being added is text that is dynamically generated from input/data which is not hard coded into your add-on. In addition, you are already mixing adding the element as text and performing changes to it using other JavaScript (e.g. assigning to canvas.style.position). You really should use one or the other. In this case, it is better to construct canvas entirely in JavaScript. It is, after all, only 4 lines to do the same thing you were doing in the two you were using for the innerHTML assignment and the getElementById() to find the canvas element.
Personally, I like using insertAdjacentHTML() in many instances with complex structures. It is generally faster to use it for inserting larget amounts of HTML. It also allows you to keep what is being inserted represented as text. Such text may be much easier to visualize the structure being added rather than figuring out what a large chunk of DOM generated using document.createElement() and setAttribute() actually looks like. However, along with the other drawbacks mentioned above, using insertAdjacentHTML() may not lend itself as easily to writing modular code.
Issues with how you insert you content script and canvas:
Every time the user clicks on your browserAction button you insert another copy of your content script into the tab. This leads to issues of errors being generated due to the consumed content scripts getting the message sent by your call to browser.tabs.sendMessage() and not being able to find the canvas. The correct solution to this is to only chrome.tabs.executeScript() the first time the button is clicked in a tab and then send a message to the content script each subsequent time the button is clicked in that tab causing the same canvas to be re-inserted into the DOM. An easy way to track if you have already loaded the StickMan into a particular tab is to use setTitle() to have the title for your button be different after the first run in that tab.
Other issues:
Note: Your code structure in stickman.js is a bit convoluted. You might want to address this.
All together
manifest.json:
{
"description": "Adds a stickfigure",
"manifest_version": 2,
"name": "StickMan",
"version": "1.0",
"icons": {
"48": "icons/StickMan-48.png"
},
"applications": {
"gecko": {
"id": "extention#stick.man",
"strict_min_version": "45.0"
}
},
"permissions": [
"activeTab"
],
"background": {
"scripts": ["StickManUpdate.js"]
},
"browser_action": {
"default_icon": {
"48": "icons/StickManButton.png"
},
"default_title": "Call StickMan",
"browser_style": true
}
}
StickManUpdate.js:
browser.browserAction.onClicked.addListener(function(tab) {
browser.browserAction.getTitle({tabId:tab.id},function(title){
if(title === 'Call StickMan') {
chrome.tabs.executeScript(tab.id, {
file: "/content_scripts/stickman.js"
});
browser.browserAction.setTitle({title:'Recall StickMan',tabId:tab.id});
} else if (title === 'Call StickMan again') {
browser.tabs.sendMessage(tab.id,"Draw");
browser.browserAction.setTitle({title:'Recall StickMan',tabId:tab.id});
}else {
browser.tabs.sendMessage(tab.id,"End");
browser.browserAction.setTitle({title:'Call StickMan again',tabId:tab.id});
}
});
});
stickman.js:
var running = true;
//document.body.insertAdjacentHTML("beforeend", '<canvas id="StickManCanvas0000000" width="100" height="200"></canvas>');
var canvas = document.createElement("canvas");
canvas.setAttribute("width",100);
canvas.setAttribute("height",200);
//var canvas = document.getElementById('StickManCanvas0000000');
canvas.style.position = 'fixed';
canvas.style.left = '0px';
canvas.style.top = (window.innerHeight-200)+'px';
canvas.style.backgroundColor = 'rgba(0, 0, 0, 0)';
canvas.style.border = '1px dashed red';
var ctx = canvas.getContext('2d');
var pos = {
x:0,
headX:50,
headY:20,
bodyX:50,
bodyY:150,
leftArmX:25,
leftArmY:90,
rightArmX:75,
rightArmY:90,
leftLegX:30,
leftLegY:200,
rightLegX:70,
rightLegY:200,
};
var setPos = function(x, y) {
canvas.style.left = x+'px';
canvas.style.top = (window.innerHeight-y-200)+'px';
};
var drawMan = function(time) {
setPos(pos.x, 0);
ctx.strokeStyle = '#000000';
ctx.lineWidth = 5;
ctx.beginPath();
ctx.arc(pos.headX, pos.headY, 20, 0, Math.PI*2, false);
ctx.moveTo(pos.headX, pos.headY);
ctx.lineTo(pos.bodyX, pos.bodyY);
ctx.lineTo(pos.rightLegX, pos.rightLegY);
ctx.moveTo(pos.bodyX, pos.bodyY);
ctx.lineTo(pos.leftLegX, pos.leftLegY);
ctx.moveTo((pos.bodyX+pos.headX)/2, ((pos.bodyY+pos.headY)/5)*2);
ctx.lineTo(pos.rightArmX, pos.rightArmY);
ctx.moveTo((pos.bodyX+pos.headX)/2, ((pos.bodyY+pos.headY)/5)*2);
ctx.lineTo(pos.leftArmX, pos.leftArmY);
ctx.stroke();
ctx.fillStyle = '#888888';
ctx.beginPath();
ctx.arc(pos.headX, pos.headY, 20, 0, Math.PI*2, false);
ctx.fill();
if(running) {
window.requestAnimationFrame(drawMan);
}
};
drawMan();
document.body.appendChild(canvas);
browser.runtime.onMessage.addListener(function(m) {
if(m === 'End' && running === true) {
running = false;
document.body.removeChild(canvas);
} else if(m === 'Draw' && running === false) {
running = true;
document.body.appendChild(canvas);
}
});
Functionality demo [Note1: You must navigate to an actual webpage. Note2: The tooltips that pop up to tell you what the title is of your browser_action button are not captured with the program I used to create the following .gif. Note3: I added the browser_style property to the browser_action in your manifest.json file. It is new in Firefox 48. Without it, Firefox will issue a warning in the Browser Console when the add-on is loaded.]:

google translator toolkit - how to exclude portions of the text from being processed

Does anybody know whether there is a possiblity to exclude portions of a text from being processed by the google translator toolkit?
A great advantage of this tool is that it suggests the translations of sentences which have already been translated in another context. However, if any additional footnote and/or remark has been added to the text, it won't be recognized as a match. I am looking for a possibility to enclose such text in "brackets" within which it will be ignored.
For example, the following two strings should be recognized as identical:
"This is one continuous sentence."
"This is {this text will be ignored}one continuous sentence."
and be translated i.e. into German as:
"Dies ist ein zusammenhängender Satz."
"Dies is {this text will be ignored}ein zusammenhängender Satz."
If neccessary I could number such insertions and place their content into additional paragraphs like:
"This is one continuous sentence."
"This is {1}one continuous sentence."
"{1 this text will be ignored}
thanks a lot in advance,
Marcel
Quoting from the Google Translate FAQ ...
How do I tell Cloud Translation API to NOT translate something?
You can use the following HTML tags:
<span translate="no"> </span>
<span class="notranslate"> </span>
This functionality requires the source text to be submitted in HTML.
If it's "not working for you" a likely reason is that you requested TEXT translation not HTML translation. If you only want TEXT translation then wrapper your plan text in HTML tags (with <span> tags as above) and then unwrap after translation.
Add a span tag with a class of 'skiptranslate' to the bits that you do not want to be translated, like this:
"This is <span class="skiptranslate">this text will be ignored</span> one continuous sentence."
The "skiptranslate" class didn't work for me in the po files I submitted to gtt. Gtt continued to translate everything. I noticed that gtt does not translate anything inside an href attribute, so what I did was to pre-process my .po file (using a "copy" grunt task) by changing {{my_expr}} type strings to <a href='{{my_expr}}/> strings, then changing them back to {{my_expr}} after the gtt translation (using another "copy" grunt task).
I'm not sure how this affects the semantics of the translation, but at least the resulting translation doesn't break my templating code.
Here is my grunt copy task config showing the regular expressions I used:
copy: {
fixupPoFileForTranslation: {
src: [], // Fill in src and dest!
dest: '',
options : {
process: function (content, srcpath) {
return content.replace(/"Go page"/g, '"Go"')
// Change handlebars {{<name>}} to <a ref='<name>'/> to stop
// machine translation from translating them.
return content.replace(/(\{\{[a-zA-Z_\$].*?\}\})/g, '<a href=\'$1\'/>')
// Same thing for our js .format {0}, {1}, ...
.replace(/(\{\d*?\})/g, '<a href=\'$1\'/>');
}
}
},
fixupPoFileForMerge: {
src: [],
dest: '',
options : {
process: function (content, srcpath) {
// Restore <a href=... back to {{<name>}}
return content..replace(/<a href='(\{\{[a-zA-Z_\$].*?\}\})'(\/>|)/gi, '$1')
// Same thing for {0} constructs
.replace(/<a href=' *(\{.\d?\})('\/>|)/gi, '$1');
}
}
}
}

Webix: how to dynamically load components from external js file into a layout?

I would like to know how to update a row/col and or layout from an external js file on webix. Let's say I have a menu at the left of the screen (col[]) and want to update the right column based on a menu selection. If the menu is composed by
Customers
Orders
Products
And want to update the right column based on the selection calling customers.js, orders.js and products.js
Just like http://webix.com/demos/admin-app/#!/app/orders
That example is very advanced for me, I would like to learn some basic method.
Thanks
Oscar P
You can use webix.ui command or addView API to add a new UI to some specific place on the page.
webix.ajax("config.json", function(text){
$$("layout").addView( JSON.parse(text) );
})
or
webix.ajax("config.json", function(text){
webix.ui( JSON.parse(text), $$("cell_to_replace"));
})
Aquatic,
I also found out an example with .showBatch() API but the console shows "Uncaught TypeError: $$(...).addView is not a function" on both functions, are those part of PRO version? what is wrong?
My code is:
$$("workArea").addView("clientes");
or
$$("workArea").showBatch("batClientes");
on:{
onMenuItemClick:function(id) {
$$("workArea").addView(id);
}
}

Resources