Repository: sphinx-doc/sphinx

Issue:
code-block default language
Hi,
it would be useful and smart to let the language in code-block to be the global one by default. For instance if I write

```
.. highlight:: c

.. code-block::

   int a;
```

I would expect the code block highlighting to be in c. Does it make sense? 


Discussion:

Patch:
diff --git a/CHANGES b/CHANGES
index 9eb401173..21abcc4d9 100644
--- a/CHANGES
+++ b/CHANGES
@@ -167,6 +167,8 @@ Features added
 * #1341 the HTML search considers words that contain a search term of length
   three or longer a match.
 * #4611: epub: Show warning for duplicated ToC entries
+* #1851: Allow to omit an argument for :rst:dir:`code-block` directive.  If
+  omitted, it follows :rst:dir:`highlight` or :confval:`highlight_language`
 
 Bugs fixed
 ----------
diff --git a/doc/usage/restructuredtext/directives.rst b/doc/usage/restructuredtext/directives.rst
index 947146dd2..85ee22ae9 100644
--- a/doc/usage/restructuredtext/directives.rst
+++ b/doc/usage/restructuredtext/directives.rst
@@ -463,7 +463,7 @@ __ http://pygments.org/docs/lexers/
 
    This will produce line numbers for all code blocks longer than five lines.
 
-.. rst:directive:: .. code-block:: language
+.. rst:directive:: .. code-block:: [language]
 
    Example::
 
@@ -471,9 +471,11 @@ __ http://pygments.org/docs/lexers/
 
          Some Ruby code.
 
-   The directive's alias name :rst:dir:`sourcecode` works as well.  As with
-   :rst:dir:`highlight`\ 's ``language`` option, ``language`` can be any lexer
-   alias supported by Pygments.
+   The directive's alias name :rst:dir:`sourcecode` works as well.  This
+   directive takes a language name as an argument.  It can be any lexer alias
+   supported by Pygments.  If it is not given, the setting of
+   :rst:dir:`highlight` directive will be used.  If not set,
+   :confval:`highlight_language` will be used.
 
    **Additional options**
 
@@ -533,6 +535,9 @@ __ http://pygments.org/docs/lexers/
    .. versionchanged:: 1.6.6
       LaTeX supports the ``emphasize-lines`` option.
 
+   .. versionchanged:: 2.0
+      The ``language`` argument becomes optional.
+
 .. rst:directive:: .. literalinclude:: filename
 
    Longer displays of verbatim text may be included by storing the example text
diff --git a/sphinx/directives/code.py b/sphinx/directives/code.py
index 106d400b9..9ac704c55 100644
--- a/sphinx/directives/code.py
+++ b/sphinx/directives/code.py
@@ -47,9 +47,11 @@ class Highlight(SphinxDirective):
 
     def run(self):
         # type: () -> List[nodes.Node]
+        language = self.arguments[0].strip()
         linenothreshold = self.options.get('linenothreshold', sys.maxsize)
-        return [addnodes.highlightlang(lang=self.arguments[0].strip(),
-                                       linenothreshold=linenothreshold)]
+
+        self.env.temp_data['highlight_language'] = language
+        return [addnodes.highlightlang(lang=language, linenothreshold=linenothreshold)]
 
 
 class HighlightLang(Highlight):
@@ -110,8 +112,8 @@ class CodeBlock(SphinxDirective):
     """
 
     has_content = True
-    required_arguments = 1
-    optional_arguments = 0
+    required_arguments = 0
+    optional_arguments = 1
     final_argument_whitespace = False
     option_spec = {
         'linenos': directives.flag,
@@ -152,10 +154,20 @@ def run(self):
             code = '\n'.join(lines)
 
         literal = nodes.literal_block(code, code)  # type: nodes.Element
-        literal['language'] = self.arguments[0]
         literal['linenos'] = 'linenos' in self.options or \
                              'lineno-start' in self.options
         literal['classes'] += self.options.get('class', [])
+        if self.arguments:
+            # highlight language specified
+            literal['language'] = self.arguments[0]
+            literal['force_highlighting'] = True
+        else:
+            # no highlight language specified.  Then this directive refers the current
+            # highlight setting via ``highlight`` directive or ``highlight_language``
+            # configuration.
+            literal['language'] = self.env.temp_data.get('highlight_language',
+                                                         self.config.highlight_language)
+            literal['force_highlighting'] = False
         extra_args = literal['highlight_args'] = {}
         if hl_lines is not None:
             extra_args['hl_lines'] = hl_lines
diff --git a/sphinx/transforms/post_transforms/code.py b/sphinx/transforms/post_transforms/code.py
index c873dd5bc..fb8533e9c 100644
--- a/sphinx/transforms/post_transforms/code.py
+++ b/sphinx/transforms/post_transforms/code.py
@@ -89,7 +89,7 @@ def visit_literal_block(self, node):
         if 'language' not in node:
             node['language'] = setting.language
             node['force_highlighting'] = False
-        else:
+        elif 'force_highlighting' not in node:
             node['force_highlighting'] = True
         if 'linenos' not in node:
             lines = node.astext().count('\n')


Test patch:
diff --git a/tests/roots/test-directive-code/highlight.rst b/tests/roots/test-directive-code/highlight.rst
new file mode 100644
index 000000000..4191b58c4
--- /dev/null
+++ b/tests/roots/test-directive-code/highlight.rst
@@ -0,0 +1,20 @@
+highlight
+---------
+
+.. code-block::
+
+   "A code-block without no language"
+
+.. code-block:: python2
+
+   "A code-block with language argument"
+
+.. highlight:: python3
+
+.. code-block::
+
+   "A code-block without no language after highlight directive"
+
+.. code-block:: python2
+
+   "A code-block without language argument after highlight directive"
diff --git a/tests/test_directive_code.py b/tests/test_directive_code.py
index 6e60d0e9b..8627820b9 100644
--- a/tests/test_directive_code.py
+++ b/tests/test_directive_code.py
@@ -11,6 +11,7 @@
 import os
 
 import pytest
+from docutils import nodes
 
 from sphinx.config import Config
 from sphinx.directives.code import LiteralIncludeReader
@@ -552,3 +553,15 @@ def test_literalinclude_pydecorators(app, status, warning):
         '    pass\n'
     )
     assert actual == expect
+
+
+@pytest.mark.sphinx('dummy', testroot='directive-code')
+def test_code_block_highlighted(app, status, warning):
+    app.builder.build(['highlight'])
+    doctree = app.env.get_doctree('highlight')
+    codeblocks = doctree.traverse(nodes.literal_block)
+
+    assert codeblocks[0]['language'] == 'default'
+    assert codeblocks[1]['language'] == 'python2'
+    assert codeblocks[2]['language'] == 'python3'
+    assert codeblocks[3]['language'] == 'python2'