This is an automated email from the ASF dual-hosted git repository. hansva pushed a commit to branch main in repository https://gitbox.apache.org/repos/asf/hop.git
The following commit(s) were added to refs/heads/main by this push:
new 95efe22b3f issue #7470 : Python script support, also for Hop Web
(#7475)
95efe22b3f is described below
commit 95efe22b3f5add03ff8f4f6a99377a866810e5d7
Author: Matt Casters <[email protected]>
AuthorDate: Fri Jul 10 09:26:19 2026 +0200
issue #7470 : Python script support, also for Hop Web (#7475)
---
.../hop/workflow/actions/shell/ActionShell.java | 28 +
.../shell/messages/messages_en_US.properties | 2 +
.../shell/ActionShellReferencedObjectTest.java | 85 +
.../apache/hop/workflow/actions/sql/ActionSql.java | 29 +
.../actions/sql/messages/messages_en_US.properties | 1 +
.../actions/sql/ActionSqlReferencedObjectTest.java | 85 +
.../transforms/types/BatExplorerFileType.java | 65 +
.../transforms/types/PythonExplorerFileType.java | 66 +
.../types/PythonExplorerFileTypeHandler.java | 37 +
.../transforms/types/ShellExplorerFileType.java | 65 +
.../textfile/src/main/resources/python.svg | 33 +
.../types/PythonExplorerFileTypeTest.java | 91 +
.../hop/ui/hopgui/ContentEditorTm4eSupport.java | 7 +-
.../hopgui/RuleBasedSourceViewerConfiguration.java | 5 +-
.../org/apache/hop/ui/hopgui/grammars/python.json | 4213 ++++++++++++++++++++
.../ui/hopgui/ContentEditorTm4eSupportTest.java | 47 +
16 files changed, 4856 insertions(+), 3 deletions(-)
diff --git
a/plugins/actions/shell/src/main/java/org/apache/hop/workflow/actions/shell/ActionShell.java
b/plugins/actions/shell/src/main/java/org/apache/hop/workflow/actions/shell/ActionShell.java
index 506581a8c1..14725046a3 100644
---
a/plugins/actions/shell/src/main/java/org/apache/hop/workflow/actions/shell/ActionShell.java
+++
b/plugins/actions/shell/src/main/java/org/apache/hop/workflow/actions/shell/ActionShell.java
@@ -39,6 +39,7 @@ import org.apache.hop.core.RowMetaAndData;
import org.apache.hop.core.annotations.Action;
import org.apache.hop.core.exception.HopException;
import org.apache.hop.core.exception.HopXmlException;
+import org.apache.hop.core.file.IHasFilename;
import org.apache.hop.core.logging.FileLoggingEventListener;
import org.apache.hop.core.logging.HopLogStore;
import org.apache.hop.core.logging.LogLevel;
@@ -655,4 +656,31 @@ public class ActionShell extends ActionBase {
AndValidator.putValidators(ActionValidatorUtils.notBlankValidator()));
}
}
+
+ @Override
+ public String[] getReferencedObjectDescriptions() {
+ if (insertScript) {
+ return null;
+ }
+ return new String[] {
+ BaseMessages.getString(PKG, "ActionShell.ReferencedObject.Description"),
+ };
+ }
+
+ @Override
+ public boolean[] isReferencedObjectEnabled() {
+ if (insertScript) {
+ return null;
+ }
+ return new boolean[] {!Utils.isEmpty(filename)};
+ }
+
+ @Override
+ public IHasFilename loadReferencedObject(
+ int index, IHopMetadataProvider metadataProvider, IVariables variables)
throws HopException {
+ if (index != 0 || insertScript || Utils.isEmpty(filename)) {
+ throw new HopException(BaseMessages.getString(PKG,
"ActionShell.NoScriptFileSpecified"));
+ }
+ return () -> filename;
+ }
}
diff --git
a/plugins/actions/shell/src/main/resources/org/apache/hop/workflow/actions/shell/messages/messages_en_US.properties
b/plugins/actions/shell/src/main/resources/org/apache/hop/workflow/actions/shell/messages/messages_en_US.properties
index b192625918..b8704ab1fe 100644
---
a/plugins/actions/shell/src/main/resources/org/apache/hop/workflow/actions/shell/messages/messages_en_US.properties
+++
b/plugins/actions/shell/src/main/resources/org/apache/hop/workflow/actions/shell/messages/messages_en_US.properties
@@ -42,6 +42,8 @@ ActionShell.Logfile.IncludeTime.Label=Include time in logfile
ActionShell.LogfileExtension.Label=Extension of logfile
ActionShell.Loglevel.Label=Log level
ActionShell.LogSettings.Group.Label=Logging settings
+ActionShell.NoScriptFileSpecified=Please specify script filename\!
+ActionShell.ReferencedObject.Description=Shell script
ActionShell.Name=Shell
ActionShell.Name.Label=Action name
ActionShell.NameOfLogfile.Label=Name of logfile
diff --git
a/plugins/actions/shell/src/test/java/org/apache/hop/workflow/actions/shell/ActionShellReferencedObjectTest.java
b/plugins/actions/shell/src/test/java/org/apache/hop/workflow/actions/shell/ActionShellReferencedObjectTest.java
new file mode 100644
index 0000000000..b1e1fe5f6b
--- /dev/null
+++
b/plugins/actions/shell/src/test/java/org/apache/hop/workflow/actions/shell/ActionShellReferencedObjectTest.java
@@ -0,0 +1,85 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.hop.workflow.actions.shell;
+
+import static org.junit.jupiter.api.Assertions.assertEquals;
+import static org.junit.jupiter.api.Assertions.assertFalse;
+import static org.junit.jupiter.api.Assertions.assertNotNull;
+import static org.junit.jupiter.api.Assertions.assertNull;
+import static org.junit.jupiter.api.Assertions.assertThrows;
+import static org.junit.jupiter.api.Assertions.assertTrue;
+
+import org.apache.hop.core.exception.HopException;
+import org.apache.hop.core.file.IHasFilename;
+import org.apache.hop.core.variables.Variables;
+import org.apache.hop.junit.rules.RestoreHopEngineEnvironmentExtension;
+import org.apache.hop.metadata.serializer.memory.MemoryMetadataProvider;
+import org.junit.jupiter.api.Test;
+import org.junit.jupiter.api.extension.RegisterExtension;
+
+class ActionShellReferencedObjectTest {
+
+ @RegisterExtension
+ static RestoreHopEngineEnvironmentExtension env = new
RestoreHopEngineEnvironmentExtension();
+
+ @Test
+ void noReferencesWhenScriptIsInline() {
+ ActionShell action = new ActionShell();
+ action.insertScript = true;
+ action.setFilename("/some/script.sh");
+
+ assertNull(action.getReferencedObjectDescriptions());
+ assertNull(action.isReferencedObjectEnabled());
+ }
+
+ @Test
+ void referencedObjectDisabledWhenFilenameEmpty() {
+ ActionShell action = new ActionShell();
+ action.insertScript = false;
+ action.setFilename("");
+
+ assertEquals(1, action.getReferencedObjectDescriptions().length);
+ assertFalse(action.isReferencedObjectEnabled()[0]);
+ }
+
+ @Test
+ void referencedObjectEnabledWhenFilenameSet() throws HopException {
+ ActionShell action = new ActionShell();
+ action.insertScript = false;
+ action.setFilename("${PROJECT_HOME}/scripts/setup.sh");
+
+ assertEquals(1, action.getReferencedObjectDescriptions().length);
+ assertTrue(action.isReferencedObjectEnabled()[0]);
+
+ IHasFilename reference =
+ action.loadReferencedObject(0, new MemoryMetadataProvider(), new
Variables());
+ assertNotNull(reference);
+ assertEquals("${PROJECT_HOME}/scripts/setup.sh", reference.getFilename());
+ }
+
+ @Test
+ void loadReferencedObjectRejectsInvalidIndex() {
+ ActionShell action = new ActionShell();
+ action.insertScript = false;
+ action.setFilename("/scripts/setup.sh");
+
+ assertThrows(
+ HopException.class,
+ () -> action.loadReferencedObject(1, new MemoryMetadataProvider(), new
Variables()));
+ }
+}
diff --git
a/plugins/actions/sql/src/main/java/org/apache/hop/workflow/actions/sql/ActionSql.java
b/plugins/actions/sql/src/main/java/org/apache/hop/workflow/actions/sql/ActionSql.java
index cd6e654698..814762671f 100644
---
a/plugins/actions/sql/src/main/java/org/apache/hop/workflow/actions/sql/ActionSql.java
+++
b/plugins/actions/sql/src/main/java/org/apache/hop/workflow/actions/sql/ActionSql.java
@@ -33,6 +33,8 @@ import org.apache.hop.core.annotations.ActionTransformType;
import org.apache.hop.core.database.Database;
import org.apache.hop.core.database.DatabaseMeta;
import org.apache.hop.core.exception.HopDatabaseException;
+import org.apache.hop.core.exception.HopException;
+import org.apache.hop.core.file.IHasFilename;
import org.apache.hop.core.util.Utils;
import org.apache.hop.core.variables.IVariables;
import org.apache.hop.core.vfs.HopVfs;
@@ -248,4 +250,31 @@ public class ActionSql extends ActionBase implements
Cloneable, IAction {
remarks,
AndValidator.putValidators(ActionValidatorUtils.notBlankValidator()));
}
+
+ @Override
+ public String[] getReferencedObjectDescriptions() {
+ if (!sqlFromFile) {
+ return null;
+ }
+ return new String[] {
+ BaseMessages.getString(PKG, "ActionSQL.ReferencedObject.Description"),
+ };
+ }
+
+ @Override
+ public boolean[] isReferencedObjectEnabled() {
+ if (!sqlFromFile) {
+ return null;
+ }
+ return new boolean[] {!Utils.isEmpty(sqlFilename)};
+ }
+
+ @Override
+ public IHasFilename loadReferencedObject(
+ int index, IHopMetadataProvider metadataProvider, IVariables variables)
throws HopException {
+ if (index != 0 || !sqlFromFile || Utils.isEmpty(sqlFilename)) {
+ throw new HopException(BaseMessages.getString(PKG,
"ActionSQL.NoSQLFileSpecified"));
+ }
+ return () -> sqlFilename;
+ }
}
diff --git
a/plugins/actions/sql/src/main/resources/org/apache/hop/workflow/actions/sql/messages/messages_en_US.properties
b/plugins/actions/sql/src/main/resources/org/apache/hop/workflow/actions/sql/messages/messages_en_US.properties
index 47825a9e1a..c912da207b 100644
---
a/plugins/actions/sql/src/main/resources/org/apache/hop/workflow/actions/sql/messages/messages_en_US.properties
+++
b/plugins/actions/sql/src/main/resources/org/apache/hop/workflow/actions/sql/messages/messages_en_US.properties
@@ -35,6 +35,7 @@ ActionSQL.Name.Default=SQL
ActionSQL.Name.Label=Action name
ActionSQL.NoDatabaseConnection=No database connection is defined.
ActionSQL.NoSQLFileSpecified=Please specify SQL filename\!
+ActionSQL.ReferencedObject.Description=SQL file
ActionSQL.Position.Label=Line {0} Column {1}
ActionSQL.Script.Label=SQL Script\:
ActionSQL.SendOneStatement.Label=Send SQL as single statement
diff --git
a/plugins/actions/sql/src/test/java/org/apache/hop/workflow/actions/sql/ActionSqlReferencedObjectTest.java
b/plugins/actions/sql/src/test/java/org/apache/hop/workflow/actions/sql/ActionSqlReferencedObjectTest.java
new file mode 100644
index 0000000000..581a2fc2b6
--- /dev/null
+++
b/plugins/actions/sql/src/test/java/org/apache/hop/workflow/actions/sql/ActionSqlReferencedObjectTest.java
@@ -0,0 +1,85 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.hop.workflow.actions.sql;
+
+import static org.junit.jupiter.api.Assertions.assertEquals;
+import static org.junit.jupiter.api.Assertions.assertFalse;
+import static org.junit.jupiter.api.Assertions.assertNotNull;
+import static org.junit.jupiter.api.Assertions.assertNull;
+import static org.junit.jupiter.api.Assertions.assertThrows;
+import static org.junit.jupiter.api.Assertions.assertTrue;
+
+import org.apache.hop.core.exception.HopException;
+import org.apache.hop.core.file.IHasFilename;
+import org.apache.hop.core.variables.Variables;
+import org.apache.hop.junit.rules.RestoreHopEngineEnvironmentExtension;
+import org.apache.hop.metadata.serializer.memory.MemoryMetadataProvider;
+import org.junit.jupiter.api.Test;
+import org.junit.jupiter.api.extension.RegisterExtension;
+
+class ActionSqlReferencedObjectTest {
+
+ @RegisterExtension
+ static RestoreHopEngineEnvironmentExtension env = new
RestoreHopEngineEnvironmentExtension();
+
+ @Test
+ void noReferencesWhenSqlIsInline() {
+ ActionSql action = new ActionSql();
+ action.setSqlFromFile(false);
+ action.setSqlFilename("/some/query.sql");
+
+ assertNull(action.getReferencedObjectDescriptions());
+ assertNull(action.isReferencedObjectEnabled());
+ }
+
+ @Test
+ void referencedObjectDisabledWhenFilenameEmpty() {
+ ActionSql action = new ActionSql();
+ action.setSqlFromFile(true);
+ action.setSqlFilename("");
+
+ assertEquals(1, action.getReferencedObjectDescriptions().length);
+ assertFalse(action.isReferencedObjectEnabled()[0]);
+ }
+
+ @Test
+ void referencedObjectEnabledWhenFilenameSet() throws HopException {
+ ActionSql action = new ActionSql();
+ action.setSqlFromFile(true);
+ action.setSqlFilename("${PROJECT_HOME}/queries/setup.sql");
+
+ assertEquals(1, action.getReferencedObjectDescriptions().length);
+ assertTrue(action.isReferencedObjectEnabled()[0]);
+
+ IHasFilename reference =
+ action.loadReferencedObject(0, new MemoryMetadataProvider(), new
Variables());
+ assertNotNull(reference);
+ assertEquals("${PROJECT_HOME}/queries/setup.sql", reference.getFilename());
+ }
+
+ @Test
+ void loadReferencedObjectRejectsInvalidIndex() {
+ ActionSql action = new ActionSql();
+ action.setSqlFromFile(true);
+ action.setSqlFilename("/queries/setup.sql");
+
+ assertThrows(
+ HopException.class,
+ () -> action.loadReferencedObject(1, new MemoryMetadataProvider(), new
Variables()));
+ }
+}
diff --git
a/plugins/transforms/textfile/src/main/java/org/apache/hop/pipeline/transforms/types/BatExplorerFileType.java
b/plugins/transforms/textfile/src/main/java/org/apache/hop/pipeline/transforms/types/BatExplorerFileType.java
new file mode 100644
index 0000000000..e3d1796686
--- /dev/null
+++
b/plugins/transforms/textfile/src/main/java/org/apache/hop/pipeline/transforms/types/BatExplorerFileType.java
@@ -0,0 +1,65 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.hop.pipeline.transforms.types;
+
+import org.apache.hop.core.exception.HopException;
+import org.apache.hop.core.variables.IVariables;
+import org.apache.hop.ui.hopgui.HopGui;
+import org.apache.hop.ui.hopgui.file.HopFileTypePlugin;
+import org.apache.hop.ui.hopgui.file.IHopFileType;
+import org.apache.hop.ui.hopgui.file.IHopFileTypeHandler;
+import org.apache.hop.ui.hopgui.file.empty.EmptyHopFileTypeHandler;
+import org.apache.hop.ui.hopgui.perspective.explorer.ExplorerFile;
+import org.apache.hop.ui.hopgui.perspective.explorer.ExplorerPerspective;
+import
org.apache.hop.ui.hopgui.perspective.explorer.file.capabilities.FileTypeCapabilities;
+import
org.apache.hop.ui.hopgui.perspective.explorer.file.types.text.BaseTextExplorerFileType;
+
+@HopFileTypePlugin(
+ id = "BatExplorerFileType",
+ name = "Windows Batch File Type",
+ description = "Batch file handling in the explorer perspective",
+ image = "ui/images/script.svg")
+public class BatExplorerFileType extends
BaseTextExplorerFileType<TextExplorerFileTypeHandler> {
+
+ public BatExplorerFileType() {
+ super(
+ "Batch file",
+ ".bat",
+ new String[] {"*.bat", "*.BAT"},
+ new String[] {"Batch files"},
+ FileTypeCapabilities.getCapabilities(
+ IHopFileType.CAPABILITY_SAVE,
+ IHopFileType.CAPABILITY_SAVE_AS,
+ IHopFileType.CAPABILITY_CLOSE,
+ IHopFileType.CAPABILITY_FILE_HISTORY,
+ IHopFileType.CAPABILITY_COPY,
+ IHopFileType.CAPABILITY_SELECT));
+ }
+
+ @Override
+ public TextExplorerFileTypeHandler createFileTypeHandler(
+ HopGui hopGui, ExplorerPerspective perspective, ExplorerFile file) {
+ return new TextExplorerFileTypeHandler(hopGui, perspective, file);
+ }
+
+ @Override
+ public IHopFileTypeHandler newFile(HopGui hopGui, IVariables
parentVariableSpace)
+ throws HopException {
+ return new EmptyHopFileTypeHandler();
+ }
+}
diff --git
a/plugins/transforms/textfile/src/main/java/org/apache/hop/pipeline/transforms/types/PythonExplorerFileType.java
b/plugins/transforms/textfile/src/main/java/org/apache/hop/pipeline/transforms/types/PythonExplorerFileType.java
new file mode 100644
index 0000000000..3d61448134
--- /dev/null
+++
b/plugins/transforms/textfile/src/main/java/org/apache/hop/pipeline/transforms/types/PythonExplorerFileType.java
@@ -0,0 +1,66 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.hop.pipeline.transforms.types;
+
+import org.apache.hop.core.exception.HopException;
+import org.apache.hop.core.variables.IVariables;
+import org.apache.hop.ui.hopgui.HopGui;
+import org.apache.hop.ui.hopgui.file.HopFileTypePlugin;
+import org.apache.hop.ui.hopgui.file.IHopFileType;
+import org.apache.hop.ui.hopgui.file.IHopFileTypeHandler;
+import org.apache.hop.ui.hopgui.file.empty.EmptyHopFileTypeHandler;
+import org.apache.hop.ui.hopgui.perspective.explorer.ExplorerFile;
+import org.apache.hop.ui.hopgui.perspective.explorer.ExplorerPerspective;
+import
org.apache.hop.ui.hopgui.perspective.explorer.file.capabilities.FileTypeCapabilities;
+import
org.apache.hop.ui.hopgui.perspective.explorer.file.types.text.BaseTextExplorerFileType;
+
+@HopFileTypePlugin(
+ id = "PythonExplorerFileType",
+ name = "Python Script File Type",
+ description = "Python file handling in the explorer perspective",
+ image = "python.svg")
+public class PythonExplorerFileType
+ extends BaseTextExplorerFileType<PythonExplorerFileTypeHandler> {
+
+ public PythonExplorerFileType() {
+ super(
+ "Python script",
+ ".py",
+ new String[] {"*.py"},
+ new String[] {"Python scripts"},
+ FileTypeCapabilities.getCapabilities(
+ IHopFileType.CAPABILITY_SAVE,
+ IHopFileType.CAPABILITY_SAVE_AS,
+ IHopFileType.CAPABILITY_CLOSE,
+ IHopFileType.CAPABILITY_FILE_HISTORY,
+ IHopFileType.CAPABILITY_COPY,
+ IHopFileType.CAPABILITY_SELECT));
+ }
+
+ @Override
+ public PythonExplorerFileTypeHandler createFileTypeHandler(
+ HopGui hopGui, ExplorerPerspective perspective, ExplorerFile file) {
+ return new PythonExplorerFileTypeHandler(hopGui, perspective, file);
+ }
+
+ @Override
+ public IHopFileTypeHandler newFile(HopGui hopGui, IVariables
parentVariableSpace)
+ throws HopException {
+ return new EmptyHopFileTypeHandler();
+ }
+}
diff --git
a/plugins/transforms/textfile/src/main/java/org/apache/hop/pipeline/transforms/types/PythonExplorerFileTypeHandler.java
b/plugins/transforms/textfile/src/main/java/org/apache/hop/pipeline/transforms/types/PythonExplorerFileTypeHandler.java
new file mode 100644
index 0000000000..9f2a1967b6
--- /dev/null
+++
b/plugins/transforms/textfile/src/main/java/org/apache/hop/pipeline/transforms/types/PythonExplorerFileTypeHandler.java
@@ -0,0 +1,37 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.hop.pipeline.transforms.types;
+
+import org.apache.hop.ui.hopgui.HopGui;
+import org.apache.hop.ui.hopgui.perspective.explorer.ExplorerFile;
+import org.apache.hop.ui.hopgui.perspective.explorer.ExplorerPerspective;
+import
org.apache.hop.ui.hopgui.perspective.explorer.file.types.text.BaseTextExplorerFileTypeHandler;
+
+/** Handler for Python script files in the file explorer perspective
(syntax-highlighted edit). */
+public class PythonExplorerFileTypeHandler extends
BaseTextExplorerFileTypeHandler {
+
+ public PythonExplorerFileTypeHandler(
+ HopGui hopGui, ExplorerPerspective perspective, ExplorerFile
explorerFile) {
+ super(hopGui, perspective, explorerFile);
+ }
+
+ @Override
+ protected String getLanguageId() {
+ return "python";
+ }
+}
diff --git
a/plugins/transforms/textfile/src/main/java/org/apache/hop/pipeline/transforms/types/ShellExplorerFileType.java
b/plugins/transforms/textfile/src/main/java/org/apache/hop/pipeline/transforms/types/ShellExplorerFileType.java
new file mode 100644
index 0000000000..c95ae4f29c
--- /dev/null
+++
b/plugins/transforms/textfile/src/main/java/org/apache/hop/pipeline/transforms/types/ShellExplorerFileType.java
@@ -0,0 +1,65 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.hop.pipeline.transforms.types;
+
+import org.apache.hop.core.exception.HopException;
+import org.apache.hop.core.variables.IVariables;
+import org.apache.hop.ui.hopgui.HopGui;
+import org.apache.hop.ui.hopgui.file.HopFileTypePlugin;
+import org.apache.hop.ui.hopgui.file.IHopFileType;
+import org.apache.hop.ui.hopgui.file.IHopFileTypeHandler;
+import org.apache.hop.ui.hopgui.file.empty.EmptyHopFileTypeHandler;
+import org.apache.hop.ui.hopgui.perspective.explorer.ExplorerFile;
+import org.apache.hop.ui.hopgui.perspective.explorer.ExplorerPerspective;
+import
org.apache.hop.ui.hopgui.perspective.explorer.file.capabilities.FileTypeCapabilities;
+import
org.apache.hop.ui.hopgui.perspective.explorer.file.types.text.BaseTextExplorerFileType;
+
+@HopFileTypePlugin(
+ id = "ShellExplorerFileType",
+ name = "Shell File Type",
+ description = "Shell file handling in the explorer perspective",
+ image = "ui/images/script.svg")
+public class ShellExplorerFileType extends
BaseTextExplorerFileType<TextExplorerFileTypeHandler> {
+
+ public ShellExplorerFileType() {
+ super(
+ "Shell Script",
+ ".sh",
+ new String[] {"*.sh"},
+ new String[] {"Shell scripts"},
+ FileTypeCapabilities.getCapabilities(
+ IHopFileType.CAPABILITY_SAVE,
+ IHopFileType.CAPABILITY_SAVE_AS,
+ IHopFileType.CAPABILITY_CLOSE,
+ IHopFileType.CAPABILITY_FILE_HISTORY,
+ IHopFileType.CAPABILITY_COPY,
+ IHopFileType.CAPABILITY_SELECT));
+ }
+
+ @Override
+ public TextExplorerFileTypeHandler createFileTypeHandler(
+ HopGui hopGui, ExplorerPerspective perspective, ExplorerFile file) {
+ return new TextExplorerFileTypeHandler(hopGui, perspective, file);
+ }
+
+ @Override
+ public IHopFileTypeHandler newFile(HopGui hopGui, IVariables
parentVariableSpace)
+ throws HopException {
+ return new EmptyHopFileTypeHandler();
+ }
+}
diff --git a/plugins/transforms/textfile/src/main/resources/python.svg
b/plugins/transforms/textfile/src/main/resources/python.svg
new file mode 100644
index 0000000000..f11a7387b3
--- /dev/null
+++ b/plugins/transforms/textfile/src/main/resources/python.svg
@@ -0,0 +1,33 @@
+<?xml version="1.0" encoding="utf-8"?>
+<svg version="1.1" xmlns="http://www.w3.org/2000/svg"
+ x="0px" y="0px" width="110px" height="110px"
+ viewBox="0.21 -0.077 110 110"
+ enable-background="new 0.21 -0.077 110 110" xml:space="preserve">
+ <linearGradient id="SVGID_1_" gradientUnits="userSpaceOnUse" x1="63.8159"
y1="56.6829"
+ x2="118.4934" y2="1.8225"
+ gradientTransform="matrix(1 0 0 -1 -53.2974 66.4321)">
+ <stop offset="0" style="stop-color:#387EB8"/>
+ <stop offset="1" style="stop-color:#366994"/>
+ </linearGradient>
+ <path fill="url(#SVGID_1_)"
+
d="M55.023-0.077c-25.971,0-26.25,10.081-26.25,12.156c0,3.148,0,12.594,0,12.594h26.75v3.781
+
c0,0-27.852,0-37.375,0c-7.949,0-17.938,4.833-17.938,26.25c0,19.673,7.792,27.281,15.656,
+ 27.281c2.335,0,9.344,0,9.344,0
s0-9.765,0-13.125c0-5.491,2.721-15.656,15.406-15.656c15.91,
+ 0,19.971,0,26.531,0c3.902,0,14.906-1.696,14.906-14.406
c0-13.452,0-17.89,
+ 0-24.219C82.054,11.426,81.515-0.077,55.023-0.077z
M40.273,8.392c2.662,0,4.813,2.15,
+ 4.813,4.813
c0,2.661-2.151,4.813-4.813,4.813s-4.813-2.151-4.813-4.813C35.46,10.542,
+ 37.611,8.392,40.273,8.392z"/>
+ <linearGradient id="SVGID_2_" gradientUnits="userSpaceOnUse" x1="97.0444"
y1="21.6321"
+ x2="155.6665" y2="-34.5308"
+ gradientTransform="matrix(1 0 0 -1 -53.2974 66.4321)">
+ <stop offset="0" style="stop-color:#FFE052"/>
+ <stop offset="1" style="stop-color:#FFC331"/>
+ </linearGradient>
+ <path fill="url(#SVGID_2_)"
d="M55.397,109.923c25.959,0,26.282-10.271,26.282-12.156c0-3.148,
+ 0-12.594,0-12.594H54.897v-3.781
c0,0,28.032,0,37.375,0c8.009,0,17.938-4.954,
+
17.938-26.25c0-23.322-10.538-27.281-15.656-27.281c-2.336,0-9.344,0-9.344,0
s0,10.216,
+
0,13.125c0,5.491-2.631,15.656-15.406,15.656c-15.91,0-19.476,0-26.532,0c-3.892,0-14.906,1.896-14.906,14.406
+
c0,14.475,0,18.265,0,24.219C28.366,100.497,31.562,109.923,55.397,109.923z
M70.148,101.454c-2.662,
+ 0-4.813-2.151-4.813-4.813
s2.15-4.813,4.813-4.813c2.661,0,4.813,2.151,4.813,4.813S72.809,101.454,
+ 70.148,101.454z"/>
+</svg>
\ No newline at end of file
diff --git
a/plugins/transforms/textfile/src/test/java/org/apache/hop/pipeline/transforms/types/PythonExplorerFileTypeTest.java
b/plugins/transforms/textfile/src/test/java/org/apache/hop/pipeline/transforms/types/PythonExplorerFileTypeTest.java
new file mode 100644
index 0000000000..1e0b25af3a
--- /dev/null
+++
b/plugins/transforms/textfile/src/test/java/org/apache/hop/pipeline/transforms/types/PythonExplorerFileTypeTest.java
@@ -0,0 +1,91 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.hop.pipeline.transforms.types;
+
+import static org.junit.jupiter.api.Assertions.assertEquals;
+import static org.junit.jupiter.api.Assertions.assertFalse;
+import static org.junit.jupiter.api.Assertions.assertInstanceOf;
+import static org.junit.jupiter.api.Assertions.assertNotNull;
+import static org.junit.jupiter.api.Assertions.assertTrue;
+
+import java.lang.reflect.Method;
+import org.apache.hop.ui.hopgui.file.IHopFileType;
+import
org.apache.hop.ui.hopgui.perspective.explorer.file.types.text.BaseTextExplorerFileTypeHandler;
+import org.junit.jupiter.api.BeforeEach;
+import org.junit.jupiter.api.Test;
+
+class PythonExplorerFileTypeTest {
+
+ private PythonExplorerFileType fileType;
+
+ @BeforeEach
+ void setUp() {
+ fileType = new PythonExplorerFileType();
+ }
+
+ @Test
+ void testConstructor() {
+ assertNotNull(fileType);
+ }
+
+ @Test
+ void testGetDefaultFileExtension() {
+ assertEquals(".py", fileType.getDefaultFileExtension());
+ }
+
+ @Test
+ void testGetFilterExtensions() {
+ String[] extensions = fileType.getFilterExtensions();
+ assertNotNull(extensions);
+ assertEquals(1, extensions.length);
+ assertEquals("*.py", extensions[0]);
+ }
+
+ @Test
+ void testGetFilterNames() {
+ String[] names = fileType.getFilterNames();
+ assertNotNull(names);
+ assertEquals(1, names.length);
+ assertEquals("Python scripts", names[0]);
+ }
+
+ @Test
+ void testHasExpectedCapabilities() {
+ assertTrue(fileType.hasCapability(IHopFileType.CAPABILITY_SAVE));
+ assertTrue(fileType.hasCapability(IHopFileType.CAPABILITY_SAVE_AS));
+ assertTrue(fileType.hasCapability(IHopFileType.CAPABILITY_CLOSE));
+ assertTrue(fileType.hasCapability(IHopFileType.CAPABILITY_FILE_HISTORY));
+ assertTrue(fileType.hasCapability(IHopFileType.CAPABILITY_COPY));
+ assertTrue(fileType.hasCapability(IHopFileType.CAPABILITY_SELECT));
+ assertFalse(fileType.hasCapability(IHopFileType.CAPABILITY_NEW));
+ }
+
+ @Test
+ void testCreateFileTypeHandlerReturnsPythonHandler() {
+ assertInstanceOf(
+ PythonExplorerFileTypeHandler.class,
fileType.createFileTypeHandler(null, null, null));
+ }
+
+ @Test
+ void testHandlerReturnsPythonLanguageId() throws Exception {
+ PythonExplorerFileTypeHandler handler = new
PythonExplorerFileTypeHandler(null, null, null);
+ Method method =
BaseTextExplorerFileTypeHandler.class.getDeclaredMethod("getLanguageId");
+ method.setAccessible(true);
+ assertEquals("python", method.invoke(handler));
+ }
+}
diff --git
a/rcp/src/main/java/org/apache/hop/ui/hopgui/ContentEditorTm4eSupport.java
b/rcp/src/main/java/org/apache/hop/ui/hopgui/ContentEditorTm4eSupport.java
index d80f68d1d0..5725c07b68 100644
--- a/rcp/src/main/java/org/apache/hop/ui/hopgui/ContentEditorTm4eSupport.java
+++ b/rcp/src/main/java/org/apache/hop/ui/hopgui/ContentEditorTm4eSupport.java
@@ -52,13 +52,15 @@ final class ContentEditorTm4eSupport {
private static final String SCOPE_JSON = "source.json";
private static final String SCOPE_XML = "text.xml";
private static final String SCOPE_SQL = "source.sql";
+ private static final String SCOPE_PYTHON = "source.python";
/** Maps TM4E scope names to grammar resource filenames (classpath-relative
to grammars/). */
private static final Map<String, String> GRAMMAR_FILES =
Map.of(
SCOPE_JSON, "json.json",
SCOPE_XML, "xml.json",
- SCOPE_SQL, "sql.json");
+ SCOPE_SQL, "sql.json",
+ SCOPE_PYTHON, "python.json");
// Same palette as before (light/dark) for consistency
private static final RGB L_COMMENT = new RGB(128, 128, 128);
@@ -107,6 +109,9 @@ final class ContentEditorTm4eSupport {
return SCOPE_XML;
case "sql":
return SCOPE_SQL;
+ case "python":
+ case "py":
+ return SCOPE_PYTHON;
default:
return null;
}
diff --git
a/rcp/src/main/java/org/apache/hop/ui/hopgui/RuleBasedSourceViewerConfiguration.java
b/rcp/src/main/java/org/apache/hop/ui/hopgui/RuleBasedSourceViewerConfiguration.java
index 91db2f17c4..fef246f722 100644
---
a/rcp/src/main/java/org/apache/hop/ui/hopgui/RuleBasedSourceViewerConfiguration.java
+++
b/rcp/src/main/java/org/apache/hop/ui/hopgui/RuleBasedSourceViewerConfiguration.java
@@ -22,7 +22,8 @@ import org.eclipse.swt.widgets.Display;
/**
* Builds a {@link SourceViewerConfiguration} for the content editor. Uses
TM4E with TextMate
- * grammars for syntax highlighting of JSON, XML, and SQL; other languages get
a plain config.
+ * grammars for syntax highlighting of JSON, XML, SQL, and Python; other
languages get a plain
+ * config.
*/
public final class RuleBasedSourceViewerConfiguration {
@@ -30,7 +31,7 @@ public final class RuleBasedSourceViewerConfiguration {
/**
* Creates a configuration for the given language. Uses TM4E when a grammar
is available (json,
- * xml, sql); otherwise returns a plain configuration with no syntax
highlighting.
+ * xml, sql, python); otherwise returns a plain configuration with no syntax
highlighting.
*
* @param languageId language id (e.g. "json", "xml", "sql"), or null for
plain text
* @return configuration, never null
diff --git
a/rcp/src/main/resources/org/apache/hop/ui/hopgui/grammars/python.json
b/rcp/src/main/resources/org/apache/hop/ui/hopgui/grammars/python.json
new file mode 100644
index 0000000000..91a2c8e756
--- /dev/null
+++ b/rcp/src/main/resources/org/apache/hop/ui/hopgui/grammars/python.json
@@ -0,0 +1,4213 @@
+{
+ "information_for_contributors": [
+ "This file has been converted from
https://github.com/MagicStack/MagicPython/blob/master/grammars/MagicPython.tmLanguage",
+ "If you want to provide a fix or improvement, please create a
pull request against the original repository.",
+ "Once accepted there, we are happy to receive an update
request."
+ ],
+ "version":
"https://github.com/MagicStack/MagicPython/commit/7d0f2b22a5ad8fccbd7341bc7b7a715169283044",
+ "name": "MagicPython",
+ "scopeName": "source.python",
+ "patterns": [
+ {
+ "include": "#statement"
+ },
+ {
+ "include": "#expression"
+ }
+ ],
+ "repository": {
+ "impossible": {
+ "comment": "This is a special rule that should be used
where no match is desired. It is not a good idea to match something like '1{0}'
because in some cases that can result in infinite loops in token generation. So
the rule instead matches and impossible expression to allow a match to fail and
move to the next token.",
+ "match": "$.^"
+ },
+ "statement": {
+ "patterns": [
+ {
+ "include": "#import"
+ },
+ {
+ "include": "#class-declaration"
+ },
+ {
+ "include": "#function-declaration"
+ },
+ {
+ "include": "#generator"
+ },
+ {
+ "include": "#statement-keyword"
+ },
+ {
+ "include": "#assignment-operator"
+ },
+ {
+ "include": "#decorator"
+ },
+ {
+ "include": "#docstring-statement"
+ },
+ {
+ "include": "#semicolon"
+ }
+ ]
+ },
+ "semicolon": {
+ "patterns": [
+ {
+ "name":
"invalid.deprecated.semicolon.python",
+ "match": "\\;$"
+ }
+ ]
+ },
+ "comments": {
+ "patterns": [
+ {
+ "name":
"comment.line.number-sign.python",
+ "contentName":
"meta.typehint.comment.python",
+ "begin": "(?x)\n (?:\n \\# \\s*
(type:)\n \\s*+ (?# we want `\\s*+` which is possessive quantifier since\n
we do not actually want to backtrack when matching\n
whitespace here)\n (?! $ | \\#)\n )\n",
+ "end": "(?:$|(?=\\#))",
+ "beginCaptures": {
+ "0": {
+ "name":
"meta.typehint.comment.python"
+ },
+ "1": {
+ "name":
"comment.typehint.directive.notation.python"
+ }
+ },
+ "patterns": [
+ {
+ "name":
"comment.typehint.ignore.notation.python",
+ "match": "(?x)\n \\G
ignore\n (?= \\s* (?: $ | \\#))\n"
+ },
+ {
+ "name":
"comment.typehint.type.notation.python",
+ "match": "(?x)\n
(?<!\\.)\\b(\n bool | bytes | float | int | object | str\n | List | Dict
| Iterable | Sequence | Set\n | FrozenSet | Callable | Union | Tuple\n |
Any | None\n )\\b\n"
+ },
+ {
+ "name":
"comment.typehint.punctuation.notation.python",
+ "match":
"([\\[\\]\\(\\),\\.\\=\\*]|(->))"
+ },
+ {
+ "name":
"comment.typehint.variable.notation.python",
+ "match":
"([[:alpha:]_]\\w*)"
+ }
+ ]
+ },
+ {
+ "include": "#comments-base"
+ }
+ ]
+ },
+ "docstring-statement": {
+ "begin":
"^(?=\\s*[rR]?(\\'\\'\\'|\\\"\\\"\\\"|\\'|\\\"))",
+ "comment": "the string either terminates correctly or
by the beginning of a new line (this is for single line docstrings that aren't
terminated) AND it's not followed by another docstring",
+ "end":
"((?<=\\1)|^)(?!\\s*[rR]?(\\'\\'\\'|\\\"\\\"\\\"|\\'|\\\"))",
+ "patterns": [
+ {
+ "include": "#docstring"
+ }
+ ]
+ },
+ "docstring": {
+ "patterns": [
+ {
+ "name":
"string.quoted.docstring.multi.python",
+ "begin": "(\\'\\'\\'|\\\"\\\"\\\")",
+ "end": "(\\1)",
+ "beginCaptures": {
+ "1": {
+ "name":
"punctuation.definition.string.begin.python"
+ }
+ },
+ "endCaptures": {
+ "1": {
+ "name":
"punctuation.definition.string.end.python"
+ }
+ },
+ "patterns": [
+ {
+ "include":
"#docstring-prompt"
+ },
+ {
+ "include": "#codetags"
+ },
+ {
+ "include":
"#docstring-guts-unicode"
+ }
+ ]
+ },
+ {
+ "name":
"string.quoted.docstring.raw.multi.python",
+ "begin":
"([rR])(\\'\\'\\'|\\\"\\\"\\\")",
+ "end": "(\\2)",
+ "beginCaptures": {
+ "1": {
+ "name":
"storage.type.string.python"
+ },
+ "2": {
+ "name":
"punctuation.definition.string.begin.python"
+ }
+ },
+ "endCaptures": {
+ "1": {
+ "name":
"punctuation.definition.string.end.python"
+ }
+ },
+ "patterns": [
+ {
+ "include":
"#string-consume-escape"
+ },
+ {
+ "include":
"#docstring-prompt"
+ },
+ {
+ "include": "#codetags"
+ }
+ ]
+ },
+ {
+ "name":
"string.quoted.docstring.single.python",
+ "begin": "(\\'|\\\")",
+ "end": "(\\1)|(\\n)",
+ "beginCaptures": {
+ "1": {
+ "name":
"punctuation.definition.string.begin.python"
+ }
+ },
+ "endCaptures": {
+ "1": {
+ "name":
"punctuation.definition.string.end.python"
+ },
+ "2": {
+ "name":
"invalid.illegal.newline.python"
+ }
+ },
+ "patterns": [
+ {
+ "include": "#codetags"
+ },
+ {
+ "include":
"#docstring-guts-unicode"
+ }
+ ]
+ },
+ {
+ "name":
"string.quoted.docstring.raw.single.python",
+ "begin": "([rR])(\\'|\\\")",
+ "end": "(\\2)|(\\n)",
+ "beginCaptures": {
+ "1": {
+ "name":
"storage.type.string.python"
+ },
+ "2": {
+ "name":
"punctuation.definition.string.begin.python"
+ }
+ },
+ "endCaptures": {
+ "1": {
+ "name":
"punctuation.definition.string.end.python"
+ },
+ "2": {
+ "name":
"invalid.illegal.newline.python"
+ }
+ },
+ "patterns": [
+ {
+ "include":
"#string-consume-escape"
+ },
+ {
+ "include": "#codetags"
+ }
+ ]
+ }
+ ]
+ },
+ "docstring-guts-unicode": {
+ "patterns": [
+ {
+ "include": "#escape-sequence-unicode"
+ },
+ {
+ "include": "#escape-sequence"
+ },
+ {
+ "include": "#string-line-continuation"
+ }
+ ]
+ },
+ "docstring-prompt": {
+ "match": "(?x)\n (?:\n (?:^|\\G) \\s* (?# '\\G' is
necessary for ST)\n ((?:>>>|\\.\\.\\.) \\s) (?=\\s*\\S)\n )\n",
+ "captures": {
+ "1": {
+ "name": "keyword.control.flow.python"
+ }
+ }
+ },
+ "statement-keyword": {
+ "patterns": [
+ {
+ "name": "storage.type.function.python",
+ "match": "\\b((async\\s+)?\\s*def)\\b"
+ },
+ {
+ "name": "keyword.control.flow.python",
+ "comment": "if `as` is eventually
followed by `:` or line continuation\nit's probably control flow like:\n
with foo as bar, \\\n Foo as Bar:\n try:\n do_stuff()\n
except Exception as e:\n pass\n",
+ "match": "\\b(?<!\\.)as\\b(?=.*[:\\\\])"
+ },
+ {
+ "name": "keyword.control.import.python",
+ "comment": "other legal use of `as` is
in an import",
+ "match": "\\b(?<!\\.)as\\b"
+ },
+ {
+ "name": "keyword.control.flow.python",
+ "match": "(?x)\n \\b(?<!\\.)(\n
async | continue | del | assert | break | finally | for\n | from | elif |
else | if | except | pass | raise\n | return | try | while | with\n )\\b\n"
+ },
+ {
+ "name":
"storage.modifier.declaration.python",
+ "match": "(?x)\n \\b(?<!\\.)(\n
global | nonlocal\n )\\b\n"
+ },
+ {
+ "name": "storage.type.class.python",
+ "match": "\\b(?<!\\.)(class)\\b"
+ },
+ {
+ "match": "(?x)\n ^\\s*(\n case |
match\n )(?=\\s*([-+\\w\\d(\\[{'\":#]|$))\\b\n",
+ "captures": {
+ "1": {
+ "name":
"keyword.control.flow.python"
+ }
+ }
+ }
+ ]
+ },
+ "expression-bare": {
+ "comment": "valid Python expressions w/o comments and
line continuation",
+ "patterns": [
+ {
+ "include": "#backticks"
+ },
+ {
+ "include": "#illegal-anno"
+ },
+ {
+ "include": "#literal"
+ },
+ {
+ "include": "#regexp"
+ },
+ {
+ "include": "#string"
+ },
+ {
+ "include": "#lambda"
+ },
+ {
+ "include": "#generator"
+ },
+ {
+ "include": "#illegal-operator"
+ },
+ {
+ "include": "#operator"
+ },
+ {
+ "include": "#curly-braces"
+ },
+ {
+ "include": "#item-access"
+ },
+ {
+ "include": "#list"
+ },
+ {
+ "include": "#odd-function-call"
+ },
+ {
+ "include": "#round-braces"
+ },
+ {
+ "include": "#function-call"
+ },
+ {
+ "include": "#builtin-functions"
+ },
+ {
+ "include": "#builtin-types"
+ },
+ {
+ "include": "#builtin-exceptions"
+ },
+ {
+ "include": "#magic-names"
+ },
+ {
+ "include": "#special-names"
+ },
+ {
+ "include": "#illegal-names"
+ },
+ {
+ "include": "#special-variables"
+ },
+ {
+ "include": "#ellipsis"
+ },
+ {
+ "include": "#punctuation"
+ },
+ {
+ "include": "#line-continuation"
+ }
+ ]
+ },
+ "expression-base": {
+ "comment": "valid Python expressions with comments and
line continuation",
+ "patterns": [
+ {
+ "include": "#comments"
+ },
+ {
+ "include": "#expression-bare"
+ },
+ {
+ "include": "#line-continuation"
+ }
+ ]
+ },
+ "expression": {
+ "comment": "All valid Python expressions",
+ "patterns": [
+ {
+ "include": "#expression-base"
+ },
+ {
+ "include": "#member-access"
+ },
+ {
+ "comment": "Tokenize identifiers to
help linters",
+ "match": "(?x) \\b ([[:alpha:]_]\\w*)
\\b"
+ }
+ ]
+ },
+ "member-access": {
+ "name": "meta.member.access.python",
+ "begin": "(\\.)\\s*(?!\\.)",
+ "end": "(?x)\n # stop when you've just read
non-whitespace followed by non-word\n # i.e. when finished reading an
identifier or function call\n (?<=\\S)(?=\\W) |\n # stop when seeing the
start of something that's not a word,\n # i.e. when seeing a non-identifier\n
(^|(?<=\\s))(?=[^\\\\\\w\\s]) |\n $\n",
+ "beginCaptures": {
+ "1": {
+ "name":
"punctuation.separator.period.python"
+ }
+ },
+ "patterns": [
+ {
+ "include": "#function-call"
+ },
+ {
+ "include": "#member-access-base"
+ },
+ {
+ "include": "#member-access-attribute"
+ }
+ ]
+ },
+ "member-access-base": {
+ "patterns": [
+ {
+ "include": "#magic-names"
+ },
+ {
+ "include": "#illegal-names"
+ },
+ {
+ "include": "#illegal-object-name"
+ },
+ {
+ "include": "#special-names"
+ },
+ {
+ "include": "#line-continuation"
+ },
+ {
+ "include": "#item-access"
+ }
+ ]
+ },
+ "member-access-attribute": {
+ "comment": "Highlight attribute access in otherwise
non-specialized cases.",
+ "name": "meta.attribute.python",
+ "match": "(?x)\n \\b ([[:alpha:]_]\\w*) \\b\n"
+ },
+ "special-names": {
+ "name": "constant.other.caps.python",
+ "match": "(?x)\n \\b\n # we want to see \"enough\",
meaning 2 or more upper-case\n # letters in the beginning of the constant\n
#\n # for more details refer to:\n #
https://github.com/MagicStack/MagicPython/issues/42\n (\n _*
[[:upper:]] [_\\d]* [[:upper:]]\n )\n [[:upper:]\\d]* (_\\w*)?\n \\b\n"
+ },
+ "curly-braces": {
+ "begin": "\\{",
+ "end": "\\}",
+ "beginCaptures": {
+ "0": {
+ "name":
"punctuation.definition.dict.begin.python"
+ }
+ },
+ "endCaptures": {
+ "0": {
+ "name":
"punctuation.definition.dict.end.python"
+ }
+ },
+ "patterns": [
+ {
+ "name":
"punctuation.separator.dict.python",
+ "match": ":"
+ },
+ {
+ "include": "#expression"
+ }
+ ]
+ },
+ "list": {
+ "begin": "\\[",
+ "end": "\\]",
+ "beginCaptures": {
+ "0": {
+ "name":
"punctuation.definition.list.begin.python"
+ }
+ },
+ "endCaptures": {
+ "0": {
+ "name":
"punctuation.definition.list.end.python"
+ }
+ },
+ "patterns": [
+ {
+ "include": "#expression"
+ }
+ ]
+ },
+ "odd-function-call": {
+ "comment": "A bit obscured function call where there
may have been an\narbitrary number of other operations to get the
function.\nE.g. \"arr[idx](args)\"\n",
+ "begin": "(?x)\n (?<= \\] | \\) ) \\s*\n (?=\\()\n",
+ "end": "(\\))",
+ "endCaptures": {
+ "1": {
+ "name":
"punctuation.definition.arguments.end.python"
+ }
+ },
+ "patterns": [
+ {
+ "include": "#function-arguments"
+ }
+ ]
+ },
+ "round-braces": {
+ "begin": "\\(",
+ "end": "\\)",
+ "beginCaptures": {
+ "0": {
+ "name":
"punctuation.parenthesis.begin.python"
+ }
+ },
+ "endCaptures": {
+ "0": {
+ "name":
"punctuation.parenthesis.end.python"
+ }
+ },
+ "patterns": [
+ {
+ "include": "#expression"
+ }
+ ]
+ },
+ "line-continuation": {
+ "patterns": [
+ {
+ "match": "(\\\\)\\s*(\\S.*$\\n?)",
+ "captures": {
+ "1": {
+ "name":
"punctuation.separator.continuation.line.python"
+ },
+ "2": {
+ "name":
"invalid.illegal.line.continuation.python"
+ }
+ }
+ },
+ {
+ "begin": "(\\\\)\\s*$\\n?",
+ "end": "(?x)\n (?=^\\s*$)\n |\n (?!
(\\s* [rR]? (\\'\\'\\'|\\\"\\\"\\\"|\\'|\\\"))\n |\n (\\G $) (?#
'\\G' is necessary for ST)\n )\n",
+ "beginCaptures": {
+ "1": {
+ "name":
"punctuation.separator.continuation.line.python"
+ }
+ },
+ "patterns": [
+ {
+ "include": "#regexp"
+ },
+ {
+ "include": "#string"
+ }
+ ]
+ }
+ ]
+ },
+ "assignment-operator": {
+ "name": "keyword.operator.assignment.python",
+ "match": "(?x)\n <<= | >>= | //= | \\*\\*=\n |
\\+= | -= | /= | @=\n | \\*= | %= | ~= | \\^= | &= | \\|=\n | =(?!=)\n"
+ },
+ "operator": {
+ "match": "(?x)\n \\b(?<!\\.)\n (?:\n
(and | or | not | in | is) (?# 1)\n |\n
(for | if | else | await | (?:yield(?:\\s+from)?)) (?# 2)\n )\n
(?!\\s*:)\\b\n\n | (<< | >> | & | \\| | \\^ | ~)
(?# 3)\n\n | (\\*\\* | \\* | \\+ | - | % | // | / | @) (?#
4)\n\n | (!= | == | >= | <= | < | >) (?# 5)\n\n
| (:=) [...]
+ "captures": {
+ "1": {
+ "name":
"keyword.operator.logical.python"
+ },
+ "2": {
+ "name": "keyword.control.flow.python"
+ },
+ "3": {
+ "name":
"keyword.operator.bitwise.python"
+ },
+ "4": {
+ "name":
"keyword.operator.arithmetic.python"
+ },
+ "5": {
+ "name":
"keyword.operator.comparison.python"
+ },
+ "6": {
+ "name":
"keyword.operator.assignment.python"
+ }
+ }
+ },
+ "punctuation": {
+ "patterns": [
+ {
+ "name":
"punctuation.separator.colon.python",
+ "match": ":"
+ },
+ {
+ "name":
"punctuation.separator.element.python",
+ "match": ","
+ }
+ ]
+ },
+ "literal": {
+ "patterns": [
+ {
+ "name": "constant.language.python",
+ "match":
"\\b(True|False|None|NotImplemented|Ellipsis)\\b"
+ },
+ {
+ "include": "#number"
+ }
+ ]
+ },
+ "number": {
+ "name": "constant.numeric.python",
+ "patterns": [
+ {
+ "include": "#number-float"
+ },
+ {
+ "include": "#number-dec"
+ },
+ {
+ "include": "#number-hex"
+ },
+ {
+ "include": "#number-oct"
+ },
+ {
+ "include": "#number-bin"
+ },
+ {
+ "include": "#number-long"
+ },
+ {
+ "name": "invalid.illegal.name.python",
+ "match": "\\b[0-9]+\\w+"
+ }
+ ]
+ },
+ "number-float": {
+ "name": "constant.numeric.float.python",
+ "match": "(?x)\n (?<! \\w)(?:\n (?:\n
\\.[0-9](?: _?[0-9] )*\n |\n [0-9](?: _?[0-9] )* \\. [0-9](?: _?[0-9]
)*\n |\n [0-9](?: _?[0-9] )* \\.\n ) (?: [eE][+-]?[0-9](?: _?[0-9]
)* )?\n |\n [0-9](?: _?[0-9] )* (?: [eE][+-]?[0-9](?: _?[0-9] )* )\n
)([jJ])?\\b\n",
+ "captures": {
+ "1": {
+ "name":
"storage.type.imaginary.number.python"
+ }
+ }
+ },
+ "number-dec": {
+ "name": "constant.numeric.dec.python",
+ "match": "(?x)\n (?<![\\w\\.])(?:\n [1-9](?:
_?[0-9] )*\n |\n 0+\n |\n [0-9](?: _?[0-9] )* ([jJ])\n
|\n 0 ([0-9]+)(?![eE\\.])\n )\\b\n",
+ "captures": {
+ "1": {
+ "name":
"storage.type.imaginary.number.python"
+ },
+ "2": {
+ "name": "invalid.illegal.dec.python"
+ }
+ }
+ },
+ "number-hex": {
+ "name": "constant.numeric.hex.python",
+ "match": "(?x)\n (?<![\\w\\.])\n (0[xX])
(_?[0-9a-fA-F])+\n \\b\n",
+ "captures": {
+ "1": {
+ "name": "storage.type.number.python"
+ }
+ }
+ },
+ "number-oct": {
+ "name": "constant.numeric.oct.python",
+ "match": "(?x)\n (?<![\\w\\.])\n (0[oO])
(_?[0-7])+\n \\b\n",
+ "captures": {
+ "1": {
+ "name": "storage.type.number.python"
+ }
+ }
+ },
+ "number-bin": {
+ "name": "constant.numeric.bin.python",
+ "match": "(?x)\n (?<![\\w\\.])\n (0[bB])
(_?[01])+\n \\b\n",
+ "captures": {
+ "1": {
+ "name": "storage.type.number.python"
+ }
+ }
+ },
+ "number-long": {
+ "name": "constant.numeric.bin.python",
+ "comment": "this is to support python2 syntax for long
ints",
+ "match": "(?x)\n (?<![\\w\\.])\n ([1-9][0-9]* | 0)
([lL])\n \\b\n",
+ "captures": {
+ "2": {
+ "name": "storage.type.number.python"
+ }
+ }
+ },
+ "regexp": {
+ "patterns": [
+ {
+ "include": "#regexp-single-three-line"
+ },
+ {
+ "include": "#regexp-double-three-line"
+ },
+ {
+ "include": "#regexp-single-one-line"
+ },
+ {
+ "include": "#regexp-double-one-line"
+ }
+ ]
+ },
+ "string": {
+ "patterns": [
+ {
+ "include": "#string-quoted-multi-line"
+ },
+ {
+ "include": "#string-quoted-single-line"
+ },
+ {
+ "include":
"#string-bin-quoted-multi-line"
+ },
+ {
+ "include":
"#string-bin-quoted-single-line"
+ },
+ {
+ "include":
"#string-raw-quoted-multi-line"
+ },
+ {
+ "include":
"#string-raw-quoted-single-line"
+ },
+ {
+ "include":
"#string-raw-bin-quoted-multi-line"
+ },
+ {
+ "include":
"#string-raw-bin-quoted-single-line"
+ },
+ {
+ "include":
"#fstring-fnorm-quoted-multi-line"
+ },
+ {
+ "include":
"#fstring-fnorm-quoted-single-line"
+ },
+ {
+ "include":
"#fstring-normf-quoted-multi-line"
+ },
+ {
+ "include":
"#fstring-normf-quoted-single-line"
+ },
+ {
+ "include":
"#fstring-raw-quoted-multi-line"
+ },
+ {
+ "include":
"#fstring-raw-quoted-single-line"
+ }
+ ]
+ },
+ "string-unicode-guts": {
+ "patterns": [
+ {
+ "include": "#escape-sequence-unicode"
+ },
+ {
+ "include": "#string-entity"
+ },
+ {
+ "include": "#string-brace-formatting"
+ }
+ ]
+ },
+ "string-consume-escape": {
+ "match": "\\\\['\"\\n\\\\]"
+ },
+ "string-raw-guts": {
+ "patterns": [
+ {
+ "include": "#string-consume-escape"
+ },
+ {
+ "include": "#string-formatting"
+ },
+ {
+ "include": "#string-brace-formatting"
+ }
+ ]
+ },
+ "string-raw-bin-guts": {
+ "patterns": [
+ {
+ "include": "#string-consume-escape"
+ },
+ {
+ "include": "#string-formatting"
+ }
+ ]
+ },
+ "string-entity": {
+ "patterns": [
+ {
+ "include": "#escape-sequence"
+ },
+ {
+ "include": "#string-line-continuation"
+ },
+ {
+ "include": "#string-formatting"
+ }
+ ]
+ },
+ "fstring-guts": {
+ "patterns": [
+ {
+ "include": "#escape-sequence-unicode"
+ },
+ {
+ "include": "#escape-sequence"
+ },
+ {
+ "include": "#string-line-continuation"
+ },
+ {
+ "include": "#fstring-formatting"
+ }
+ ]
+ },
+ "fstring-raw-guts": {
+ "patterns": [
+ {
+ "include": "#string-consume-escape"
+ },
+ {
+ "include": "#fstring-formatting"
+ }
+ ]
+ },
+ "fstring-illegal-single-brace": {
+ "comment": "it is illegal to have a multiline brace
inside a single-line string",
+ "begin": "(\\{)(?=[^\\n}]*$\\n?)",
+ "end": "(\\})|(?=\\n)",
+ "beginCaptures": {
+ "1": {
+ "name":
"constant.character.format.placeholder.other.python"
+ }
+ },
+ "endCaptures": {
+ "1": {
+ "name":
"constant.character.format.placeholder.other.python"
+ }
+ },
+ "patterns": [
+ {
+ "include": "#fstring-terminator-single"
+ },
+ {
+ "include": "#f-expression"
+ }
+ ]
+ },
+ "fstring-illegal-multi-brace": {
+ "patterns": [
+ {
+ "include": "#impossible"
+ }
+ ]
+ },
+ "f-expression": {
+ "comment": "All valid Python expressions, except
comments and line continuation",
+ "patterns": [
+ {
+ "include": "#expression-bare"
+ },
+ {
+ "include": "#member-access"
+ },
+ {
+ "comment": "Tokenize identifiers to
help linters",
+ "match": "(?x) \\b ([[:alpha:]_]\\w*)
\\b"
+ }
+ ]
+ },
+ "escape-sequence-unicode": {
+ "patterns": [
+ {
+ "name":
"constant.character.escape.python",
+ "match": "(?x)\n \\\\ (\n
u[0-9A-Fa-f]{4}\n | U[0-9A-Fa-f]{8}\n | N\\{[\\w\\s]+?\\}\n
)\n"
+ }
+ ]
+ },
+ "escape-sequence": {
+ "name": "constant.character.escape.python",
+ "match": "(?x)\n \\\\ (\n x[0-9A-Fa-f]{2}\n
| [0-7]{1,3}\n | [\\\\\"'abfnrtv]\n )\n"
+ },
+ "string-line-continuation": {
+ "name": "constant.language.python",
+ "match": "\\\\$"
+ },
+ "string-formatting": {
+ "name": "meta.format.percent.python",
+ "match": "(?x)\n (\n % (\\([\\w\\s]*\\))?\n
[-+#0 ]*\n (\\d+|\\*)? (\\.(\\d+|\\*))?\n ([hlL])?\n
[diouxXeEfFgGcrsab%]\n )\n",
+ "captures": {
+ "1": {
+ "name":
"constant.character.format.placeholder.other.python"
+ }
+ }
+ },
+ "string-brace-formatting": {
+ "patterns": [
+ {
+ "name": "meta.format.brace.python",
+ "match": "(?x)\n (\n {{ | }}\n |
(?:\n {\n \\w* (\\.[[:alpha:]_]\\w* | \\[[^\\]'\"]+\\])*\n
(![rsa])?\n ( : \\w? [<>=^]? [-+ ]? \\#?\n \\d* ,? (\\.\\d+)?
[bcdeEfFgGnosxX%]? )?\n })\n )\n",
+ "captures": {
+ "1": {
+ "name":
"constant.character.format.placeholder.other.python"
+ },
+ "3": {
+ "name":
"storage.type.format.python"
+ },
+ "4": {
+ "name":
"storage.type.format.python"
+ }
+ }
+ },
+ {
+ "name": "meta.format.brace.python",
+ "match": "(?x)\n (\n {\n \\w*
(\\.[[:alpha:]_]\\w* | \\[[^\\]'\"]+\\])*\n (![rsa])?\n (:)\n
[^'\"{}\\n]* (?:\n \\{ [^'\"}\\n]*? \\} [^'\"{}\\n]*\n )*\n
}\n )\n",
+ "captures": {
+ "1": {
+ "name":
"constant.character.format.placeholder.other.python"
+ },
+ "3": {
+ "name":
"storage.type.format.python"
+ },
+ "4": {
+ "name":
"storage.type.format.python"
+ }
+ }
+ }
+ ]
+ },
+ "fstring-formatting": {
+ "patterns": [
+ {
+ "include": "#fstring-formatting-braces"
+ },
+ {
+ "include":
"#fstring-formatting-singe-brace"
+ }
+ ]
+ },
+ "fstring-formatting-singe-brace": {
+ "name": "invalid.illegal.brace.python",
+ "match": "(}(?!}))"
+ },
+ "import": {
+ "comment": "Import statements used to correctly mark
`from`, `import`, and `as`\n",
+ "patterns": [
+ {
+ "begin":
"\\b(?<!\\.)(from)\\b(?=.+import)",
+ "end": "$|(?=import)",
+ "beginCaptures": {
+ "1": {
+ "name":
"keyword.control.import.python"
+ }
+ },
+ "patterns": [
+ {
+ "name":
"punctuation.separator.period.python",
+ "match": "\\.+"
+ },
+ {
+ "include": "#expression"
+ }
+ ]
+ },
+ {
+ "begin": "\\b(?<!\\.)(import)\\b",
+ "end": "$",
+ "beginCaptures": {
+ "1": {
+ "name":
"keyword.control.import.python"
+ }
+ },
+ "patterns": [
+ {
+ "name":
"keyword.control.import.python",
+ "match":
"\\b(?<!\\.)as\\b"
+ },
+ {
+ "include": "#expression"
+ }
+ ]
+ }
+ ]
+ },
+ "class-declaration": {
+ "patterns": [
+ {
+ "name": "meta.class.python",
+ "begin": "(?x)\n \\s*(class)\\s+\n
(?=\n [[:alpha:]_]\\w* \\s* (:|\\()\n )\n",
+ "end": "(:)",
+ "beginCaptures": {
+ "1": {
+ "name":
"storage.type.class.python"
+ }
+ },
+ "endCaptures": {
+ "1": {
+ "name":
"punctuation.section.class.begin.python"
+ }
+ },
+ "patterns": [
+ {
+ "include": "#class-name"
+ },
+ {
+ "include":
"#class-inheritance"
+ }
+ ]
+ }
+ ]
+ },
+ "class-name": {
+ "patterns": [
+ {
+ "include": "#illegal-object-name"
+ },
+ {
+ "include": "#builtin-possible-callables"
+ },
+ {
+ "name": "entity.name.type.class.python",
+ "match": "(?x)\n \\b
([[:alpha:]_]\\w*) \\b\n"
+ }
+ ]
+ },
+ "class-inheritance": {
+ "name": "meta.class.inheritance.python",
+ "begin": "(\\()",
+ "end": "(\\))",
+ "beginCaptures": {
+ "1": {
+ "name":
"punctuation.definition.inheritance.begin.python"
+ }
+ },
+ "endCaptures": {
+ "1": {
+ "name":
"punctuation.definition.inheritance.end.python"
+ }
+ },
+ "patterns": [
+ {
+ "name":
"keyword.operator.unpacking.arguments.python",
+ "match": "(\\*\\*|\\*)"
+ },
+ {
+ "name":
"punctuation.separator.inheritance.python",
+ "match": ","
+ },
+ {
+ "name":
"keyword.operator.assignment.python",
+ "match": "=(?!=)"
+ },
+ {
+ "name": "support.type.metaclass.python",
+ "match": "\\bmetaclass\\b"
+ },
+ {
+ "include": "#illegal-names"
+ },
+ {
+ "include": "#class-kwarg"
+ },
+ {
+ "include": "#call-wrapper-inheritance"
+ },
+ {
+ "include": "#expression-base"
+ },
+ {
+ "include": "#member-access-class"
+ },
+ {
+ "include": "#inheritance-identifier"
+ }
+ ]
+ },
+ "class-kwarg": {
+ "match": "(?x)\n \\b ([[:alpha:]_]\\w*)
\\s*(=)(?!=)\n",
+ "captures": {
+ "1": {
+ "name":
"entity.other.inherited-class.python variable.parameter.class.python"
+ },
+ "2": {
+ "name":
"keyword.operator.assignment.python"
+ }
+ }
+ },
+ "inheritance-identifier": {
+ "match": "(?x)\n \\b ([[:alpha:]_]\\w*) \\b\n",
+ "captures": {
+ "1": {
+ "name":
"entity.other.inherited-class.python"
+ }
+ }
+ },
+ "member-access-class": {
+ "name": "meta.member.access.python",
+ "begin": "(\\.)\\s*(?!\\.)",
+ "end": "(?<=\\S)(?=\\W)|$",
+ "beginCaptures": {
+ "1": {
+ "name":
"punctuation.separator.period.python"
+ }
+ },
+ "patterns": [
+ {
+ "include": "#call-wrapper-inheritance"
+ },
+ {
+ "include": "#member-access-base"
+ },
+ {
+ "include": "#inheritance-identifier"
+ }
+ ]
+ },
+ "lambda": {
+ "patterns": [
+ {
+ "match":
"((?<=\\.)lambda|lambda(?=\\s*[\\.=]))",
+ "captures": {
+ "1": {
+ "name":
"keyword.control.flow.python"
+ }
+ }
+ },
+ {
+ "match": "\\b(lambda)\\s*?(?=[,\\n]|$)",
+ "captures": {
+ "1": {
+ "name":
"storage.type.function.lambda.python"
+ }
+ }
+ },
+ {
+ "name": "meta.lambda-function.python",
+ "begin": "(?x)\n \\b (lambda) \\b\n",
+ "end": "(:)|(\\n)",
+ "beginCaptures": {
+ "1": {
+ "name":
"storage.type.function.lambda.python"
+ }
+ },
+ "endCaptures": {
+ "1": {
+ "name":
"punctuation.section.function.lambda.begin.python"
+ }
+ },
+ "contentName":
"meta.function.lambda.parameters.python",
+ "patterns": [
+ {
+ "name":
"keyword.operator.positional.parameter.python",
+ "match": "/"
+ },
+ {
+ "name":
"keyword.operator.unpacking.parameter.python",
+ "match": "(\\*\\*|\\*)"
+ },
+ {
+ "include":
"#lambda-nested-incomplete"
+ },
+ {
+ "include":
"#illegal-names"
+ },
+ {
+ "match":
"([[:alpha:]_]\\w*)\\s*(?:(,)|(?=:|$))",
+ "captures": {
+ "1": {
+ "name":
"variable.parameter.function.language.python"
+ },
+ "2": {
+ "name":
"punctuation.separator.parameters.python"
+ }
+ }
+ },
+ {
+ "include": "#comments"
+ },
+ {
+ "include": "#backticks"
+ },
+ {
+ "include":
"#illegal-anno"
+ },
+ {
+ "include":
"#lambda-parameter-with-default"
+ },
+ {
+ "include":
"#line-continuation"
+ },
+ {
+ "include":
"#illegal-operator"
+ }
+ ]
+ }
+ ]
+ },
+ "lambda-incomplete": {
+ "name": "storage.type.function.lambda.python",
+ "match": "\\blambda(?=\\s*[,)])"
+ },
+ "lambda-nested-incomplete": {
+ "name": "storage.type.function.lambda.python",
+ "match": "\\blambda(?=\\s*[:,)])"
+ },
+ "lambda-parameter-with-default": {
+ "begin": "(?x)\n \\b\n ([[:alpha:]_]\\w*) \\s* (=)\n",
+ "end": "(,)|(?=:|$)",
+ "beginCaptures": {
+ "1": {
+ "name":
"variable.parameter.function.language.python"
+ },
+ "2": {
+ "name": "keyword.operator.python"
+ }
+ },
+ "endCaptures": {
+ "1": {
+ "name":
"punctuation.separator.parameters.python"
+ }
+ },
+ "patterns": [
+ {
+ "include": "#expression"
+ }
+ ]
+ },
+ "generator": {
+ "comment": "Match \"for ... in\" construct used in
generators and for loops to\ncorrectly identify the \"in\" as a control flow
keyword.\n",
+ "begin": "\\bfor\\b",
+ "beginCaptures": {
+ "0": {
+ "name": "keyword.control.flow.python"
+ }
+ },
+ "end": "\\bin\\b",
+ "endCaptures": {
+ "0": {
+ "name": "keyword.control.flow.python"
+ }
+ },
+ "patterns": [
+ {
+ "include": "#expression"
+ }
+ ]
+ },
+ "function-declaration": {
+ "name": "meta.function.python",
+ "begin": "(?x)\n \\s*\n (?:\\b(async) \\s+)?
\\b(def)\\s+\n (?=\n [[:alpha:]_][[:word:]]* \\s* \\(\n )\n",
+ "end": "(:|(?=[#'\"\\n]))",
+ "beginCaptures": {
+ "1": {
+ "name":
"storage.type.function.async.python"
+ },
+ "2": {
+ "name": "storage.type.function.python"
+ }
+ },
+ "endCaptures": {
+ "1": {
+ "name":
"punctuation.section.function.begin.python"
+ }
+ },
+ "patterns": [
+ {
+ "include": "#function-def-name"
+ },
+ {
+ "include": "#parameters"
+ },
+ {
+ "include": "#line-continuation"
+ },
+ {
+ "include": "#return-annotation"
+ }
+ ]
+ },
+ "function-def-name": {
+ "patterns": [
+ {
+ "include": "#illegal-object-name"
+ },
+ {
+ "include": "#builtin-possible-callables"
+ },
+ {
+ "name": "entity.name.function.python",
+ "match": "(?x)\n \\b
([[:alpha:]_]\\w*) \\b\n"
+ }
+ ]
+ },
+ "parameters": {
+ "name": "meta.function.parameters.python",
+ "begin": "(\\()",
+ "end": "(\\))",
+ "beginCaptures": {
+ "1": {
+ "name":
"punctuation.definition.parameters.begin.python"
+ }
+ },
+ "endCaptures": {
+ "1": {
+ "name":
"punctuation.definition.parameters.end.python"
+ }
+ },
+ "patterns": [
+ {
+ "name":
"keyword.operator.positional.parameter.python",
+ "match": "/"
+ },
+ {
+ "name":
"keyword.operator.unpacking.parameter.python",
+ "match": "(\\*\\*|\\*)"
+ },
+ {
+ "include": "#lambda-incomplete"
+ },
+ {
+ "include": "#illegal-names"
+ },
+ {
+ "include": "#illegal-object-name"
+ },
+ {
+ "include": "#parameter-special"
+ },
+ {
+ "match": "(?x)\n ([[:alpha:]_]\\w*)\n
\\s* (?: (,) | (?=[)#\\n=]))\n",
+ "captures": {
+ "1": {
+ "name":
"variable.parameter.function.language.python"
+ },
+ "2": {
+ "name":
"punctuation.separator.parameters.python"
+ }
+ }
+ },
+ {
+ "include": "#comments"
+ },
+ {
+ "include": "#loose-default"
+ },
+ {
+ "include": "#annotated-parameter"
+ }
+ ]
+ },
+ "parameter-special": {
+ "match": "(?x)\n \\b ((self)|(cls)) \\b
\\s*(?:(,)|(?=\\)))\n",
+ "captures": {
+ "1": {
+ "name":
"variable.parameter.function.language.python"
+ },
+ "2": {
+ "name":
"variable.parameter.function.language.special.self.python"
+ },
+ "3": {
+ "name":
"variable.parameter.function.language.special.cls.python"
+ },
+ "4": {
+ "name":
"punctuation.separator.parameters.python"
+ }
+ }
+ },
+ "loose-default": {
+ "begin": "(=)",
+ "end": "(,)|(?=\\))",
+ "beginCaptures": {
+ "1": {
+ "name": "keyword.operator.python"
+ }
+ },
+ "endCaptures": {
+ "1": {
+ "name":
"punctuation.separator.parameters.python"
+ }
+ },
+ "patterns": [
+ {
+ "include": "#expression"
+ }
+ ]
+ },
+ "annotated-parameter": {
+ "begin": "(?x)\n \\b\n ([[:alpha:]_]\\w*) \\s* (:)\n",
+ "end": "(,)|(?=\\))",
+ "beginCaptures": {
+ "1": {
+ "name":
"variable.parameter.function.language.python"
+ },
+ "2": {
+ "name":
"punctuation.separator.annotation.python"
+ }
+ },
+ "endCaptures": {
+ "1": {
+ "name":
"punctuation.separator.parameters.python"
+ }
+ },
+ "patterns": [
+ {
+ "include": "#expression"
+ },
+ {
+ "name":
"keyword.operator.assignment.python",
+ "match": "=(?!=)"
+ }
+ ]
+ },
+ "return-annotation": {
+ "begin": "(->)",
+ "end": "(?=:)",
+ "beginCaptures": {
+ "1": {
+ "name":
"punctuation.separator.annotation.result.python"
+ }
+ },
+ "patterns": [
+ {
+ "include": "#expression"
+ }
+ ]
+ },
+ "item-access": {
+ "patterns": [
+ {
+ "name": "meta.item-access.python",
+ "begin": "(?x)\n \\b(?=\n
[[:alpha:]_]\\w* \\s* \\[\n )\n",
+ "end": "(\\])",
+ "endCaptures": {
+ "1": {
+ "name":
"punctuation.definition.arguments.end.python"
+ }
+ },
+ "patterns": [
+ {
+ "include": "#item-name"
+ },
+ {
+ "include": "#item-index"
+ },
+ {
+ "include": "#expression"
+ }
+ ]
+ }
+ ]
+ },
+ "item-name": {
+ "patterns": [
+ {
+ "include": "#special-variables"
+ },
+ {
+ "include": "#builtin-functions"
+ },
+ {
+ "include": "#special-names"
+ },
+ {
+ "name": "meta.indexed-name.python",
+ "match": "(?x)\n \\b
([[:alpha:]_]\\w*) \\b\n"
+ }
+ ]
+ },
+ "item-index": {
+ "begin": "(\\[)",
+ "end": "(?=\\])",
+ "beginCaptures": {
+ "1": {
+ "name":
"punctuation.definition.arguments.begin.python"
+ }
+ },
+ "contentName": "meta.item-access.arguments.python",
+ "patterns": [
+ {
+ "name":
"punctuation.separator.slice.python",
+ "match": ":"
+ },
+ {
+ "include": "#expression"
+ }
+ ]
+ },
+ "decorator": {
+ "name": "meta.function.decorator.python",
+ "begin": "(?x)\n ^\\s*\n ((@)) \\s*
(?=[[:alpha:]_]\\w*)\n",
+ "end": "(?x)\n ( \\) )\n # trailing whitespace and
comments are legal\n (?: (.*?) (?=\\s*(?:\\#|$)) )\n | (?=\\n|\\#)\n",
+ "beginCaptures": {
+ "1": {
+ "name":
"entity.name.function.decorator.python"
+ },
+ "2": {
+ "name":
"punctuation.definition.decorator.python"
+ }
+ },
+ "endCaptures": {
+ "1": {
+ "name":
"punctuation.definition.arguments.end.python"
+ },
+ "2": {
+ "name":
"invalid.illegal.decorator.python"
+ }
+ },
+ "patterns": [
+ {
+ "include": "#decorator-name"
+ },
+ {
+ "include": "#function-arguments"
+ }
+ ]
+ },
+ "decorator-name": {
+ "patterns": [
+ {
+ "include": "#builtin-callables"
+ },
+ {
+ "include": "#illegal-object-name"
+ },
+ {
+ "name":
"entity.name.function.decorator.python",
+ "match": "(?x)\n ([[:alpha:]_]\\w*) |
(\\.)\n",
+ "captures": {
+ "2": {
+ "name":
"punctuation.separator.period.python"
+ }
+ }
+ },
+ {
+ "include": "#line-continuation"
+ },
+ {
+ "name":
"invalid.illegal.decorator.python",
+ "match": "(?x)\n \\s*
([^([:alpha:]\\s_\\.#\\\\] .*?) (?=\\#|$)\n",
+ "captures": {
+ "1": {
+ "name":
"invalid.illegal.decorator.python"
+ }
+ }
+ }
+ ]
+ },
+ "call-wrapper-inheritance": {
+ "comment": "same as a function call, but in inheritance
context",
+ "name": "meta.function-call.python",
+ "begin": "(?x)\n \\b(?=\n ([[:alpha:]_]\\w*) \\s*
(\\()\n )\n",
+ "end": "(\\))",
+ "endCaptures": {
+ "1": {
+ "name":
"punctuation.definition.arguments.end.python"
+ }
+ },
+ "patterns": [
+ {
+ "include": "#inheritance-name"
+ },
+ {
+ "include": "#function-arguments"
+ }
+ ]
+ },
+ "inheritance-name": {
+ "patterns": [
+ {
+ "include": "#lambda-incomplete"
+ },
+ {
+ "include": "#builtin-possible-callables"
+ },
+ {
+ "include": "#inheritance-identifier"
+ }
+ ]
+ },
+ "function-call": {
+ "name": "meta.function-call.python",
+ "comment": "Regular function call of the type
\"name(args)\"",
+ "begin": "(?x)\n \\b(?=\n ([[:alpha:]_]\\w*) \\s*
(\\()\n )\n",
+ "end": "(\\))",
+ "endCaptures": {
+ "1": {
+ "name":
"punctuation.definition.arguments.end.python"
+ }
+ },
+ "patterns": [
+ {
+ "include": "#special-variables"
+ },
+ {
+ "include": "#function-name"
+ },
+ {
+ "include": "#function-arguments"
+ }
+ ]
+ },
+ "function-name": {
+ "patterns": [
+ {
+ "include": "#builtin-possible-callables"
+ },
+ {
+ "comment": "Some color schemas support
meta.function-call.generic scope",
+ "name":
"meta.function-call.generic.python",
+ "match": "(?x)\n \\b
([[:alpha:]_]\\w*) \\b\n"
+ }
+ ]
+ },
+ "function-arguments": {
+ "begin": "(\\()",
+ "end": "(?=\\))(?!\\)\\s*\\()",
+ "beginCaptures": {
+ "1": {
+ "name":
"punctuation.definition.arguments.begin.python"
+ }
+ },
+ "contentName": "meta.function-call.arguments.python",
+ "patterns": [
+ {
+ "name":
"punctuation.separator.arguments.python",
+ "match": "(,)"
+ },
+ {
+ "match": "(?x)\n (?:(?<=[,(])|^) \\s*
(\\*{1,2})\n",
+ "captures": {
+ "1": {
+ "name":
"keyword.operator.unpacking.arguments.python"
+ }
+ }
+ },
+ {
+ "include": "#lambda-incomplete"
+ },
+ {
+ "include": "#illegal-names"
+ },
+ {
+ "match":
"\\b([[:alpha:]_]\\w*)\\s*(=)(?!=)",
+ "captures": {
+ "1": {
+ "name":
"variable.parameter.function-call.python"
+ },
+ "2": {
+ "name":
"keyword.operator.assignment.python"
+ }
+ }
+ },
+ {
+ "name":
"keyword.operator.assignment.python",
+ "match": "=(?!=)"
+ },
+ {
+ "include": "#expression"
+ },
+ {
+ "match": "\\s*(\\))\\s*(\\()",
+ "captures": {
+ "1": {
+ "name":
"punctuation.definition.arguments.end.python"
+ },
+ "2": {
+ "name":
"punctuation.definition.arguments.begin.python"
+ }
+ }
+ }
+ ]
+ },
+ "builtin-callables": {
+ "patterns": [
+ {
+ "include": "#illegal-names"
+ },
+ {
+ "include": "#illegal-object-name"
+ },
+ {
+ "include": "#builtin-exceptions"
+ },
+ {
+ "include": "#builtin-functions"
+ },
+ {
+ "include": "#builtin-types"
+ }
+ ]
+ },
+ "builtin-possible-callables": {
+ "patterns": [
+ {
+ "include": "#builtin-callables"
+ },
+ {
+ "include": "#magic-names"
+ }
+ ]
+ },
+ "builtin-exceptions": {
+ "name": "support.type.exception.python",
+ "match": "(?x) (?<!\\.) \\b(\n (\n Arithmetic |
Assertion | Attribute | Buffer | BlockingIO\n | BrokenPipe | ChildProcess\n
| (Connection (Aborted | Refused | Reset)?)\n | EOF | Environment |
FileExists | FileNotFound\n | FloatingPoint | IO | Import | Indentation |
Index | Interrupted\n | IsADirectory | NotADirectory | Permission |
ProcessLookup\n | Timeout\n | Key | Lookup | Memory | Name |
NotImplemented | OS | Overflow\n | Reference | Runtime | Recursion [...]
+ },
+ "builtin-functions": {
+ "patterns": [
+ {
+ "name":
"support.function.builtin.python",
+ "match": "(?x)\n (?<!\\.) \\b(\n
__import__ | abs | aiter | all | any | anext | ascii | bin\n | breakpoint |
callable | chr | compile | copyright | credits\n | delattr | dir | divmod |
enumerate | eval | exec | exit\n | filter | format | getattr | globals |
hasattr | hash | help\n | hex | id | input | isinstance | issubclass | iter
| len\n | license | locals | map | max | memoryview | min | next\n | oct
| open | ord | pow | print | quit | range | reload | repr\ [...]
+ },
+ {
+ "name":
"variable.legacy.builtin.python",
+ "match": "(?x)\n (?<!\\.) \\b(\n
file | reduce | intern | raw_input | unicode | cmp | basestring\n | execfile
| long | xrange\n )\\b\n"
+ }
+ ]
+ },
+ "builtin-types": {
+ "name": "support.type.python",
+ "match": "(?x)\n (?<!\\.) \\b(\n bool | bytearray |
bytes | classmethod | complex | dict\n | float | frozenset | int | list |
object | property\n | set | slice | staticmethod | str | tuple | type\n\n
(?# Although 'super' is not a type, it's related to types,\n and is
special enough to be highlighted differently from\n other built-ins)\n
| super\n )\\b\n"
+ },
+ "magic-function-names": {
+ "comment": "these methods have magic interpretation by
python and are generally called\nindirectly through syntactic constructs\n",
+ "match": "(?x)\n \\b(\n __(?:\n abs | add |
aenter | aexit | aiter | and | anext\n | await | bool | call | ceil |
class_getitem\n | cmp | coerce | complex | contains | copy\n |
deepcopy | del | delattr | delete | delitem\n | delslice | dir | div |
divmod | enter | eq\n | exit | float | floor | floordiv | format | ge\n
| get | getattr | getattribute | getinitargs\n | getitem | getnewargs |
getslice | getstate | gt\n | hash | hex | iadd | [...]
+ "captures": {
+ "1": {
+ "name": "support.function.magic.python"
+ }
+ }
+ },
+ "magic-variable-names": {
+ "comment": "magic variables which a class/module may
have.",
+ "match": "(?x)\n \\b(\n __(?:\n all |
annotations | bases | builtins | class\n | closure | code | debug |
defaults | dict | doc | file | func\n | globals | kwdefaults | match_args
| members | metaclass | methods\n | module | mro | mro_entries | name |
qualname | post_init | self\n | signature | slots | subclasses | version |
weakref | wrapped\n | classcell | spec | path | package | future |
traceback\n )__\n )\\b\n",
+ "captures": {
+ "1": {
+ "name": "support.variable.magic.python"
+ }
+ }
+ },
+ "magic-names": {
+ "patterns": [
+ {
+ "include": "#magic-function-names"
+ },
+ {
+ "include": "#magic-variable-names"
+ }
+ ]
+ },
+ "illegal-names": {
+ "match": "(?x)\n \\b(?:\n (\n and | assert |
async | await | break | class | continue | def\n | del | elif | else |
except | finally | for | from | global\n | if | in | is | (?<=\\.)lambda |
lambda(?=\\s*[\\.=])\n | nonlocal | not | or | pass | raise | return | try
| while | with\n | yield\n ) | (\n as | import\n )\n )\\b\n",
+ "captures": {
+ "1": {
+ "name": "keyword.control.flow.python"
+ },
+ "2": {
+ "name": "keyword.control.import.python"
+ }
+ }
+ },
+ "special-variables": {
+ "match": "(?x)\n \\b (?<!\\.) (?:\n (self) |
(cls)\n )\\b\n",
+ "captures": {
+ "1": {
+ "name":
"variable.language.special.self.python"
+ },
+ "2": {
+ "name":
"variable.language.special.cls.python"
+ }
+ }
+ },
+ "ellipsis": {
+ "name": "constant.other.ellipsis.python",
+ "match": "\\.\\.\\."
+ },
+ "backticks": {
+ "name": "invalid.deprecated.backtick.python",
+ "begin": "\\`",
+ "end": "(?:\\`|(?<!\\\\)(\\n))",
+ "patterns": [
+ {
+ "include": "#expression"
+ }
+ ]
+ },
+ "illegal-operator": {
+ "patterns": [
+ {
+ "name":
"invalid.illegal.operator.python",
+ "match": "&&|\\|\\||--|\\+\\+"
+ },
+ {
+ "name":
"invalid.illegal.operator.python",
+ "match": "[?$]"
+ },
+ {
+ "name":
"invalid.illegal.operator.python",
+ "comment": "We don't want `!` to flash
when we're typing `!=`",
+ "match": "!\\b"
+ }
+ ]
+ },
+ "illegal-object-name": {
+ "comment": "It's illegal to name class or function
\"True\"",
+ "name": "keyword.illegal.name.python",
+ "match": "\\b(True|False|None)\\b"
+ },
+ "illegal-anno": {
+ "name": "invalid.illegal.annotation.python",
+ "match": "->"
+ },
+ "regexp-base-expression": {
+ "patterns": [
+ {
+ "include": "#regexp-quantifier"
+ },
+ {
+ "include": "#regexp-base-common"
+ }
+ ]
+ },
+ "fregexp-base-expression": {
+ "patterns": [
+ {
+ "include": "#fregexp-quantifier"
+ },
+ {
+ "include": "#fstring-formatting-braces"
+ },
+ {
+ "match": "\\{.*?\\}"
+ },
+ {
+ "include": "#regexp-base-common"
+ }
+ ]
+ },
+ "fstring-formatting-braces": {
+ "patterns": [
+ {
+ "comment": "empty braces are illegal",
+ "match": "({)(\\s*?)(})",
+ "captures": {
+ "1": {
+ "name":
"constant.character.format.placeholder.other.python"
+ },
+ "2": {
+ "name":
"invalid.illegal.brace.python"
+ },
+ "3": {
+ "name":
"constant.character.format.placeholder.other.python"
+ }
+ }
+ },
+ {
+ "name":
"constant.character.escape.python",
+ "match": "({{|}})"
+ }
+ ]
+ },
+ "regexp-base-common": {
+ "patterns": [
+ {
+ "name":
"support.other.match.any.regexp",
+ "match": "\\."
+ },
+ {
+ "name":
"support.other.match.begin.regexp",
+ "match": "\\^"
+ },
+ {
+ "name":
"support.other.match.end.regexp",
+ "match": "\\$"
+ },
+ {
+ "name":
"keyword.operator.quantifier.regexp",
+ "match": "[+*?]\\??"
+ },
+ {
+ "name":
"keyword.operator.disjunction.regexp",
+ "match": "\\|"
+ },
+ {
+ "include": "#regexp-escape-sequence"
+ }
+ ]
+ },
+ "regexp-quantifier": {
+ "name": "keyword.operator.quantifier.regexp",
+ "match": "(?x)\n \\{(\n \\d+ | \\d+,(\\d+)? |
,\\d+\n )\\}\n"
+ },
+ "fregexp-quantifier": {
+ "name": "keyword.operator.quantifier.regexp",
+ "match": "(?x)\n \\{\\{(\n \\d+ | \\d+,(\\d+)? |
,\\d+\n )\\}\\}\n"
+ },
+ "regexp-backreference-number": {
+ "name": "meta.backreference.regexp",
+ "match": "(\\\\[1-9]\\d?)",
+ "captures": {
+ "1": {
+ "name":
"entity.name.tag.backreference.regexp"
+ }
+ }
+ },
+ "regexp-backreference": {
+ "name": "meta.backreference.named.regexp",
+ "match": "(?x)\n (\\() (\\?P=
\\w+(?:\\s+[[:alnum:]]+)?) (\\))\n",
+ "captures": {
+ "1": {
+ "name":
"support.other.parenthesis.regexp
punctuation.parenthesis.backreference.named.begin.regexp"
+ },
+ "2": {
+ "name":
"entity.name.tag.named.backreference.regexp"
+ },
+ "3": {
+ "name":
"support.other.parenthesis.regexp
punctuation.parenthesis.backreference.named.end.regexp"
+ }
+ }
+ },
+ "regexp-flags": {
+ "name": "storage.modifier.flag.regexp",
+ "match": "\\(\\?[aiLmsux]+\\)"
+ },
+ "regexp-escape-special": {
+ "name": "support.other.escape.special.regexp",
+ "match": "\\\\([AbBdDsSwWZ])"
+ },
+ "regexp-escape-character": {
+ "name": "constant.character.escape.regexp",
+ "match": "(?x)\n \\\\ (\n x[0-9A-Fa-f]{2}\n
| 0[0-7]{1,2}\n | [0-7]{3}\n )\n"
+ },
+ "regexp-escape-unicode": {
+ "name": "constant.character.unicode.regexp",
+ "match": "(?x)\n \\\\ (\n u[0-9A-Fa-f]{4}\n
| U[0-9A-Fa-f]{8}\n )\n"
+ },
+ "regexp-escape-catchall": {
+ "name": "constant.character.escape.regexp",
+ "match": "\\\\(.|\\n)"
+ },
+ "regexp-escape-sequence": {
+ "patterns": [
+ {
+ "include": "#regexp-escape-special"
+ },
+ {
+ "include": "#regexp-escape-character"
+ },
+ {
+ "include": "#regexp-escape-unicode"
+ },
+ {
+ "include":
"#regexp-backreference-number"
+ },
+ {
+ "include": "#regexp-escape-catchall"
+ }
+ ]
+ },
+ "regexp-charecter-set-escapes": {
+ "patterns": [
+ {
+ "name":
"constant.character.escape.regexp",
+ "match": "\\\\[abfnrtv\\\\]"
+ },
+ {
+ "include": "#regexp-escape-special"
+ },
+ {
+ "name":
"constant.character.escape.regexp",
+ "match": "\\\\([0-7]{1,3})"
+ },
+ {
+ "include": "#regexp-escape-character"
+ },
+ {
+ "include": "#regexp-escape-unicode"
+ },
+ {
+ "include": "#regexp-escape-catchall"
+ }
+ ]
+ },
+ "codetags": {
+ "match": "(?:\\b(NOTE|XXX|HACK|FIXME|BUG|TODO)\\b)",
+ "captures": {
+ "1": {
+ "name":
"keyword.codetag.notation.python"
+ }
+ }
+ },
+ "comments-base": {
+ "name": "comment.line.number-sign.python",
+ "begin": "(\\#)",
+ "beginCaptures": {
+ "1": {
+ "name":
"punctuation.definition.comment.python"
+ }
+ },
+ "end": "($)",
+ "patterns": [
+ {
+ "include": "#codetags"
+ }
+ ]
+ },
+ "comments-string-single-three": {
+ "name": "comment.line.number-sign.python",
+ "begin": "(\\#)",
+ "beginCaptures": {
+ "1": {
+ "name":
"punctuation.definition.comment.python"
+ }
+ },
+ "end": "($|(?='''))",
+ "patterns": [
+ {
+ "include": "#codetags"
+ }
+ ]
+ },
+ "comments-string-double-three": {
+ "name": "comment.line.number-sign.python",
+ "begin": "(\\#)",
+ "beginCaptures": {
+ "1": {
+ "name":
"punctuation.definition.comment.python"
+ }
+ },
+ "end": "($|(?=\"\"\"))",
+ "patterns": [
+ {
+ "include": "#codetags"
+ }
+ ]
+ },
+ "single-one-regexp-expression": {
+ "patterns": [
+ {
+ "include": "#regexp-base-expression"
+ },
+ {
+ "include":
"#single-one-regexp-character-set"
+ },
+ {
+ "include": "#single-one-regexp-comments"
+ },
+ {
+ "include": "#regexp-flags"
+ },
+ {
+ "include":
"#single-one-regexp-named-group"
+ },
+ {
+ "include": "#regexp-backreference"
+ },
+ {
+ "include":
"#single-one-regexp-lookahead"
+ },
+ {
+ "include":
"#single-one-regexp-lookahead-negative"
+ },
+ {
+ "include":
"#single-one-regexp-lookbehind"
+ },
+ {
+ "include":
"#single-one-regexp-lookbehind-negative"
+ },
+ {
+ "include":
"#single-one-regexp-conditional"
+ },
+ {
+ "include":
"#single-one-regexp-parentheses-non-capturing"
+ },
+ {
+ "include":
"#single-one-regexp-parentheses"
+ }
+ ]
+ },
+ "single-one-regexp-character-set": {
+ "patterns": [
+ {
+ "match": "(?x)\n \\[ \\^? \\] (?!
.*?\\])\n"
+ },
+ {
+ "name": "meta.character.set.regexp",
+ "begin": "(\\[)(\\^)?(\\])?",
+ "end":
"(\\]|(?=\\'))|((?=(?<!\\\\)\\n))",
+ "beginCaptures": {
+ "1": {
+ "name":
"punctuation.character.set.begin.regexp constant.other.set.regexp"
+ },
+ "2": {
+ "name":
"keyword.operator.negation.regexp"
+ },
+ "3": {
+ "name":
"constant.character.set.regexp"
+ }
+ },
+ "endCaptures": {
+ "1": {
+ "name":
"punctuation.character.set.end.regexp constant.other.set.regexp"
+ },
+ "2": {
+ "name":
"invalid.illegal.newline.python"
+ }
+ },
+ "patterns": [
+ {
+ "include":
"#regexp-charecter-set-escapes"
+ },
+ {
+ "name":
"constant.character.set.regexp",
+ "match": "[^\\n]"
+ }
+ ]
+ }
+ ]
+ },
+ "single-one-regexp-named-group": {
+ "name": "meta.named.regexp",
+ "begin": "(?x)\n (\\() (\\?P
<\\w+(?:\\s+[[:alnum:]]+)?>)\n",
+ "end": "(\\)|(?=\\'))|((?=(?<!\\\\)\\n))",
+ "beginCaptures": {
+ "1": {
+ "name":
"support.other.parenthesis.regexp punctuation.parenthesis.named.begin.regexp"
+ },
+ "2": {
+ "name":
"entity.name.tag.named.group.regexp"
+ }
+ },
+ "endCaptures": {
+ "1": {
+ "name":
"support.other.parenthesis.regexp punctuation.parenthesis.named.end.regexp"
+ },
+ "2": {
+ "name": "invalid.illegal.newline.python"
+ }
+ },
+ "patterns": [
+ {
+ "include":
"#single-one-regexp-expression"
+ }
+ ]
+ },
+ "single-one-regexp-comments": {
+ "name": "comment.regexp",
+ "begin": "\\(\\?#",
+ "end": "(\\)|(?=\\'))|((?=(?<!\\\\)\\n))",
+ "beginCaptures": {
+ "0": {
+ "name":
"punctuation.comment.begin.regexp"
+ }
+ },
+ "endCaptures": {
+ "1": {
+ "name": "punctuation.comment.end.regexp"
+ },
+ "2": {
+ "name": "invalid.illegal.newline.python"
+ }
+ },
+ "patterns": [
+ {
+ "include": "#codetags"
+ }
+ ]
+ },
+ "single-one-regexp-lookahead": {
+ "begin": "(\\()\\?=",
+ "end": "(\\)|(?=\\'))|((?=(?<!\\\\)\\n))",
+ "beginCaptures": {
+ "0": {
+ "name":
"keyword.operator.lookahead.regexp"
+ },
+ "1": {
+ "name":
"punctuation.parenthesis.lookahead.begin.regexp"
+ }
+ },
+ "endCaptures": {
+ "1": {
+ "name":
"keyword.operator.lookahead.regexp punctuation.parenthesis.lookahead.end.regexp"
+ },
+ "2": {
+ "name": "invalid.illegal.newline.python"
+ }
+ },
+ "patterns": [
+ {
+ "include":
"#single-one-regexp-expression"
+ }
+ ]
+ },
+ "single-one-regexp-lookahead-negative": {
+ "begin": "(\\()\\?!",
+ "end": "(\\)|(?=\\'))|((?=(?<!\\\\)\\n))",
+ "beginCaptures": {
+ "0": {
+ "name":
"keyword.operator.lookahead.negative.regexp"
+ },
+ "1": {
+ "name":
"punctuation.parenthesis.lookahead.begin.regexp"
+ }
+ },
+ "endCaptures": {
+ "1": {
+ "name":
"keyword.operator.lookahead.negative.regexp
punctuation.parenthesis.lookahead.end.regexp"
+ },
+ "2": {
+ "name": "invalid.illegal.newline.python"
+ }
+ },
+ "patterns": [
+ {
+ "include":
"#single-one-regexp-expression"
+ }
+ ]
+ },
+ "single-one-regexp-lookbehind": {
+ "begin": "(\\()\\?<=",
+ "end": "(\\)|(?=\\'))|((?=(?<!\\\\)\\n))",
+ "beginCaptures": {
+ "0": {
+ "name":
"keyword.operator.lookbehind.regexp"
+ },
+ "1": {
+ "name":
"punctuation.parenthesis.lookbehind.begin.regexp"
+ }
+ },
+ "endCaptures": {
+ "1": {
+ "name":
"keyword.operator.lookbehind.regexp
punctuation.parenthesis.lookbehind.end.regexp"
+ },
+ "2": {
+ "name": "invalid.illegal.newline.python"
+ }
+ },
+ "patterns": [
+ {
+ "include":
"#single-one-regexp-expression"
+ }
+ ]
+ },
+ "single-one-regexp-lookbehind-negative": {
+ "begin": "(\\()\\?<!",
+ "end": "(\\)|(?=\\'))|((?=(?<!\\\\)\\n))",
+ "beginCaptures": {
+ "0": {
+ "name":
"keyword.operator.lookbehind.negative.regexp"
+ },
+ "1": {
+ "name":
"punctuation.parenthesis.lookbehind.begin.regexp"
+ }
+ },
+ "endCaptures": {
+ "1": {
+ "name":
"keyword.operator.lookbehind.negative.regexp
punctuation.parenthesis.lookbehind.end.regexp"
+ },
+ "2": {
+ "name": "invalid.illegal.newline.python"
+ }
+ },
+ "patterns": [
+ {
+ "include":
"#single-one-regexp-expression"
+ }
+ ]
+ },
+ "single-one-regexp-conditional": {
+ "begin":
"(\\()\\?\\((\\w+(?:\\s+[[:alnum:]]+)?|\\d+)\\)",
+ "end": "(\\)|(?=\\'))|((?=(?<!\\\\)\\n))",
+ "beginCaptures": {
+ "0": {
+ "name":
"keyword.operator.conditional.regexp"
+ },
+ "1": {
+ "name":
"punctuation.parenthesis.conditional.begin.regexp"
+ }
+ },
+ "endCaptures": {
+ "1": {
+ "name":
"keyword.operator.conditional.negative.regexp
punctuation.parenthesis.conditional.end.regexp"
+ },
+ "2": {
+ "name": "invalid.illegal.newline.python"
+ }
+ },
+ "patterns": [
+ {
+ "include":
"#single-one-regexp-expression"
+ }
+ ]
+ },
+ "single-one-regexp-parentheses-non-capturing": {
+ "begin": "\\(\\?:",
+ "end": "(\\)|(?=\\'))|((?=(?<!\\\\)\\n))",
+ "beginCaptures": {
+ "0": {
+ "name":
"support.other.parenthesis.regexp
punctuation.parenthesis.non-capturing.begin.regexp"
+ }
+ },
+ "endCaptures": {
+ "1": {
+ "name":
"support.other.parenthesis.regexp
punctuation.parenthesis.non-capturing.end.regexp"
+ },
+ "2": {
+ "name": "invalid.illegal.newline.python"
+ }
+ },
+ "patterns": [
+ {
+ "include":
"#single-one-regexp-expression"
+ }
+ ]
+ },
+ "single-one-regexp-parentheses": {
+ "begin": "\\(",
+ "end": "(\\)|(?=\\'))|((?=(?<!\\\\)\\n))",
+ "beginCaptures": {
+ "0": {
+ "name":
"support.other.parenthesis.regexp punctuation.parenthesis.begin.regexp"
+ }
+ },
+ "endCaptures": {
+ "1": {
+ "name":
"support.other.parenthesis.regexp punctuation.parenthesis.end.regexp"
+ },
+ "2": {
+ "name": "invalid.illegal.newline.python"
+ }
+ },
+ "patterns": [
+ {
+ "include":
"#single-one-regexp-expression"
+ }
+ ]
+ },
+ "single-three-regexp-expression": {
+ "patterns": [
+ {
+ "include": "#regexp-base-expression"
+ },
+ {
+ "include":
"#single-three-regexp-character-set"
+ },
+ {
+ "include":
"#single-three-regexp-comments"
+ },
+ {
+ "include": "#regexp-flags"
+ },
+ {
+ "include":
"#single-three-regexp-named-group"
+ },
+ {
+ "include": "#regexp-backreference"
+ },
+ {
+ "include":
"#single-three-regexp-lookahead"
+ },
+ {
+ "include":
"#single-three-regexp-lookahead-negative"
+ },
+ {
+ "include":
"#single-three-regexp-lookbehind"
+ },
+ {
+ "include":
"#single-three-regexp-lookbehind-negative"
+ },
+ {
+ "include":
"#single-three-regexp-conditional"
+ },
+ {
+ "include":
"#single-three-regexp-parentheses-non-capturing"
+ },
+ {
+ "include":
"#single-three-regexp-parentheses"
+ },
+ {
+ "include":
"#comments-string-single-three"
+ }
+ ]
+ },
+ "single-three-regexp-character-set": {
+ "patterns": [
+ {
+ "match": "(?x)\n \\[ \\^? \\] (?!
.*?\\])\n"
+ },
+ {
+ "name": "meta.character.set.regexp",
+ "begin": "(\\[)(\\^)?(\\])?",
+ "end": "(\\]|(?=\\'\\'\\'))",
+ "beginCaptures": {
+ "1": {
+ "name":
"punctuation.character.set.begin.regexp constant.other.set.regexp"
+ },
+ "2": {
+ "name":
"keyword.operator.negation.regexp"
+ },
+ "3": {
+ "name":
"constant.character.set.regexp"
+ }
+ },
+ "endCaptures": {
+ "1": {
+ "name":
"punctuation.character.set.end.regexp constant.other.set.regexp"
+ },
+ "2": {
+ "name":
"invalid.illegal.newline.python"
+ }
+ },
+ "patterns": [
+ {
+ "include":
"#regexp-charecter-set-escapes"
+ },
+ {
+ "name":
"constant.character.set.regexp",
+ "match": "[^\\n]"
+ }
+ ]
+ }
+ ]
+ },
+ "single-three-regexp-named-group": {
+ "name": "meta.named.regexp",
+ "begin": "(?x)\n (\\() (\\?P
<\\w+(?:\\s+[[:alnum:]]+)?>)\n",
+ "end": "(\\)|(?=\\'\\'\\'))",
+ "beginCaptures": {
+ "1": {
+ "name":
"support.other.parenthesis.regexp punctuation.parenthesis.named.begin.regexp"
+ },
+ "2": {
+ "name":
"entity.name.tag.named.group.regexp"
+ }
+ },
+ "endCaptures": {
+ "1": {
+ "name":
"support.other.parenthesis.regexp punctuation.parenthesis.named.end.regexp"
+ },
+ "2": {
+ "name": "invalid.illegal.newline.python"
+ }
+ },
+ "patterns": [
+ {
+ "include":
"#single-three-regexp-expression"
+ },
+ {
+ "include":
"#comments-string-single-three"
+ }
+ ]
+ },
+ "single-three-regexp-comments": {
+ "name": "comment.regexp",
+ "begin": "\\(\\?#",
+ "end": "(\\)|(?=\\'\\'\\'))",
+ "beginCaptures": {
+ "0": {
+ "name":
"punctuation.comment.begin.regexp"
+ }
+ },
+ "endCaptures": {
+ "1": {
+ "name": "punctuation.comment.end.regexp"
+ },
+ "2": {
+ "name": "invalid.illegal.newline.python"
+ }
+ },
+ "patterns": [
+ {
+ "include": "#codetags"
+ }
+ ]
+ },
+ "single-three-regexp-lookahead": {
+ "begin": "(\\()\\?=",
+ "end": "(\\)|(?=\\'\\'\\'))",
+ "beginCaptures": {
+ "0": {
+ "name":
"keyword.operator.lookahead.regexp"
+ },
+ "1": {
+ "name":
"punctuation.parenthesis.lookahead.begin.regexp"
+ }
+ },
+ "endCaptures": {
+ "1": {
+ "name":
"keyword.operator.lookahead.regexp punctuation.parenthesis.lookahead.end.regexp"
+ },
+ "2": {
+ "name": "invalid.illegal.newline.python"
+ }
+ },
+ "patterns": [
+ {
+ "include":
"#single-three-regexp-expression"
+ },
+ {
+ "include":
"#comments-string-single-three"
+ }
+ ]
+ },
+ "single-three-regexp-lookahead-negative": {
+ "begin": "(\\()\\?!",
+ "end": "(\\)|(?=\\'\\'\\'))",
+ "beginCaptures": {
+ "0": {
+ "name":
"keyword.operator.lookahead.negative.regexp"
+ },
+ "1": {
+ "name":
"punctuation.parenthesis.lookahead.begin.regexp"
+ }
+ },
+ "endCaptures": {
+ "1": {
+ "name":
"keyword.operator.lookahead.negative.regexp
punctuation.parenthesis.lookahead.end.regexp"
+ },
+ "2": {
+ "name": "invalid.illegal.newline.python"
+ }
+ },
+ "patterns": [
+ {
+ "include":
"#single-three-regexp-expression"
+ },
+ {
+ "include":
"#comments-string-single-three"
+ }
+ ]
+ },
+ "single-three-regexp-lookbehind": {
+ "begin": "(\\()\\?<=",
+ "end": "(\\)|(?=\\'\\'\\'))",
+ "beginCaptures": {
+ "0": {
+ "name":
"keyword.operator.lookbehind.regexp"
+ },
+ "1": {
+ "name":
"punctuation.parenthesis.lookbehind.begin.regexp"
+ }
+ },
+ "endCaptures": {
+ "1": {
+ "name":
"keyword.operator.lookbehind.regexp
punctuation.parenthesis.lookbehind.end.regexp"
+ },
+ "2": {
+ "name": "invalid.illegal.newline.python"
+ }
+ },
+ "patterns": [
+ {
+ "include":
"#single-three-regexp-expression"
+ },
+ {
+ "include":
"#comments-string-single-three"
+ }
+ ]
+ },
+ "single-three-regexp-lookbehind-negative": {
+ "begin": "(\\()\\?<!",
+ "end": "(\\)|(?=\\'\\'\\'))",
+ "beginCaptures": {
+ "0": {
+ "name":
"keyword.operator.lookbehind.negative.regexp"
+ },
+ "1": {
+ "name":
"punctuation.parenthesis.lookbehind.begin.regexp"
+ }
+ },
+ "endCaptures": {
+ "1": {
+ "name":
"keyword.operator.lookbehind.negative.regexp
punctuation.parenthesis.lookbehind.end.regexp"
+ },
+ "2": {
+ "name": "invalid.illegal.newline.python"
+ }
+ },
+ "patterns": [
+ {
+ "include":
"#single-three-regexp-expression"
+ },
+ {
+ "include":
"#comments-string-single-three"
+ }
+ ]
+ },
+ "single-three-regexp-conditional": {
+ "begin":
"(\\()\\?\\((\\w+(?:\\s+[[:alnum:]]+)?|\\d+)\\)",
+ "end": "(\\)|(?=\\'\\'\\'))",
+ "beginCaptures": {
+ "0": {
+ "name":
"keyword.operator.conditional.regexp"
+ },
+ "1": {
+ "name":
"punctuation.parenthesis.conditional.begin.regexp"
+ }
+ },
+ "endCaptures": {
+ "1": {
+ "name":
"keyword.operator.conditional.negative.regexp
punctuation.parenthesis.conditional.end.regexp"
+ },
+ "2": {
+ "name": "invalid.illegal.newline.python"
+ }
+ },
+ "patterns": [
+ {
+ "include":
"#single-three-regexp-expression"
+ },
+ {
+ "include":
"#comments-string-single-three"
+ }
+ ]
+ },
+ "single-three-regexp-parentheses-non-capturing": {
+ "begin": "\\(\\?:",
+ "end": "(\\)|(?=\\'\\'\\'))",
+ "beginCaptures": {
+ "0": {
+ "name":
"support.other.parenthesis.regexp
punctuation.parenthesis.non-capturing.begin.regexp"
+ }
+ },
+ "endCaptures": {
+ "1": {
+ "name":
"support.other.parenthesis.regexp
punctuation.parenthesis.non-capturing.end.regexp"
+ },
+ "2": {
+ "name": "invalid.illegal.newline.python"
+ }
+ },
+ "patterns": [
+ {
+ "include":
"#single-three-regexp-expression"
+ },
+ {
+ "include":
"#comments-string-single-three"
+ }
+ ]
+ },
+ "single-three-regexp-parentheses": {
+ "begin": "\\(",
+ "end": "(\\)|(?=\\'\\'\\'))",
+ "beginCaptures": {
+ "0": {
+ "name":
"support.other.parenthesis.regexp punctuation.parenthesis.begin.regexp"
+ }
+ },
+ "endCaptures": {
+ "1": {
+ "name":
"support.other.parenthesis.regexp punctuation.parenthesis.end.regexp"
+ },
+ "2": {
+ "name": "invalid.illegal.newline.python"
+ }
+ },
+ "patterns": [
+ {
+ "include":
"#single-three-regexp-expression"
+ },
+ {
+ "include":
"#comments-string-single-three"
+ }
+ ]
+ },
+ "double-one-regexp-expression": {
+ "patterns": [
+ {
+ "include": "#regexp-base-expression"
+ },
+ {
+ "include":
"#double-one-regexp-character-set"
+ },
+ {
+ "include": "#double-one-regexp-comments"
+ },
+ {
+ "include": "#regexp-flags"
+ },
+ {
+ "include":
"#double-one-regexp-named-group"
+ },
+ {
+ "include": "#regexp-backreference"
+ },
+ {
+ "include":
"#double-one-regexp-lookahead"
+ },
+ {
+ "include":
"#double-one-regexp-lookahead-negative"
+ },
+ {
+ "include":
"#double-one-regexp-lookbehind"
+ },
+ {
+ "include":
"#double-one-regexp-lookbehind-negative"
+ },
+ {
+ "include":
"#double-one-regexp-conditional"
+ },
+ {
+ "include":
"#double-one-regexp-parentheses-non-capturing"
+ },
+ {
+ "include":
"#double-one-regexp-parentheses"
+ }
+ ]
+ },
+ "double-one-regexp-character-set": {
+ "patterns": [
+ {
+ "match": "(?x)\n \\[ \\^? \\] (?!
.*?\\])\n"
+ },
+ {
+ "name": "meta.character.set.regexp",
+ "begin": "(\\[)(\\^)?(\\])?",
+ "end":
"(\\]|(?=\"))|((?=(?<!\\\\)\\n))",
+ "beginCaptures": {
+ "1": {
+ "name":
"punctuation.character.set.begin.regexp constant.other.set.regexp"
+ },
+ "2": {
+ "name":
"keyword.operator.negation.regexp"
+ },
+ "3": {
+ "name":
"constant.character.set.regexp"
+ }
+ },
+ "endCaptures": {
+ "1": {
+ "name":
"punctuation.character.set.end.regexp constant.other.set.regexp"
+ },
+ "2": {
+ "name":
"invalid.illegal.newline.python"
+ }
+ },
+ "patterns": [
+ {
+ "include":
"#regexp-charecter-set-escapes"
+ },
+ {
+ "name":
"constant.character.set.regexp",
+ "match": "[^\\n]"
+ }
+ ]
+ }
+ ]
+ },
+ "double-one-regexp-named-group": {
+ "name": "meta.named.regexp",
+ "begin": "(?x)\n (\\() (\\?P
<\\w+(?:\\s+[[:alnum:]]+)?>)\n",
+ "end": "(\\)|(?=\"))|((?=(?<!\\\\)\\n))",
+ "beginCaptures": {
+ "1": {
+ "name":
"support.other.parenthesis.regexp punctuation.parenthesis.named.begin.regexp"
+ },
+ "2": {
+ "name":
"entity.name.tag.named.group.regexp"
+ }
+ },
+ "endCaptures": {
+ "1": {
+ "name":
"support.other.parenthesis.regexp punctuation.parenthesis.named.end.regexp"
+ },
+ "2": {
+ "name": "invalid.illegal.newline.python"
+ }
+ },
+ "patterns": [
+ {
+ "include":
"#double-one-regexp-expression"
+ }
+ ]
+ },
+ "double-one-regexp-comments": {
+ "name": "comment.regexp",
+ "begin": "\\(\\?#",
+ "end": "(\\)|(?=\"))|((?=(?<!\\\\)\\n))",
+ "beginCaptures": {
+ "0": {
+ "name":
"punctuation.comment.begin.regexp"
+ }
+ },
+ "endCaptures": {
+ "1": {
+ "name": "punctuation.comment.end.regexp"
+ },
+ "2": {
+ "name": "invalid.illegal.newline.python"
+ }
+ },
+ "patterns": [
+ {
+ "include": "#codetags"
+ }
+ ]
+ },
+ "double-one-regexp-lookahead": {
+ "begin": "(\\()\\?=",
+ "end": "(\\)|(?=\"))|((?=(?<!\\\\)\\n))",
+ "beginCaptures": {
+ "0": {
+ "name":
"keyword.operator.lookahead.regexp"
+ },
+ "1": {
+ "name":
"punctuation.parenthesis.lookahead.begin.regexp"
+ }
+ },
+ "endCaptures": {
+ "1": {
+ "name":
"keyword.operator.lookahead.regexp punctuation.parenthesis.lookahead.end.regexp"
+ },
+ "2": {
+ "name": "invalid.illegal.newline.python"
+ }
+ },
+ "patterns": [
+ {
+ "include":
"#double-one-regexp-expression"
+ }
+ ]
+ },
+ "double-one-regexp-lookahead-negative": {
+ "begin": "(\\()\\?!",
+ "end": "(\\)|(?=\"))|((?=(?<!\\\\)\\n))",
+ "beginCaptures": {
+ "0": {
+ "name":
"keyword.operator.lookahead.negative.regexp"
+ },
+ "1": {
+ "name":
"punctuation.parenthesis.lookahead.begin.regexp"
+ }
+ },
+ "endCaptures": {
+ "1": {
+ "name":
"keyword.operator.lookahead.negative.regexp
punctuation.parenthesis.lookahead.end.regexp"
+ },
+ "2": {
+ "name": "invalid.illegal.newline.python"
+ }
+ },
+ "patterns": [
+ {
+ "include":
"#double-one-regexp-expression"
+ }
+ ]
+ },
+ "double-one-regexp-lookbehind": {
+ "begin": "(\\()\\?<=",
+ "end": "(\\)|(?=\"))|((?=(?<!\\\\)\\n))",
+ "beginCaptures": {
+ "0": {
+ "name":
"keyword.operator.lookbehind.regexp"
+ },
+ "1": {
+ "name":
"punctuation.parenthesis.lookbehind.begin.regexp"
+ }
+ },
+ "endCaptures": {
+ "1": {
+ "name":
"keyword.operator.lookbehind.regexp
punctuation.parenthesis.lookbehind.end.regexp"
+ },
+ "2": {
+ "name": "invalid.illegal.newline.python"
+ }
+ },
+ "patterns": [
+ {
+ "include":
"#double-one-regexp-expression"
+ }
+ ]
+ },
+ "double-one-regexp-lookbehind-negative": {
+ "begin": "(\\()\\?<!",
+ "end": "(\\)|(?=\"))|((?=(?<!\\\\)\\n))",
+ "beginCaptures": {
+ "0": {
+ "name":
"keyword.operator.lookbehind.negative.regexp"
+ },
+ "1": {
+ "name":
"punctuation.parenthesis.lookbehind.begin.regexp"
+ }
+ },
+ "endCaptures": {
+ "1": {
+ "name":
"keyword.operator.lookbehind.negative.regexp
punctuation.parenthesis.lookbehind.end.regexp"
+ },
+ "2": {
+ "name": "invalid.illegal.newline.python"
+ }
+ },
+ "patterns": [
+ {
+ "include":
"#double-one-regexp-expression"
+ }
+ ]
+ },
+ "double-one-regexp-conditional": {
+ "begin":
"(\\()\\?\\((\\w+(?:\\s+[[:alnum:]]+)?|\\d+)\\)",
+ "end": "(\\)|(?=\"))|((?=(?<!\\\\)\\n))",
+ "beginCaptures": {
+ "0": {
+ "name":
"keyword.operator.conditional.regexp"
+ },
+ "1": {
+ "name":
"punctuation.parenthesis.conditional.begin.regexp"
+ }
+ },
+ "endCaptures": {
+ "1": {
+ "name":
"keyword.operator.conditional.negative.regexp
punctuation.parenthesis.conditional.end.regexp"
+ },
+ "2": {
+ "name": "invalid.illegal.newline.python"
+ }
+ },
+ "patterns": [
+ {
+ "include":
"#double-one-regexp-expression"
+ }
+ ]
+ },
+ "double-one-regexp-parentheses-non-capturing": {
+ "begin": "\\(\\?:",
+ "end": "(\\)|(?=\"))|((?=(?<!\\\\)\\n))",
+ "beginCaptures": {
+ "0": {
+ "name":
"support.other.parenthesis.regexp
punctuation.parenthesis.non-capturing.begin.regexp"
+ }
+ },
+ "endCaptures": {
+ "1": {
+ "name":
"support.other.parenthesis.regexp
punctuation.parenthesis.non-capturing.end.regexp"
+ },
+ "2": {
+ "name": "invalid.illegal.newline.python"
+ }
+ },
+ "patterns": [
+ {
+ "include":
"#double-one-regexp-expression"
+ }
+ ]
+ },
+ "double-one-regexp-parentheses": {
+ "begin": "\\(",
+ "end": "(\\)|(?=\"))|((?=(?<!\\\\)\\n))",
+ "beginCaptures": {
+ "0": {
+ "name":
"support.other.parenthesis.regexp punctuation.parenthesis.begin.regexp"
+ }
+ },
+ "endCaptures": {
+ "1": {
+ "name":
"support.other.parenthesis.regexp punctuation.parenthesis.end.regexp"
+ },
+ "2": {
+ "name": "invalid.illegal.newline.python"
+ }
+ },
+ "patterns": [
+ {
+ "include":
"#double-one-regexp-expression"
+ }
+ ]
+ },
+ "double-three-regexp-expression": {
+ "patterns": [
+ {
+ "include": "#regexp-base-expression"
+ },
+ {
+ "include":
"#double-three-regexp-character-set"
+ },
+ {
+ "include":
"#double-three-regexp-comments"
+ },
+ {
+ "include": "#regexp-flags"
+ },
+ {
+ "include":
"#double-three-regexp-named-group"
+ },
+ {
+ "include": "#regexp-backreference"
+ },
+ {
+ "include":
"#double-three-regexp-lookahead"
+ },
+ {
+ "include":
"#double-three-regexp-lookahead-negative"
+ },
+ {
+ "include":
"#double-three-regexp-lookbehind"
+ },
+ {
+ "include":
"#double-three-regexp-lookbehind-negative"
+ },
+ {
+ "include":
"#double-three-regexp-conditional"
+ },
+ {
+ "include":
"#double-three-regexp-parentheses-non-capturing"
+ },
+ {
+ "include":
"#double-three-regexp-parentheses"
+ },
+ {
+ "include":
"#comments-string-double-three"
+ }
+ ]
+ },
+ "double-three-regexp-character-set": {
+ "patterns": [
+ {
+ "match": "(?x)\n \\[ \\^? \\] (?!
.*?\\])\n"
+ },
+ {
+ "name": "meta.character.set.regexp",
+ "begin": "(\\[)(\\^)?(\\])?",
+ "end": "(\\]|(?=\"\"\"))",
+ "beginCaptures": {
+ "1": {
+ "name":
"punctuation.character.set.begin.regexp constant.other.set.regexp"
+ },
+ "2": {
+ "name":
"keyword.operator.negation.regexp"
+ },
+ "3": {
+ "name":
"constant.character.set.regexp"
+ }
+ },
+ "endCaptures": {
+ "1": {
+ "name":
"punctuation.character.set.end.regexp constant.other.set.regexp"
+ },
+ "2": {
+ "name":
"invalid.illegal.newline.python"
+ }
+ },
+ "patterns": [
+ {
+ "include":
"#regexp-charecter-set-escapes"
+ },
+ {
+ "name":
"constant.character.set.regexp",
+ "match": "[^\\n]"
+ }
+ ]
+ }
+ ]
+ },
+ "double-three-regexp-named-group": {
+ "name": "meta.named.regexp",
+ "begin": "(?x)\n (\\() (\\?P
<\\w+(?:\\s+[[:alnum:]]+)?>)\n",
+ "end": "(\\)|(?=\"\"\"))",
+ "beginCaptures": {
+ "1": {
+ "name":
"support.other.parenthesis.regexp punctuation.parenthesis.named.begin.regexp"
+ },
+ "2": {
+ "name":
"entity.name.tag.named.group.regexp"
+ }
+ },
+ "endCaptures": {
+ "1": {
+ "name":
"support.other.parenthesis.regexp punctuation.parenthesis.named.end.regexp"
+ },
+ "2": {
+ "name": "invalid.illegal.newline.python"
+ }
+ },
+ "patterns": [
+ {
+ "include":
"#double-three-regexp-expression"
+ },
+ {
+ "include":
"#comments-string-double-three"
+ }
+ ]
+ },
+ "double-three-regexp-comments": {
+ "name": "comment.regexp",
+ "begin": "\\(\\?#",
+ "end": "(\\)|(?=\"\"\"))",
+ "beginCaptures": {
+ "0": {
+ "name":
"punctuation.comment.begin.regexp"
+ }
+ },
+ "endCaptures": {
+ "1": {
+ "name": "punctuation.comment.end.regexp"
+ },
+ "2": {
+ "name": "invalid.illegal.newline.python"
+ }
+ },
+ "patterns": [
+ {
+ "include": "#codetags"
+ }
+ ]
+ },
+ "double-three-regexp-lookahead": {
+ "begin": "(\\()\\?=",
+ "end": "(\\)|(?=\"\"\"))",
+ "beginCaptures": {
+ "0": {
+ "name":
"keyword.operator.lookahead.regexp"
+ },
+ "1": {
+ "name":
"punctuation.parenthesis.lookahead.begin.regexp"
+ }
+ },
+ "endCaptures": {
+ "1": {
+ "name":
"keyword.operator.lookahead.regexp punctuation.parenthesis.lookahead.end.regexp"
+ },
+ "2": {
+ "name": "invalid.illegal.newline.python"
+ }
+ },
+ "patterns": [
+ {
+ "include":
"#double-three-regexp-expression"
+ },
+ {
+ "include":
"#comments-string-double-three"
+ }
+ ]
+ },
+ "double-three-regexp-lookahead-negative": {
+ "begin": "(\\()\\?!",
+ "end": "(\\)|(?=\"\"\"))",
+ "beginCaptures": {
+ "0": {
+ "name":
"keyword.operator.lookahead.negative.regexp"
+ },
+ "1": {
+ "name":
"punctuation.parenthesis.lookahead.begin.regexp"
+ }
+ },
+ "endCaptures": {
+ "1": {
+ "name":
"keyword.operator.lookahead.negative.regexp
punctuation.parenthesis.lookahead.end.regexp"
+ },
+ "2": {
+ "name": "invalid.illegal.newline.python"
+ }
+ },
+ "patterns": [
+ {
+ "include":
"#double-three-regexp-expression"
+ },
+ {
+ "include":
"#comments-string-double-three"
+ }
+ ]
+ },
+ "double-three-regexp-lookbehind": {
+ "begin": "(\\()\\?<=",
+ "end": "(\\)|(?=\"\"\"))",
+ "beginCaptures": {
+ "0": {
+ "name":
"keyword.operator.lookbehind.regexp"
+ },
+ "1": {
+ "name":
"punctuation.parenthesis.lookbehind.begin.regexp"
+ }
+ },
+ "endCaptures": {
+ "1": {
+ "name":
"keyword.operator.lookbehind.regexp
punctuation.parenthesis.lookbehind.end.regexp"
+ },
+ "2": {
+ "name": "invalid.illegal.newline.python"
+ }
+ },
+ "patterns": [
+ {
+ "include":
"#double-three-regexp-expression"
+ },
+ {
+ "include":
"#comments-string-double-three"
+ }
+ ]
+ },
+ "double-three-regexp-lookbehind-negative": {
+ "begin": "(\\()\\?<!",
+ "end": "(\\)|(?=\"\"\"))",
+ "beginCaptures": {
+ "0": {
+ "name":
"keyword.operator.lookbehind.negative.regexp"
+ },
+ "1": {
+ "name":
"punctuation.parenthesis.lookbehind.begin.regexp"
+ }
+ },
+ "endCaptures": {
+ "1": {
+ "name":
"keyword.operator.lookbehind.negative.regexp
punctuation.parenthesis.lookbehind.end.regexp"
+ },
+ "2": {
+ "name": "invalid.illegal.newline.python"
+ }
+ },
+ "patterns": [
+ {
+ "include":
"#double-three-regexp-expression"
+ },
+ {
+ "include":
"#comments-string-double-three"
+ }
+ ]
+ },
+ "double-three-regexp-conditional": {
+ "begin":
"(\\()\\?\\((\\w+(?:\\s+[[:alnum:]]+)?|\\d+)\\)",
+ "end": "(\\)|(?=\"\"\"))",
+ "beginCaptures": {
+ "0": {
+ "name":
"keyword.operator.conditional.regexp"
+ },
+ "1": {
+ "name":
"punctuation.parenthesis.conditional.begin.regexp"
+ }
+ },
+ "endCaptures": {
+ "1": {
+ "name":
"keyword.operator.conditional.negative.regexp
punctuation.parenthesis.conditional.end.regexp"
+ },
+ "2": {
+ "name": "invalid.illegal.newline.python"
+ }
+ },
+ "patterns": [
+ {
+ "include":
"#double-three-regexp-expression"
+ },
+ {
+ "include":
"#comments-string-double-three"
+ }
+ ]
+ },
+ "double-three-regexp-parentheses-non-capturing": {
+ "begin": "\\(\\?:",
+ "end": "(\\)|(?=\"\"\"))",
+ "beginCaptures": {
+ "0": {
+ "name":
"support.other.parenthesis.regexp
punctuation.parenthesis.non-capturing.begin.regexp"
+ }
+ },
+ "endCaptures": {
+ "1": {
+ "name":
"support.other.parenthesis.regexp
punctuation.parenthesis.non-capturing.end.regexp"
+ },
+ "2": {
+ "name": "invalid.illegal.newline.python"
+ }
+ },
+ "patterns": [
+ {
+ "include":
"#double-three-regexp-expression"
+ },
+ {
+ "include":
"#comments-string-double-three"
+ }
+ ]
+ },
+ "double-three-regexp-parentheses": {
+ "begin": "\\(",
+ "end": "(\\)|(?=\"\"\"))",
+ "beginCaptures": {
+ "0": {
+ "name":
"support.other.parenthesis.regexp punctuation.parenthesis.begin.regexp"
+ }
+ },
+ "endCaptures": {
+ "1": {
+ "name":
"support.other.parenthesis.regexp punctuation.parenthesis.end.regexp"
+ },
+ "2": {
+ "name": "invalid.illegal.newline.python"
+ }
+ },
+ "patterns": [
+ {
+ "include":
"#double-three-regexp-expression"
+ },
+ {
+ "include":
"#comments-string-double-three"
+ }
+ ]
+ },
+ "regexp-single-one-line": {
+ "name": "string.regexp.quoted.single.python",
+ "begin": "\\b(([uU]r)|([bB]r)|(r[bB]?))(\\')",
+ "end": "(\\')|(?<!\\\\)(\\n)",
+ "beginCaptures": {
+ "2": {
+ "name":
"invalid.deprecated.prefix.python"
+ },
+ "3": {
+ "name": "storage.type.string.python"
+ },
+ "4": {
+ "name": "storage.type.string.python"
+ },
+ "5": {
+ "name":
"punctuation.definition.string.begin.python"
+ }
+ },
+ "endCaptures": {
+ "1": {
+ "name":
"punctuation.definition.string.end.python"
+ },
+ "2": {
+ "name": "invalid.illegal.newline.python"
+ }
+ },
+ "patterns": [
+ {
+ "include":
"#single-one-regexp-expression"
+ }
+ ]
+ },
+ "regexp-single-three-line": {
+ "name": "string.regexp.quoted.multi.python",
+ "begin": "\\b(([uU]r)|([bB]r)|(r[bB]?))(\\'\\'\\')",
+ "end": "(\\'\\'\\')",
+ "beginCaptures": {
+ "2": {
+ "name":
"invalid.deprecated.prefix.python"
+ },
+ "3": {
+ "name": "storage.type.string.python"
+ },
+ "4": {
+ "name": "storage.type.string.python"
+ },
+ "5": {
+ "name":
"punctuation.definition.string.begin.python"
+ }
+ },
+ "endCaptures": {
+ "1": {
+ "name":
"punctuation.definition.string.end.python"
+ },
+ "2": {
+ "name": "invalid.illegal.newline.python"
+ }
+ },
+ "patterns": [
+ {
+ "include":
"#single-three-regexp-expression"
+ }
+ ]
+ },
+ "regexp-double-one-line": {
+ "name": "string.regexp.quoted.single.python",
+ "begin": "\\b(([uU]r)|([bB]r)|(r[bB]?))(\")",
+ "end": "(\")|(?<!\\\\)(\\n)",
+ "beginCaptures": {
+ "2": {
+ "name":
"invalid.deprecated.prefix.python"
+ },
+ "3": {
+ "name": "storage.type.string.python"
+ },
+ "4": {
+ "name": "storage.type.string.python"
+ },
+ "5": {
+ "name":
"punctuation.definition.string.begin.python"
+ }
+ },
+ "endCaptures": {
+ "1": {
+ "name":
"punctuation.definition.string.end.python"
+ },
+ "2": {
+ "name": "invalid.illegal.newline.python"
+ }
+ },
+ "patterns": [
+ {
+ "include":
"#double-one-regexp-expression"
+ }
+ ]
+ },
+ "regexp-double-three-line": {
+ "name": "string.regexp.quoted.multi.python",
+ "begin": "\\b(([uU]r)|([bB]r)|(r[bB]?))(\"\"\")",
+ "end": "(\"\"\")",
+ "beginCaptures": {
+ "2": {
+ "name":
"invalid.deprecated.prefix.python"
+ },
+ "3": {
+ "name": "storage.type.string.python"
+ },
+ "4": {
+ "name": "storage.type.string.python"
+ },
+ "5": {
+ "name":
"punctuation.definition.string.begin.python"
+ }
+ },
+ "endCaptures": {
+ "1": {
+ "name":
"punctuation.definition.string.end.python"
+ },
+ "2": {
+ "name": "invalid.illegal.newline.python"
+ }
+ },
+ "patterns": [
+ {
+ "include":
"#double-three-regexp-expression"
+ }
+ ]
+ },
+ "string-raw-quoted-single-line": {
+ "name": "string.quoted.raw.single.python",
+ "begin": "\\b(([uU]R)|(R))((['\"]))",
+ "end": "(\\4)|((?<!\\\\)\\n)",
+ "beginCaptures": {
+ "2": {
+ "name":
"invalid.deprecated.prefix.python"
+ },
+ "3": {
+ "name": "storage.type.string.python"
+ },
+ "4": {
+ "name":
"punctuation.definition.string.begin.python"
+ }
+ },
+ "endCaptures": {
+ "1": {
+ "name":
"punctuation.definition.string.end.python"
+ },
+ "2": {
+ "name": "invalid.illegal.newline.python"
+ }
+ },
+ "patterns": [
+ {
+ "include":
"#string-single-bad-brace1-formatting-raw"
+ },
+ {
+ "include":
"#string-single-bad-brace2-formatting-raw"
+ },
+ {
+ "include": "#string-raw-guts"
+ }
+ ]
+ },
+ "string-bin-quoted-single-line": {
+ "name": "string.quoted.binary.single.python",
+ "begin": "(\\b[bB])((['\"]))",
+ "end": "(\\2)|((?<!\\\\)\\n)",
+ "beginCaptures": {
+ "1": {
+ "name": "storage.type.string.python"
+ },
+ "2": {
+ "name":
"punctuation.definition.string.begin.python"
+ }
+ },
+ "endCaptures": {
+ "1": {
+ "name":
"punctuation.definition.string.end.python"
+ },
+ "2": {
+ "name": "invalid.illegal.newline.python"
+ }
+ },
+ "patterns": [
+ {
+ "include": "#string-entity"
+ }
+ ]
+ },
+ "string-raw-bin-quoted-single-line": {
+ "name": "string.quoted.raw.binary.single.python",
+ "begin": "(\\b(?:R[bB]|[bB]R))((['\"]))",
+ "end": "(\\2)|((?<!\\\\)\\n)",
+ "beginCaptures": {
+ "1": {
+ "name": "storage.type.string.python"
+ },
+ "2": {
+ "name":
"punctuation.definition.string.begin.python"
+ }
+ },
+ "endCaptures": {
+ "1": {
+ "name":
"punctuation.definition.string.end.python"
+ },
+ "2": {
+ "name": "invalid.illegal.newline.python"
+ }
+ },
+ "patterns": [
+ {
+ "include": "#string-raw-bin-guts"
+ }
+ ]
+ },
+ "string-quoted-single-line": {
+ "name": "string.quoted.single.python",
+ "begin": "(?:\\b([rR])(?=[uU]))?([uU])?((['\"]))",
+ "end": "(\\3)|((?<!\\\\)\\n)",
+ "beginCaptures": {
+ "1": {
+ "name": "invalid.illegal.prefix.python"
+ },
+ "2": {
+ "name": "storage.type.string.python"
+ },
+ "3": {
+ "name":
"punctuation.definition.string.begin.python"
+ }
+ },
+ "endCaptures": {
+ "1": {
+ "name":
"punctuation.definition.string.end.python"
+ },
+ "2": {
+ "name": "invalid.illegal.newline.python"
+ }
+ },
+ "patterns": [
+ {
+ "include":
"#string-single-bad-brace1-formatting-unicode"
+ },
+ {
+ "include":
"#string-single-bad-brace2-formatting-unicode"
+ },
+ {
+ "include": "#string-unicode-guts"
+ }
+ ]
+ },
+ "string-single-bad-brace1-formatting-unicode": {
+ "comment": "template using {% ... %}",
+ "begin": "(?x)\n (?= \\{%\n ( .*?
(?!(['\"])|((?<!\\\\)\\n)) )\n %\\}\n )\n",
+ "end": "(?=(['\"])|((?<!\\\\)\\n))",
+ "patterns": [
+ {
+ "include": "#escape-sequence-unicode"
+ },
+ {
+ "include": "#escape-sequence"
+ },
+ {
+ "include": "#string-line-continuation"
+ }
+ ]
+ },
+ "string-single-bad-brace1-formatting-raw": {
+ "comment": "template using {% ... %}",
+ "begin": "(?x)\n (?= \\{%\n ( .*?
(?!(['\"])|((?<!\\\\)\\n)) )\n %\\}\n )\n",
+ "end": "(?=(['\"])|((?<!\\\\)\\n))",
+ "patterns": [
+ {
+ "include": "#string-consume-escape"
+ }
+ ]
+ },
+ "string-single-bad-brace2-formatting-unicode": {
+ "comment": "odd format or format-like syntax",
+ "begin": "(?x)\n (?!\\{\\{)\n (?= \\{ (\n
\\w*? (?!(['\"])|((?<!\\\\)\\n)) [^!:\\.\\[}\\w]\n )\n
.*?(?!(['\"])|((?<!\\\\)\\n))\n \\}\n )\n",
+ "end": "(?=(['\"])|((?<!\\\\)\\n))",
+ "patterns": [
+ {
+ "include": "#escape-sequence-unicode"
+ },
+ {
+ "include": "#string-entity"
+ }
+ ]
+ },
+ "string-single-bad-brace2-formatting-raw": {
+ "comment": "odd format or format-like syntax",
+ "begin": "(?x)\n (?!\\{\\{)\n (?= \\{ (\n
\\w*? (?!(['\"])|((?<!\\\\)\\n)) [^!:\\.\\[}\\w]\n )\n
.*?(?!(['\"])|((?<!\\\\)\\n))\n \\}\n )\n",
+ "end": "(?=(['\"])|((?<!\\\\)\\n))",
+ "patterns": [
+ {
+ "include": "#string-consume-escape"
+ },
+ {
+ "include": "#string-formatting"
+ }
+ ]
+ },
+ "string-raw-quoted-multi-line": {
+ "name": "string.quoted.raw.multi.python",
+ "begin": "\\b(([uU]R)|(R))('''|\"\"\")",
+ "end": "(\\4)",
+ "beginCaptures": {
+ "2": {
+ "name":
"invalid.deprecated.prefix.python"
+ },
+ "3": {
+ "name": "storage.type.string.python"
+ },
+ "4": {
+ "name":
"punctuation.definition.string.begin.python"
+ }
+ },
+ "endCaptures": {
+ "1": {
+ "name":
"punctuation.definition.string.end.python"
+ },
+ "2": {
+ "name": "invalid.illegal.newline.python"
+ }
+ },
+ "patterns": [
+ {
+ "include":
"#string-multi-bad-brace1-formatting-raw"
+ },
+ {
+ "include":
"#string-multi-bad-brace2-formatting-raw"
+ },
+ {
+ "include": "#string-raw-guts"
+ }
+ ]
+ },
+ "string-bin-quoted-multi-line": {
+ "name": "string.quoted.binary.multi.python",
+ "begin": "(\\b[bB])('''|\"\"\")",
+ "end": "(\\2)",
+ "beginCaptures": {
+ "1": {
+ "name": "storage.type.string.python"
+ },
+ "2": {
+ "name":
"punctuation.definition.string.begin.python"
+ }
+ },
+ "endCaptures": {
+ "1": {
+ "name":
"punctuation.definition.string.end.python"
+ },
+ "2": {
+ "name": "invalid.illegal.newline.python"
+ }
+ },
+ "patterns": [
+ {
+ "include": "#string-entity"
+ }
+ ]
+ },
+ "string-raw-bin-quoted-multi-line": {
+ "name": "string.quoted.raw.binary.multi.python",
+ "begin": "(\\b(?:R[bB]|[bB]R))('''|\"\"\")",
+ "end": "(\\2)",
+ "beginCaptures": {
+ "1": {
+ "name": "storage.type.string.python"
+ },
+ "2": {
+ "name":
"punctuation.definition.string.begin.python"
+ }
+ },
+ "endCaptures": {
+ "1": {
+ "name":
"punctuation.definition.string.end.python"
+ },
+ "2": {
+ "name": "invalid.illegal.newline.python"
+ }
+ },
+ "patterns": [
+ {
+ "include": "#string-raw-bin-guts"
+ }
+ ]
+ },
+ "string-quoted-multi-line": {
+ "name": "string.quoted.multi.python",
+ "begin": "(?:\\b([rR])(?=[uU]))?([uU])?('''|\"\"\")",
+ "end": "(\\3)",
+ "beginCaptures": {
+ "1": {
+ "name": "invalid.illegal.prefix.python"
+ },
+ "2": {
+ "name": "storage.type.string.python"
+ },
+ "3": {
+ "name":
"punctuation.definition.string.begin.python"
+ }
+ },
+ "endCaptures": {
+ "1": {
+ "name":
"punctuation.definition.string.end.python"
+ },
+ "2": {
+ "name": "invalid.illegal.newline.python"
+ }
+ },
+ "patterns": [
+ {
+ "include":
"#string-multi-bad-brace1-formatting-unicode"
+ },
+ {
+ "include":
"#string-multi-bad-brace2-formatting-unicode"
+ },
+ {
+ "include": "#string-unicode-guts"
+ }
+ ]
+ },
+ "string-multi-bad-brace1-formatting-unicode": {
+ "comment": "template using {% ... %}",
+ "begin": "(?x)\n (?= \\{%\n ( .*?
(?!'''|\"\"\") )\n %\\}\n )\n",
+ "end": "(?='''|\"\"\")",
+ "patterns": [
+ {
+ "include": "#escape-sequence-unicode"
+ },
+ {
+ "include": "#escape-sequence"
+ },
+ {
+ "include": "#string-line-continuation"
+ }
+ ]
+ },
+ "string-multi-bad-brace1-formatting-raw": {
+ "comment": "template using {% ... %}",
+ "begin": "(?x)\n (?= \\{%\n ( .*?
(?!'''|\"\"\") )\n %\\}\n )\n",
+ "end": "(?='''|\"\"\")",
+ "patterns": [
+ {
+ "include": "#string-consume-escape"
+ }
+ ]
+ },
+ "string-multi-bad-brace2-formatting-unicode": {
+ "comment": "odd format or format-like syntax",
+ "begin": "(?x)\n (?!\\{\\{)\n (?= \\{ (\n
\\w*? (?!'''|\"\"\") [^!:\\.\\[}\\w]\n )\n
.*?(?!'''|\"\"\")\n \\}\n )\n",
+ "end": "(?='''|\"\"\")",
+ "patterns": [
+ {
+ "include": "#escape-sequence-unicode"
+ },
+ {
+ "include": "#string-entity"
+ }
+ ]
+ },
+ "string-multi-bad-brace2-formatting-raw": {
+ "comment": "odd format or format-like syntax",
+ "begin": "(?x)\n (?!\\{\\{)\n (?= \\{ (\n
\\w*? (?!'''|\"\"\") [^!:\\.\\[}\\w]\n )\n
.*?(?!'''|\"\"\")\n \\}\n )\n",
+ "end": "(?='''|\"\"\")",
+ "patterns": [
+ {
+ "include": "#string-consume-escape"
+ },
+ {
+ "include": "#string-formatting"
+ }
+ ]
+ },
+ "fstring-fnorm-quoted-single-line": {
+ "name": "meta.fstring.python",
+ "begin": "(\\b[fF])([bBuU])?((['\"]))",
+ "end": "(\\3)|((?<!\\\\)\\n)",
+ "beginCaptures": {
+ "1": {
+ "name": "string.interpolated.python
string.quoted.single.python storage.type.string.python"
+ },
+ "2": {
+ "name": "invalid.illegal.prefix.python"
+ },
+ "3": {
+ "name":
"punctuation.definition.string.begin.python string.interpolated.python
string.quoted.single.python"
+ }
+ },
+ "endCaptures": {
+ "1": {
+ "name":
"punctuation.definition.string.end.python string.interpolated.python
string.quoted.single.python"
+ },
+ "2": {
+ "name": "invalid.illegal.newline.python"
+ }
+ },
+ "patterns": [
+ {
+ "include": "#fstring-guts"
+ },
+ {
+ "include":
"#fstring-illegal-single-brace"
+ },
+ {
+ "include": "#fstring-single-brace"
+ },
+ {
+ "include": "#fstring-single-core"
+ }
+ ]
+ },
+ "fstring-normf-quoted-single-line": {
+ "name": "meta.fstring.python",
+ "begin": "(\\b[bBuU])([fF])((['\"]))",
+ "end": "(\\3)|((?<!\\\\)\\n)",
+ "beginCaptures": {
+ "1": {
+ "name": "invalid.illegal.prefix.python"
+ },
+ "2": {
+ "name": "string.interpolated.python
string.quoted.single.python storage.type.string.python"
+ },
+ "3": {
+ "name":
"punctuation.definition.string.begin.python string.quoted.single.python"
+ }
+ },
+ "endCaptures": {
+ "1": {
+ "name":
"punctuation.definition.string.end.python string.interpolated.python
string.quoted.single.python"
+ },
+ "2": {
+ "name": "invalid.illegal.newline.python"
+ }
+ },
+ "patterns": [
+ {
+ "include": "#fstring-guts"
+ },
+ {
+ "include":
"#fstring-illegal-single-brace"
+ },
+ {
+ "include": "#fstring-single-brace"
+ },
+ {
+ "include": "#fstring-single-core"
+ }
+ ]
+ },
+ "fstring-raw-quoted-single-line": {
+ "name": "meta.fstring.python",
+ "begin": "(\\b(?:[rR][fF]|[fF][rR]))((['\"]))",
+ "end": "(\\2)|((?<!\\\\)\\n)",
+ "beginCaptures": {
+ "1": {
+ "name": "string.interpolated.python
string.quoted.raw.single.python storage.type.string.python"
+ },
+ "2": {
+ "name":
"punctuation.definition.string.begin.python string.quoted.raw.single.python"
+ }
+ },
+ "endCaptures": {
+ "1": {
+ "name":
"punctuation.definition.string.end.python string.interpolated.python
string.quoted.raw.single.python"
+ },
+ "2": {
+ "name": "invalid.illegal.newline.python"
+ }
+ },
+ "patterns": [
+ {
+ "include": "#fstring-raw-guts"
+ },
+ {
+ "include":
"#fstring-illegal-single-brace"
+ },
+ {
+ "include": "#fstring-single-brace"
+ },
+ {
+ "include": "#fstring-raw-single-core"
+ }
+ ]
+ },
+ "fstring-single-core": {
+ "name": "string.interpolated.python
string.quoted.single.python",
+ "match": "(?x)\n (.+?)\n (\n (?# .* and .*? in
multi-line match need special handling of\n newlines otherwise
SublimeText and Atom will match slightly\n differently.\n\n The
guard for newlines has to be separate from the\n lookahead because of
special $ matching rule.)\n ($\\n?)\n |\n
(?=[\\\\\\}\\{]|(['\"])|((?<!\\\\)\\n))\n )\n (?# due to how multiline
regexps are matched we need a special case\n for matching a newline chara
[...]
+ },
+ "fstring-raw-single-core": {
+ "name": "string.interpolated.python
string.quoted.raw.single.python",
+ "match": "(?x)\n (.+?)\n (\n (?# .* and .*? in
multi-line match need special handling of\n newlines otherwise
SublimeText and Atom will match slightly\n differently.\n\n The
guard for newlines has to be separate from the\n lookahead because of
special $ matching rule.)\n ($\\n?)\n |\n
(?=[\\\\\\}\\{]|(['\"])|((?<!\\\\)\\n))\n )\n (?# due to how multiline
regexps are matched we need a special case\n for matching a newline chara
[...]
+ },
+ "fstring-single-brace": {
+ "comment": "value interpolation using { ... }",
+ "begin": "(\\{)",
+ "end": "(?x)\n (\\})|(?=\\n)\n",
+ "beginCaptures": {
+ "1": {
+ "name":
"constant.character.format.placeholder.other.python"
+ }
+ },
+ "endCaptures": {
+ "1": {
+ "name":
"constant.character.format.placeholder.other.python"
+ }
+ },
+ "patterns": [
+ {
+ "include": "#fstring-terminator-single"
+ },
+ {
+ "include": "#f-expression"
+ }
+ ]
+ },
+ "fstring-terminator-single": {
+ "patterns": [
+ {
+ "name": "storage.type.format.python",
+ "match": "(=(![rsa])?)(?=})"
+ },
+ {
+ "name": "storage.type.format.python",
+ "match": "(=?![rsa])(?=})"
+ },
+ {
+ "match": "(?x)\n ( (?: =?) (?:
![rsa])? )\n ( : \\w? [<>=^]? [-+ ]? \\#?\n \\d* ,? (\\.\\d+)?
[bcdeEfFgGnosxX%]? )(?=})\n",
+ "captures": {
+ "1": {
+ "name":
"storage.type.format.python"
+ },
+ "2": {
+ "name":
"storage.type.format.python"
+ }
+ }
+ },
+ {
+ "include":
"#fstring-terminator-single-tail"
+ }
+ ]
+ },
+ "fstring-terminator-single-tail": {
+ "begin": "((?:=?)(?:![rsa])?)(:)(?=.*?{)",
+ "end": "(?=})|(?=\\n)",
+ "beginCaptures": {
+ "1": {
+ "name": "storage.type.format.python"
+ },
+ "2": {
+ "name": "storage.type.format.python"
+ }
+ },
+ "patterns": [
+ {
+ "include":
"#fstring-illegal-single-brace"
+ },
+ {
+ "include": "#fstring-single-brace"
+ },
+ {
+ "name": "storage.type.format.python",
+ "match": "([bcdeEfFgGnosxX%])(?=})"
+ },
+ {
+ "name": "storage.type.format.python",
+ "match": "(\\.\\d+)"
+ },
+ {
+ "name": "storage.type.format.python",
+ "match": "(,)"
+ },
+ {
+ "name": "storage.type.format.python",
+ "match": "(\\d+)"
+ },
+ {
+ "name": "storage.type.format.python",
+ "match": "(\\#)"
+ },
+ {
+ "name": "storage.type.format.python",
+ "match": "([-+ ])"
+ },
+ {
+ "name": "storage.type.format.python",
+ "match": "([<>=^])"
+ },
+ {
+ "name": "storage.type.format.python",
+ "match": "(\\w)"
+ }
+ ]
+ },
+ "fstring-fnorm-quoted-multi-line": {
+ "name": "meta.fstring.python",
+ "begin": "(\\b[fF])([bBuU])?('''|\"\"\")",
+ "end": "(\\3)",
+ "beginCaptures": {
+ "1": {
+ "name": "string.interpolated.python
string.quoted.multi.python storage.type.string.python"
+ },
+ "2": {
+ "name": "invalid.illegal.prefix.python"
+ },
+ "3": {
+ "name":
"punctuation.definition.string.begin.python string.interpolated.python
string.quoted.multi.python"
+ }
+ },
+ "endCaptures": {
+ "1": {
+ "name":
"punctuation.definition.string.end.python string.interpolated.python
string.quoted.multi.python"
+ },
+ "2": {
+ "name": "invalid.illegal.newline.python"
+ }
+ },
+ "patterns": [
+ {
+ "include": "#fstring-guts"
+ },
+ {
+ "include":
"#fstring-illegal-multi-brace"
+ },
+ {
+ "include": "#fstring-multi-brace"
+ },
+ {
+ "include": "#fstring-multi-core"
+ }
+ ]
+ },
+ "fstring-normf-quoted-multi-line": {
+ "name": "meta.fstring.python",
+ "begin": "(\\b[bBuU])([fF])('''|\"\"\")",
+ "end": "(\\3)",
+ "beginCaptures": {
+ "1": {
+ "name": "invalid.illegal.prefix.python"
+ },
+ "2": {
+ "name": "string.interpolated.python
string.quoted.multi.python storage.type.string.python"
+ },
+ "3": {
+ "name":
"punctuation.definition.string.begin.python string.quoted.multi.python"
+ }
+ },
+ "endCaptures": {
+ "1": {
+ "name":
"punctuation.definition.string.end.python string.interpolated.python
string.quoted.multi.python"
+ },
+ "2": {
+ "name": "invalid.illegal.newline.python"
+ }
+ },
+ "patterns": [
+ {
+ "include": "#fstring-guts"
+ },
+ {
+ "include":
"#fstring-illegal-multi-brace"
+ },
+ {
+ "include": "#fstring-multi-brace"
+ },
+ {
+ "include": "#fstring-multi-core"
+ }
+ ]
+ },
+ "fstring-raw-quoted-multi-line": {
+ "name": "meta.fstring.python",
+ "begin": "(\\b(?:[rR][fF]|[fF][rR]))('''|\"\"\")",
+ "end": "(\\2)",
+ "beginCaptures": {
+ "1": {
+ "name": "string.interpolated.python
string.quoted.raw.multi.python storage.type.string.python"
+ },
+ "2": {
+ "name":
"punctuation.definition.string.begin.python string.quoted.raw.multi.python"
+ }
+ },
+ "endCaptures": {
+ "1": {
+ "name":
"punctuation.definition.string.end.python string.interpolated.python
string.quoted.raw.multi.python"
+ },
+ "2": {
+ "name": "invalid.illegal.newline.python"
+ }
+ },
+ "patterns": [
+ {
+ "include": "#fstring-raw-guts"
+ },
+ {
+ "include":
"#fstring-illegal-multi-brace"
+ },
+ {
+ "include": "#fstring-multi-brace"
+ },
+ {
+ "include": "#fstring-raw-multi-core"
+ }
+ ]
+ },
+ "fstring-multi-core": {
+ "name": "string.interpolated.python
string.quoted.multi.python",
+ "match": "(?x)\n (.+?)\n (\n (?# .* and .*? in
multi-line match need special handling of\n newlines otherwise
SublimeText and Atom will match slightly\n differently.\n\n The
guard for newlines has to be separate from the\n lookahead because of
special $ matching rule.)\n ($\\n?)\n |\n
(?=[\\\\\\}\\{]|'''|\"\"\")\n )\n (?# due to how multiline regexps are
matched we need a special case\n for matching a newline character)\n |
\\n\n"
+ },
+ "fstring-raw-multi-core": {
+ "name": "string.interpolated.python
string.quoted.raw.multi.python",
+ "match": "(?x)\n (.+?)\n (\n (?# .* and .*? in
multi-line match need special handling of\n newlines otherwise
SublimeText and Atom will match slightly\n differently.\n\n The
guard for newlines has to be separate from the\n lookahead because of
special $ matching rule.)\n ($\\n?)\n |\n
(?=[\\\\\\}\\{]|'''|\"\"\")\n )\n (?# due to how multiline regexps are
matched we need a special case\n for matching a newline character)\n |
\\n\n"
+ },
+ "fstring-multi-brace": {
+ "comment": "value interpolation using { ... }",
+ "begin": "(\\{)",
+ "end": "(?x)\n (\\})\n",
+ "beginCaptures": {
+ "1": {
+ "name":
"constant.character.format.placeholder.other.python"
+ }
+ },
+ "endCaptures": {
+ "1": {
+ "name":
"constant.character.format.placeholder.other.python"
+ }
+ },
+ "patterns": [
+ {
+ "include": "#fstring-terminator-multi"
+ },
+ {
+ "include": "#f-expression"
+ }
+ ]
+ },
+ "fstring-terminator-multi": {
+ "patterns": [
+ {
+ "name": "storage.type.format.python",
+ "match": "(=(![rsa])?)(?=})"
+ },
+ {
+ "name": "storage.type.format.python",
+ "match": "(=?![rsa])(?=})"
+ },
+ {
+ "match": "(?x)\n ( (?: =?) (?:
![rsa])? )\n ( : \\w? [<>=^]? [-+ ]? \\#?\n \\d* ,? (\\.\\d+)?
[bcdeEfFgGnosxX%]? )(?=})\n",
+ "captures": {
+ "1": {
+ "name":
"storage.type.format.python"
+ },
+ "2": {
+ "name":
"storage.type.format.python"
+ }
+ }
+ },
+ {
+ "include":
"#fstring-terminator-multi-tail"
+ }
+ ]
+ },
+ "fstring-terminator-multi-tail": {
+ "begin": "((?:=?)(?:![rsa])?)(:)(?=.*?{)",
+ "end": "(?=})",
+ "beginCaptures": {
+ "1": {
+ "name": "storage.type.format.python"
+ },
+ "2": {
+ "name": "storage.type.format.python"
+ }
+ },
+ "patterns": [
+ {
+ "include":
"#fstring-illegal-multi-brace"
+ },
+ {
+ "include": "#fstring-multi-brace"
+ },
+ {
+ "name": "storage.type.format.python",
+ "match": "([bcdeEfFgGnosxX%])(?=})"
+ },
+ {
+ "name": "storage.type.format.python",
+ "match": "(\\.\\d+)"
+ },
+ {
+ "name": "storage.type.format.python",
+ "match": "(,)"
+ },
+ {
+ "name": "storage.type.format.python",
+ "match": "(\\d+)"
+ },
+ {
+ "name": "storage.type.format.python",
+ "match": "(\\#)"
+ },
+ {
+ "name": "storage.type.format.python",
+ "match": "([-+ ])"
+ },
+ {
+ "name": "storage.type.format.python",
+ "match": "([<>=^])"
+ },
+ {
+ "name": "storage.type.format.python",
+ "match": "(\\w)"
+ }
+ ]
+ }
+ }
+}
\ No newline at end of file
diff --git
a/rcp/src/test/java/org/apache/hop/ui/hopgui/ContentEditorTm4eSupportTest.java
b/rcp/src/test/java/org/apache/hop/ui/hopgui/ContentEditorTm4eSupportTest.java
new file mode 100644
index 0000000000..63cfc77a72
--- /dev/null
+++
b/rcp/src/test/java/org/apache/hop/ui/hopgui/ContentEditorTm4eSupportTest.java
@@ -0,0 +1,47 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.hop.ui.hopgui;
+
+import static org.junit.jupiter.api.Assertions.assertEquals;
+import static org.junit.jupiter.api.Assertions.assertNotNull;
+import static org.junit.jupiter.api.Assertions.assertTrue;
+
+import java.io.InputStream;
+import org.junit.jupiter.api.Test;
+
+class ContentEditorTm4eSupportTest {
+
+ @Test
+ void scopeForLanguage_python_returnsSourcePython() {
+ assertEquals("source.python",
ContentEditorTm4eSupport.scopeForLanguage("python"));
+ }
+
+ @Test
+ void scopeForLanguage_py_returnsSourcePython() {
+ assertEquals("source.python",
ContentEditorTm4eSupport.scopeForLanguage("py"));
+ }
+
+ @Test
+ void pythonGrammarResourceIsOnClasspath() throws Exception {
+ try (InputStream in =
+
ContentEditorTm4eSupport.class.getResourceAsStream("grammars/python.json")) {
+ assertNotNull(in, "grammars/python.json should be on the classpath");
+ assertTrue(in.read() >= 0, "python.json should not be empty");
+ }
+ }
+}
