The implementation of this command is in Default/transform.py
:
class Transformer(sublime_plugin.TextCommand):
def run(self, edit):
view = self.view
for s in view.sel():
if s.empty():
s = view.word(s)
txt = self.transformer(view.substr(s))
view.replace(edit, s, txt)
class TitleCaseCommand(Transformer):
@staticmethod
def transformer(s):
return string.capwords(s, " ")
As seen here, the command operates by extracting each block of selected text and then running string.capwords()
on it. The documentation for this method says:
If the optional second argument sep is absent or None
, runs of whitespace characters are replaced by a single space and leading and trailing whitespace are removed, otherwise sep is used to split and join the words.
Since the call here is passing in the second argument as a space, the underlying Python interpreter splits the output into words using spaces, which is the basis of the problem; the newlines that separate the lines of text are not being considered as word separators.
That results in something like this given your example:
>>> view.substr(view.sel()[0]).split(" ")
['real', 'PROBLEM\nLaw', 'suites\nreputation\nexternal',
'certification', 'of', 'the', 'development', 'processes\nAPT', '–',
'hackers', 'and', 'nation', 'states']
Given that, Law
is made lower case because it’s actually part of a larger word, reputation
and external
are similarly not individual words, and so on.
One way around that would be to split the selection into lines before you execute the command; in that case every line would be considered in turn and it would work as expected.
Alternately, you could use an override to replace the code for TitleCaseCommand
to use the title()
method instead:
class TitleCaseCommand(Transformer):
@staticmethod
def transformer(s):
return s.title()
It may be worth logging a bug on the issue tracker for this; there’s not any clear indication that I can see that this should only be applied to single lines and not blocks of text. It also does the wrong thing if the text contains physical tabs (even if you split the selection into lines).