Skip to content

Configure comment chars based on language #38

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 3 commits into from
Jun 19, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 5 additions & 2 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,10 @@ All notable changes to this project will be documented in this file.

## [Unreleased]

- Select text using click and drag ([#8](https://github.com/tconbeer/textual-textarea/issues/8))
- Select text using click and drag ([#8](https://github.com/tconbeer/textual-textarea/issues/8)).
- Comment characters inserted with <kbd>ctrl+/</kbd> are now based on the language that the
TextArea is initialized with ([#24](https://github.com/tconbeer/textual-textarea/issues/24)).
- TextArea exposes a `language` property for the currently-configured language.

## [0.2.2] - 2023-06-15

Expand All @@ -14,7 +17,7 @@ All notable changes to this project will be documented in this file.
- Adds 3 attributes to TextArea to make it easier to access the child widgets: `text_input`, `text_container`, and `footer`.
### Bug Fixes

- Fixes a bug that was preventing the cursor from being scrolled into view
- Fixes a bug that was preventing the cursor from being scrolled into view.

## [0.2.1] - 2023-06-15

Expand Down
16 changes: 14 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ pip install textual-textarea
Full-featured text editor experience with VS-Code-like bindings, in your Textual App:
- Syntax highlighting and support for themes.
- Move cursor and scroll with mouse or keys (including <kbd>ctrl+arrow</kbd>, <kbd>PgUp/Dn</kbd>, <kbd>Home/End</kbd>).
- Select text using <kbd>shift</kbd>.
- Select text using <kbd>shift</kbd> or click and drag.
- Open (<kbd>ctrl+o</kbd>) and save (<kbd>ctrl+s</kbd>) files.
- Cut (<kbd>ctrl+x</kbd>), copy (<kbd>ctrl+c</kbd>), paste (<kbd>ctrl+u/v</kbd>), optionally using the system clipboard.
- Comment selections with <kbd>ctrl+/</kbd>.
Expand Down Expand Up @@ -76,6 +76,18 @@ cursor_line_number = ta.cursor.lno
cursor_x_position = ta.cursor.pos
```

#### Getting and Setting The Language

Syntax highlighting and comment insertion depends on the configured language for the TextArea.

The TextArea exposes a `language` property that returns `None` or a string that is equal to the short name of the [Pygments lexer](https://pygments.org/docs/lexers/) for the currently configured language:

```python
ta = self.query_one(TextArea)
old_language = ta.language
ta.language = "python"
```

#### Getting Theme Colors

If you would like the rest of your app to match the colors from the TextArea's theme, they are exposed via the `theme_colors` property.
Expand Down Expand Up @@ -109,4 +121,4 @@ class CodeEditor(TextArea):

async def action_submit(self) -> None:
self.post_message(self.Submitted(self.text))
```
```
185 changes: 185 additions & 0 deletions src/textual_textarea/comments.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,185 @@
INLINE_MARKERS = {
"abap": '"',
"actionscript": "//",
"as": "//",
"actionscript3": "//",
"as3": "//",
"ada": "--",
"ada95": "--",
"ada2005": "--",
"antlr-objc": "//",
"apl": "⍝",
"applescript": "--",
"autohotkey": ";",
"ahk": ";",
"autoit": ";",
"basemake": "#",
"bash": "#",
"sh": "#",
"ksh": "#",
"zsh": "#",
"shell": "#",
"batch": "::",
"bat": "::",
"dosbatch": "::",
"winbatch": "::",
"bbcbasic": "REM",
"blitzbasic": "REM",
"b3d": "REM",
"bplus": "REM",
"boo": "#",
"c": "//",
"csharp": "//",
"c#": "//",
"cs": "//",
"cpp": "//",
"c++": "//",
"cbmbas": "REM",
"clojure": ";",
"clj": ";",
"clojurescript": ";",
"cljs": ";",
"cmake": "#",
"cobol": "*>",
"cobolfree": "*>",
"common-lisp": ";",
"cl": ";",
"lisp": ";",
"d": "//",
"delphi": "//",
"pas": "//",
"pascal": "//",
"objectpascal": "//",
"eiffel": "--",
"elixir": "#",
"ex": "#",
"exs": "#",
"iex": "#",
"elm": "--",
"emacs-lisp": ";",
"elisp": ";",
"emacs": ";",
"erlang": "%",
"erl": "%",
"fsharp": "//",
"f#": "//",
"factor": "!",
"fish": "#",
"fishshell": "#",
"forth": "\\",
"fortran": "!",
"f90": "!",
"fortranfixed": "!",
"go": "//",
"golang": "//",
"haskell": "--",
"hs": "--",
"inform6": "!",
"i6": "!",
"i6t": "!",
"inform7": "!",
"i7": "!",
"j": "NB.",
"java": "//",
"jsp": "//",
"javascript": "//",
"js": "//",
"julia": "#",
"jl": "#",
"jlcon": "#",
"julia-repl": "#",
"kotlin": "//",
"lua": "--",
"make": "#",
"makefile": "#",
"mf": "#",
"bsdmake": "#",
"matlab": "%",
"matlabsession": "%",
"monkey": "'",
"mysql": "#",
"newlisp": ";",
"nimrod": "#",
"nim": "#",
"objective-c": "//",
"objectivec": "//",
"obj-c": "//",
"objc": "//",
"objective-c++": "//",
"objectivec++": "//",
"obj-c++": "//",
"objc++": "//",
"perl": "#",
"pl": "#",
"perl6": "#",
"pl6": "#",
"raku": "#",
"php": "#",
"php3": "#",
"php4": "#",
"php5": "#",
"plpgsql": "--",
"psql": "--",
"postgresql-console": "--",
"postgres-console": "--",
"postgres-explain": "--",
"postgresql": "--",
"postgres": "--",
"postscript": "%",
"postscr": "%",
"powershell": "#",
"pwsh": "#",
"posh": "#",
"ps1": "#",
"psm1": "#",
"pwsh-session": "#",
"ps1con": "#",
"prolog": "%",
"python": "#",
"py": "#",
"sage": "#",
"python3": "#",
"py3": "#",
"python2": "#",
"py2": "#",
"py2tb": "#",
"pycon": "#",
"pytb": "#",
"py3tb": "#",
"py+ul4": "#",
"qbasic": "REM",
"basic": "REM",
"ragel-ruby": "#",
"ragel-rb": "#",
"rebol": ";",
"red": ";",
"red/system": ";",
"ruby": "#",
"rb": "#",
"duby": "#",
"rbcon": "#",
"irb": "#",
"rust": "//",
"rs": "//",
"sass": "//",
"scala": "//",
"scheme": ";",
"scm": ";",
"sql": "--",
"sql+jinja": "--",
"sqlite3": "--",
"swift": "//",
"tex": "%",
"latex": "%",
"tsql": "--",
"t-sql": "--",
"vbscript": "'",
"vhdl": "--",
"wast": ";;",
"wat": ";;",
"yaml": "#",
"yaml+jinja": "#",
"salt": "#",
"sls": "#",
"zig": "//",
}
64 changes: 48 additions & 16 deletions src/textual_textarea/textarea.py
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@

from textual_textarea.cancellable_input import CancellableInput
from textual_textarea.colors import WidgetColors
from textual_textarea.comments import INLINE_MARKERS
from textual_textarea.containers import FooterContainer, TextContainer
from textual_textarea.error_modal import ErrorModal
from textual_textarea.key_handlers import Cursor, handle_arrow
Expand Down Expand Up @@ -44,6 +45,7 @@ class TextInput(Static, can_focus=True):
clipboard: List[str] = list()
cursor_visible: reactive[bool] = reactive(True)
use_system_clipboard: bool = True
language: reactive[Union[str, None]] = reactive(None)

def __init__(
self,
Expand Down Expand Up @@ -168,20 +170,29 @@ def on_key(self, event: events.Key) -> None:
self.cursor = Cursor(lno=len(self.lines) - 1, pos=len(self.lines[-1]) - 1)
elif event.key == "ctrl+underscore": # actually ctrl+/
event.stop()
lines, first, last = self._get_selected_lines(selection_before)
stripped_lines = [line.lstrip() for line in lines]
indents = [len(line) - len(line.lstrip()) for line in lines]
if all([line.startswith("-- ") for line in stripped_lines]):
no_comment_lines = [line[3:] for line in stripped_lines]
self.lines[first.lno : last.lno + 1] = [
f"{' ' * indent}{stripped_line}"
for indent, stripped_line in zip(indents, no_comment_lines)
]
else:
self.lines[first.lno : last.lno + 1] = [
f"{' ' * indent}-- {stripped_line}"
for indent, stripped_line in zip(indents, stripped_lines)
]
if self.inline_comment_marker:
lines, first, last = self._get_selected_lines(selection_before)
stripped_lines = [line.lstrip() for line in lines]
indents = [len(line) - len(line.lstrip()) for line in lines]
if all(
[
line.startswith(self.inline_comment_marker)
for line in stripped_lines
]
):
no_comment_lines = [
line[len(self.inline_comment_marker) :].lstrip()
for line in stripped_lines
]
self.lines[first.lno : last.lno + 1] = [
f"{' ' * indent}{line}"
for indent, line in zip(indents, no_comment_lines)
]
else:
self.lines[first.lno : last.lno + 1] = [
f"{' ' * indent}{self.inline_comment_marker} {stripped_line}"
for indent, stripped_line in zip(indents, stripped_lines)
]
elif event.key in ("ctrl+c", "ctrl+x"):
event.stop()
if selection_before:
Expand Down Expand Up @@ -291,6 +302,9 @@ def on_key(self, event: events.Key) -> None:
def watch_cursor(self) -> None:
self._scroll_to_cursor()

def watch_language(self, language: str) -> None:
self.inline_comment_marker = INLINE_MARKERS.get(language)

@property
def _content(self) -> RenderableType:
syntax = Syntax(
Expand Down Expand Up @@ -515,7 +529,7 @@ def __init__(
super().__init__(
*children, name=name, id=id, classes=classes, disabled=disabled
)
self.language = language
self._language = language
self.theme = theme
self.theme_colors = WidgetColors.from_theme(self.theme)
self.use_system_clipboard = use_system_clipboard
Expand Down Expand Up @@ -555,10 +569,28 @@ def cursor(self, cursor: Union[Cursor, Tuple[int, int]]) -> None:
"""
self.text_input.move_cursor(cursor[1], cursor[0])

@property
def language(self) -> Union[str, None]:
"""
Returns
str | None: The Pygments short name of the active language
"""
return self.text_input.language

@language.setter
def language(self, language: str) -> None:
"""
Args:
langage (str | None): The Pygments short name for the new language
"""
self.text_input.language = language

def compose(self) -> ComposeResult:
with TextContainer():
yield TextInput(
language=self.language, theme=self.theme, theme_colors=self.theme_colors
language=self._language,
theme=self.theme,
theme_colors=self.theme_colors,
)
yield FooterContainer(theme_colors=self.theme_colors)

Expand Down
8 changes: 6 additions & 2 deletions tests/functional_tests/conftest.py
Original file line number Diff line number Diff line change
Expand Up @@ -12,13 +12,17 @@ def __init__(
driver_class: Union[Type[Driver], None] = None,
css_path: Union[CSSPathType, None] = None,
watch_css: bool = False,
language: Union[str, None] = None,
use_system_clipboard: bool = True,
):
self.language = language
self.use_system_clipboard = use_system_clipboard
super().__init__(driver_class, css_path, watch_css)

def compose(self) -> ComposeResult:
yield TextArea(use_system_clipboard=self.use_system_clipboard)
yield TextArea(
language=self.language, use_system_clipboard=self.use_system_clipboard
)

def on_mount(self) -> None:
ta = self.query_one(TextArea)
Expand All @@ -27,7 +31,7 @@ def on_mount(self) -> None:

@pytest.fixture
def app() -> App:
app = TextAreaApp()
app = TextAreaApp(language="python")
return app


Expand Down
Loading