Sublime Forum

Auto-pair typographical quotes

#15

Your “obligatory” XKCD reference is really funny.
My knowledge about regular expressions is very limited but you’re right: it’s mandatory to properly use Sublime Text. Give me time. And yes: texts are my life.
I’ll continue to test and I’ll report.
Best regards.

0 Likes

#16

So far, everything works according to my needs. Only a problem: after opening parenthesis (), when I attempt to write quotes, the quotes I get are the closing quotes: ” (only).

0 Likes

#17

I am assuming the caret is between the parentheses like so:

(|)

And that what you want as an outcome is:

(“|”)

Fixing this issue requires some thinking, which I am not currently able to do. (I am next to the sea and the sea beckons.)

Here’s a highly inelegant hack to get you by, written for double-quotes. You will need to change the “smart quotes” section:

// Smart quotes before & after word
{ "keys": "\""], "command": "insert_snippet", "args": {"contents": "“$0"}, "context":
	
		{ "key": "setting.auto_match_enabled", "operator": "equal", "operand": true },
		{ "key": "selection_empty", "operator": "equal", "operand": true, "match_all": true },
		{ "key": "following_text", "operator": "regex_contains", "operand": "^\\S", "match_all": true },
		{ "key": "preceding_text", "operator": "not_regex_contains", "operand": "($", "match_all": true },
		{ "key": "following_text", "operator": "not_regex_contains", "operand": "^(“|”)", "match_all": true },
		{ "key": "selector", "operator": "equal", "operand": "text", "match_all": true }
	]
},
{ "keys": "\""], "command": "insert_snippet", "args": {"contents": "”$0"}, "context":
	
		{ "key": "setting.auto_match_enabled", "operator": "equal", "operand": true },
		{ "key": "selection_empty", "operator": "equal", "operand": true, "match_all": true },
		{ "key": "preceding_text", "operator": "regex_contains", "operand": "\\S$", "match_all": true },
		{ "key": "preceding_text", "operator": "not_regex_contains", "operand": "”(]$", "match_all": true },
		{ "key": "following_text", "operator": "not_regex_contains", "operand": "^(“|”)", "match_all": true },
		{ "key": "selector", "operator": "equal", "operand": "text", "match_all": true }
	]
},

I haven’t tested it much, so it might introduce other issues.

The issue, I think, boils down on what to do when the caret is between two characters, e.g.:

x|y

For x and y are there values other than parentheses that you would like to the auto-pair quotes to be triggered?

Alex

0 Likes

#18

You are perfectly understanding my goal (“”). I am in a also hurry but I’ll test as soon as possible and I’ll report. Anyhow, don’t worry: I can wait. It’s not urgent at all. Thank you very much again.

0 Likes

#19

You were right. The new version has undesirable side-effects. For example: if I attempt to add quotes to an existent text, the previous version, very cleverly, avoided auto-pair and put only opening or closing quotes. Now, I get non-typographical quotes ("). Anyhow, I’ll continue to test for a while, even if the old version is much preferable for me.
Other values for x and y other than parentheses? Brackets ] and, if possible, recursive quoting, already mentioned in a previous message: “‘lorem’ etc. / lorem’” etc.
Anyhow, take it easy. The old code works very well.
Best regards.

0 Likes

#20

Hi,

I was looking over the existing bindings and I couldn’t figure out what purpose was served by certain things, so I quickly re-wrote it. I’ve taken a lot of stuff out, but I think the functionality is the same (?). I’ve left some comments in there, as well as some stuff I’m not sure about, but if you get back to me soon enough I will hopefully remember why I’ve left them in :smile:

It would be very helpful if I could find an example document to type, which would highlight potential issues.

Anyway, let me know if this is better or worse. (Oh, by the way, this includes both double and singe quotes; I think it makes sense to look at these together. Guillemets, etc. are another story and I don’t know enough about them.)

/*** Smart Double Quotes (“”) ***/
// Surround selection with smart quotes // OK, I think
{ "keys": "\""], "command": "insert_snippet", "args": {"contents": "“${0:$SELECTION}”"}, "context":
	
		{ "key": "setting.auto_match_enabled", "operator": "equal", "operand": true },
		{ "key": "selection_empty", "operator": "equal", "operand": false, "match_all": true },
		{ "key": "selector", "operator": "equal", "operand": "text", "match_all": true }
	]
},
// Auto-pair smart quotes // OK -- but what about following/preceding regexs?
{ "keys": "\""], "command": "insert_snippet", "args": {"contents": "“$0”"}, "context":
	
		{ "key": "setting.auto_match_enabled", "operator": "equal", "operand": true },
		{ "key": "selection_empty", "operator": "equal", "operand": true, "match_all": true },
		// { "key": "following_text", "operator": "regex_contains", "operand": "^(?:\t| |\\)|”|;|\\}|$)", "match_all": true }, // does commenting it out alter anything?
		{ "key": "selector", "operator": "equal", "operand": "text", "match_all": true }
	]
},
// Smart quote before word
{ "keys": "\""], "command": "insert_snippet", "args": {"contents": "“$0"}, "context":
	
		{ "key": "setting.auto_match_enabled", "operator": "equal", "operand": true },
		{ "key": "selection_empty", "operator": "equal", "operand": true, "match_all": true },
		{ "key": "following_text", "operator": "reg ex_contains", "operand": "^\\S", "match_all": true },
		{ "key": "following_text", "operator": "not_regex_contains", "operand": "^(“|”|‘|’|\\)|])", "match_all": true },
		{ "key": "selector", "operator": "equal", "operand": "text", "match_all": true }
	]
},
// Smart quote after word
{ "keys": "\""], "command": "insert_snippet", "args": {"contents": "”$0"}, "context":
	
		{ "key": "setting.auto_match_enabled", "operator": "equal", "operand": true },
		{ "key": "selection_empty", "operator": "equal", "operand": true, "match_all": true },
		{ "key": "preceding_text", "operator": "regex_contains", "operand": "\\S$", "match_all": true },
		{ "key": "following_text", "operator": "not_regex_contains", "operand": "^(“|”|‘|’|\\)|])", "match_all": true },
		{ "key": "selector", "operator": "equal", "operand": "text", "match_all": true }
	]
},
// Do not overwrite smart quotes with dumb quotes // OK, I think
{ "keys": "\""], "command": "move", "args": {"by": "characters", "forward": true}, "context":
	
		{ "key": "setting.auto_match_enabled", "operator": "equal", "operand": true },
		{ "key": "selection_empty", "operator": "equal", "operand": true, "match_all": true },
		{ "key": "following_text", "operator": "regex_contains", "operand": "^(“|”)", "match_all": true },
		{ "key": "selector", "operator": "equal", "operand": "text", "match_all": true }
	]
},
{ "keys": "backspace"], "command": "run_macro_file", "args": {"file": "Packages/Default/Delete Left Right.sublime-macro"}, "context":
	
		{ "key": "setting.auto_match_enabled", "operator": "equal", "operand": true },
		{ "key": "selection_empty", "operator": "equal", "operand": true, "match_all": true },
		{ "key": "preceding_text", "operator": "regex_contains", "operand": "“$", "match_all": true },
		{ "key": "following_text", "operator": "regex_contains", "operand": "^”", "match_all": true },
		{ "key": "selector", "operator": "equal", "operand": "text", "match_all": true }
	]
},
/*** Smart Single Quotes (‘’) ***/
// Surround selection with smart quotes // OK, I think
{ "keys": "'"], "command": "insert_snippet", "args": {"contents": "‘${0:$SELECTION}’"}, "context":
	
		{ "key": "setting.auto_match_enabled", "operator": "equal", "operand": true },
		{ "key": "selection_empty", "operator": "equal", "operand": false, "match_all": true },
		{ "key": "selector", "operator": "equal", "operand": "text", "match_all": true }
	]
},
// Auto-pair smart quotes // OK -- but what about following/preceding regexs?
{ "keys": "'"], "command": "insert_snippet", "args": {"contents": "‘$0’"}, "context":
	
		{ "key": "setting.auto_match_enabled", "operator": "equal", "operand": true },
		{ "key": "selection_empty", "operator": "equal", "operand": true, "match_all": true },
		// { "key": "following_text", "operator": "regex_contains", "operand": "^(?:\t| |\\)|’|;|\\}|$)", "match_all": true }, // does commenting it out alter anything?
		{ "key": "selector", "operator": "equal", "operand": "text", "match_all": true }
	]
},
// Smart quote before word
{ "keys": "'"], "command": "insert_snippet", "args": {"contents": "‘$0"}, "context":
	
		{ "key": "setting.auto_match_enabled", "operator": "equal", "operand": true },
		{ "key": "selection_empty", "operator": "equal", "operand": true, "match_all": true },
		{ "key": "following_text", "operator": "regex_contains", "operand": "^\\S", "match_all": true },
		{ "key": "following_text", "operator": "not_regex_contains", "operand": "^(“|”|‘|’|\\)|])", "match_all": true },
		{ "key": "selector", "operator": "equal", "operand": "text", "match_all": true }
	]
},
// Smart quote after word
{ "keys": "'"], "command": "insert_snippet", "args": {"contents": "’$0"}, "context":
	
		{ "key": "setting.auto_match_enabled", "operator": "equal", "operand": true },
		{ "key": "selection_empty", "operator": "equal", "operand": true, "match_all": true },
		{ "key": "preceding_text", "operator": "regex_contains", "operand": "\\S$", "match_all": true },
		{ "key": "preceding_text", "operator": "not_regex_contains", "operand": "(“|‘)$", "match_all": true },
		// { "key": "following_text", "operator": "not_regex_contains", "operand": "^(“|”|‘|’|\\)|])", "match_all": true },
		{ "key": "following_text", "operator": "not_regex_contains", "operand": "^(“|‘|’|\\)|])", "match_all": true },
		{ "key": "selector", "operator": "equal", "operand": "text", "match_all": true }
	]
},
// Do not overwrite smart quotes with dumb quotes // OK, I think
{ "keys": "'"], "command": "move", "args": {"by": "characters", "forward": true}, "context":
	
		{ "key": "setting.auto_match_enabled", "operator": "equal", "operand": true },
		{ "key": "selection_empty", "operator": "equal", "operand": true, "match_all": true },
		{ "key": "following_text", "operator": "regex_contains", "operand": "^(‘|’)", "match_all": true },
		{ "key": "selector", "operator": "equal", "operand": "text", "match_all": true }
	]
},
{ "keys": "backspace"], "command": "run_macro_file", "args": {"file": "Packages/Default/Delete Left Right.sublime-macro"}, "context":
	
		{ "key": "setting.auto_match_enabled", "operator": "equal", "operand": true },
		{ "key": "selection_empty", "operator": "equal", "operand": true, "match_all": true },
		{ "key": "preceding_text", "operator": "regex_contains", "operand": "‘$", "match_all": true },
		{ "key": "following_text", "operator": "regex_contains", "operand": "^’", "match_all": true },
		{ "key": "selector", "operator": "equal", "operand": "text", "match_all": true }
	]
},

Alex

1 Like

Insert smart quotes into Sublime
#21

Thank you very much again. I’ll test the new version and I’ll report.

0 Likes

#22

Don’t worry about guillemets. If necessary, I can adapt your code to include french quotes.

0 Likes

#23

First feedback about the new code:
–If I add a quote before an existent word, first version only added “. Now, both appears. No problem after a word.
–After a dash (-, n or m dash), the new code adds only ”, instead of “”.
No other problems, for the moment. I’ll continue to test. By the way: recursive quoting is solved!

0 Likes

#24

[quote=“pedrober”]First feedback about the new code:
–If I add a quote before an existent word, first version only added “. Now, both appears. No problem after a word.[/quote]

A typo had crept it in. It took me all of 10 seconds to fix this, but I thought rather than posting and re-posting here, I might look at other issues as well, but I didn’t have the time, so it’s just this fix for now. I thought it might be simpler for both of us if I put up a repo and use that, so: github.com/alehandrof/SmartType

You should be able to add the repository via Package Control and then just install the package “SmartType”. (If not, or if you need more detailed instructions, let me know.)

I’m still trying to get to grips with git (as I’m not a programmer, but I’ve been using it for version control for documents).

I haven’t taken hyphens & dashes into account at all. I’ll look at it tomorrow (maybe :wink: ).

Yeah, but not really. Let me know how it goes :smile:

Alex

0 Likes

#25

The forum was off-line yesterday. I have just installed the package. I’ll test and I’ll report later.

0 Likes

#26

First feedback about the package:
–As previously stated, after a dash (-, –, —), the package adds only ”, instead of “”.
–Same problem, after a existent parenthesis or square bracket. The package outputs: (”xxxxx), ”xxxxx].
No more problems, for the moment.

0 Likes

#27

Just a note: in the case of an existent parenthesis or square bracket, the expected behaviour is (“xxxxx)/“xxxxx or (xxxxx”)/[xxxxx”].

0 Likes

#28

New version in the repo. Let me know what stuff explodes.

If you’re using Package Control to handle the package, it should update automatically.

0 Likes

#29

OK, I’ll report after testing. Thank you very much. New features are welcome.

0 Likes

#30

No problem for the moment. I had macros to get n dash and m dash but your package makes it easier.
Let me encourage you to add settings to be able to switch ellipsis off. It’s a very controversial matter in good typography. So, I have modified your key bindings to switch it off.

0 Likes

#31

The n and m dash automatic substitution doesn’t work after a word. Ex: XXXX-- and not XXXX–
They also don’t work before and after an existent word ONLY at the beginning of a paragraph.

0 Likes

#32

Glad I could help. If there’s any other macros that you think might be improved upon, let me know—although see below.

I love a good typographic controversy, but I’m not aware of this one *. Nevertheless, I’ve turned off ellipsis, since you’re the only user at the moment :smile:

  • It seems to me the only alternative to the ellipsis glyph is spaced out dots (. . .) which the macro doesn’t interfer with. Can you point me to any reading material (online, books, articles, whatever) related to this?

[quote]The n and m dash automatic substitution doesn’t work after a word. Ex: XXXX-- and not XXXX–
They also don’t work before and after an existent word ONLY at the beginning of a paragraph.[/quote]

There was a silly bug with the em-dash, which was causing all sorts of weirdness. It’s fixed now.

You’ll still run into trouble if you want to write, for example, A–Z (that’s an en-dash) because the way I’ve set up the macro it only works for space followed by two hyphens.

If you can list the various situations you need/use em- and en- dashes, I can tell you whether we can work out something automagical. But I’ve found that MS Word is crap at this sort of thing, and its auto-replace feature is more powerful than what my current knowledge of Sublime allows for.

As always, feedback, &c., is welcome.

Alex

0 Likes

#33

Thank you very much again. I’ll update and I’ll continue to test.
It’s possible to perform a Google search to find information about the ellipsis and its use in good typography. Anyhow, the key point is as follows: in most of the typographic families, the dots of the ellipsis character are too tightly spaced and the designer uses other resources. Don’t think I am a designer but just test a few faces and see.
By the way, I hate Word and its behavior in this area (auto-correction)…

0 Likes

#34

The only user of your package… I can’t believe it! There are a lot of people using Sublime Text for prose. Maybe if the package were in the main repository…
More feedback:
— n dash replacement doesn’t work at the beginning of a paragraph (no problem with m dash). Is this by design?
— n dash replacement doesn’t work after an existent word (no problem with m dash).
— m dash replacement doesn’t work before an existent word (no problem with n dash).
I’ll continue testing…

0 Likes