From b623aec3cbe0ea2edbecffc16e58296646ba5353 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Yaroslav=20de=20la=20Pe=C3=B1a=20Smirnov?= Date: Tue, 25 Jan 2022 22:54:56 +0300 Subject: How I debug programs in Vim --- config.toml | 7 +- .../debugging-session.png | Bin 0 -> 322587 bytes .../weblog/2022-01-25_debugging-in-vim/index.md | 234 ++++++++++++++++++++ .../weblog/2022-01-25_debugging-in-vim/index.ru.md | 237 +++++++++++++++++++++ .../2022-01-25_debugging-in-vim/quit-vim.jpg | Bin 0 -> 17067 bytes sass/css/yaroslavps.scss | 5 +- templates/index.html | 32 ++- 7 files changed, 498 insertions(+), 17 deletions(-) create mode 100644 content/weblog/2022-01-25_debugging-in-vim/debugging-session.png create mode 100644 content/weblog/2022-01-25_debugging-in-vim/index.md create mode 100644 content/weblog/2022-01-25_debugging-in-vim/index.ru.md create mode 100644 content/weblog/2022-01-25_debugging-in-vim/quit-vim.jpg diff --git a/config.toml b/config.toml index e472624..3376afa 100644 --- a/config.toml +++ b/config.toml @@ -7,15 +7,16 @@ feed_filename = "feed.xml" compile_sass = true build_search_index = false -highlight_code = true -highlight_theme = "ayu-dark" - taxonomies = [ {name = "categories"}, ] default_language = "en" +[markdown] +highlight_code = true +highlight_theme = "1337" + [languages.en] [languages.en.translations] diff --git a/content/weblog/2022-01-25_debugging-in-vim/debugging-session.png b/content/weblog/2022-01-25_debugging-in-vim/debugging-session.png new file mode 100644 index 0000000..6dbf16c Binary files /dev/null and b/content/weblog/2022-01-25_debugging-in-vim/debugging-session.png differ diff --git a/content/weblog/2022-01-25_debugging-in-vim/index.md b/content/weblog/2022-01-25_debugging-in-vim/index.md new file mode 100644 index 0000000..a281e6d --- /dev/null +++ b/content/weblog/2022-01-25_debugging-in-vim/index.md @@ -0,0 +1,234 @@ ++++ +title = "How I debug programs in Vim" +date = 2022-01-25T19:49:30Z ++++ + +I like to use separate tools for different tasks, and also like it when those +tools integrate with the other tools I use. Unfortunately I haven't seen much +information online for possible workflows on debugging programs with Vim. There +is information but it is all spread out. So I decided to write about the +workflow that I've built over the years for writing and debugging programs with +Vim. + + + +Many, or most, IDEs already come with some sort of debugging facility. But +those IDEs are not vim, and more often than not, they also come with a lot of +other features that frankly are not very useful. And no, vimifcation plugins +don't count, most of them don't cover even half the functionality that vim +offers. + +I love using vim (technically neovim, eh, same diff), I really think that it is +the best text editor out there. Hell, I even, unironically, think it might the +program with best UI/UX. Sure it is not intuitive at all ([insert obligatory +exit vim meme](#exit-vim)), but once you learn the ropes of it, and it doesn't +take as long as +they meme it to be, it's just bliss. In shot, vim is a lifestyle, not a program. + +The problem is that learning to be a power user of vim can take time. And to be +able to efficiently develop programs typing them is not enough. You also need +to navigate around code bases, quickly fix the most basic of errors (such as +typos), reduce repetitive tasks (e.g. autocomplete), and of course, debug and +profile. + +Fortunately both vim and neovim, for at least some 3–4 years now, offer +facilities to integrate software development and debugging tools with ease. +Since I use neovim, some things that I talk about here might differ a little +bit. + +## Development tools + +It used to be that each editor/IDE needed to have plugins developed for, or even +baked into, them individually. This meant that efforts had to split for every +and each language and editor combination, which meant that not every editor had +support even every popular language. + +Fortunately, Microsoft did a very un-Microsoft thing, and instead of baking in +another completely new implementation of IDE-like development tools for each +language to their VSCode editor, they developed what now is called the Language +Server Protocol[^1], and with the help of Red Hat et al they open sourced it and +standardized it. + +In short, the Language Server Protocol (LSP) is just a JSON RPC protocol for +communications between a server that provides the functions, such as +autocompletion, linting, go-to-definition, etc. and the client which would be +the IDE or editor, which basically displays the results. And one of the editors +that supports LSP is vim. In fact, at least in neovim, the support is built in. + +### LSP + +In order to use LSP in neovim, you need to enable the plugin and configure the +LSP server(s) for your language(s). The good thing is that configuring it is +pretty simple and straightforward, especially since there is a plugin that +already comes with default configurations for a lot of popular languages LSPs. + +I use [vim-plug](https://github.com/junegunn/vim-plug) to manage my plugins, so +first I added the official [lspconfig](https://github.com/neovim/nvim-lspconfig) +plugin: + +```vim +"... + Plug 'neovim/nvim-lspconfig' +"... +``` + +And then proceeded to configure it for my needs by adding the configurations for +the LSP servers for C, Go, Rust, Python and Javascript, the languages that I use +the most often: + +```vim +" LSP + set omnifunc=v:lua.vim.lsp.omnifunc + lua require('lspconfig').clangd.setup{filetypes = { "c", "cpp", "objc", "objcpp", "ch" }} + lua require('lspconfig').gopls.setup{} + lua require('lspconfig').pylsp.setup{} + lua require('lspconfig').rls.setup{} + lua require('lspconfig').tsserver.setup{} + +" LSP keybinds + nmap gd lua vim.lsp.buf.definition() + nmap gD lua vim.lsp.buf.declaration() + nmap gK lua vim.lsp.buf.hover() + nmap n lua vim.lsp.buf.rename() + nmap b lua vim.lsp.buf.formatting() + +" neovim overrides my omnifunc with whatever ccomplete is, so I use this + autocmd FileType c,ch,header,cpp setlocal omnifunc=v:lua.vim.lsp.omnifunc +``` + +Of course, in order to use the functionality, you also need to have the LSP +servers installed in your system. In the case of Go, it comes with the standard +toolchain, same as with Rust. For C, you will need clang, as far as I know GCC +doesn't provide an LSP implementation. + +The keybindings in my config make it so that I can go to definition with `gd`, +go to declaration with `gD`, get a popup with information implementations and +comment documentation with `gK`, rename variables with `n` (leader is +space in my config) and process the file through a formatting tool (e.g. gofmt) +with `b`. + +### Other plugins + +Now, LSP provides most of the functions you would expect from an IDE, but there +are still some other plugins that I use for better quality of life. Namely: + +```vim +"... + Plug 'ervandew/supertab' + Plug 'majutsushi/tagbar' + Plug 'tpope/vim-commentary' +"... +``` + +The first one, [supertab](https://github.com/ervandew/supertab), makes using +autocompletion much more comfy. By default the omnifunc autocomplete is bound to +\\ which is not very comfortable to enter. Usually, in shells and +other editors, you use Tab to autocomplete, which is a pretty sane way to do it. +But sometimes you actually need to insert tabs manually. That's where this +plugin comes in, since it intelligently chooses whether to insert a tab, or +autocomplete on pressing the Tab key. + +The next is [tagbar](https://github.com/preservim/tagbar). This plugin allows +you to open up a sidebar of sorts that lists the global variables, +data/struct/type definitions, and functions in the current source file. It +requires a ctags implementation to work. + +Finally is [vim-commentary](https://github.com/tpope/vim-commentary), which +provides an easy way to comment out multiple lines easily. Not AS useful as the +other ones, but still handy nonetheless. + +## Debugging + +Sooner or later when writing programs and testing them you come across some kind +of programming error that causes it to misbehave. You can try to find the +problem manually by looking at the code and scratching your head, but a much +better way is to fire up a debugger and go step-by-step over the program +execution in search of your bug. + +For some time, at least for C and other languages that are supported by it, I +would just open gdb in a different terminal window and keep my source file open +in vim in another. But a couple of additions were made to both vim and neovim +which made it much pleasurable to debug programs. + +The first is `terminal` which, just as its name implies, is a terminal emulator +inside vim. Basically it allows you to open a terminal inside a vim buffer. For +me personally this feature by itself is not of great use, since I use a tiling +WM which already makes it much more comfortable for me to move around terminals. +However, it gives way to a plugin that is much more useful, and which depends on +this functionality. + +Introducing `Termdebug`. This is a plugin which is built into both Vim (version +>= 8.1) and Neovim. What it basically does is it opens two split windows, one +with gdb and the second one with the output of your program. You can then input +gdb commands directly into the gdb console, or through some vim-provided +shortcuts. + +In order to start using Termdebug, you need to first load the plugin: + +```vim +:packadd termdebug +``` + +And then load it providing the name of the binary that is going to be executed +by gdb: + +```vim +:Termdebug +``` + +You will be greeted with two new split buffers, one with the program output and +the other with a gdb console. They are both terminal buffers. The way you use +those terminal buffers in vim is, you enter insert mode to type into the +terminal, and exit insert mode with \\. Why not escape? Because +some terminal programs make use of escape, for example, vi mode in bash or zsh. +Or maybe even another vim instance inside vim, why not ¯\\_(ツ)_/¯. + +After having opened Termdebug, you can run the program the classic way from gdb. +Or from within vim with `:Run `. Other useful shortcuts are `:Break` to +set a breakpoint at the current line of code, `:Clear` to delete a breakpoint at +the current line if there's any, `:Continue` to resume program execution, etc. + +[![A screenshot of an example debugging session](debugging-session.png)](debugging-session.png) +
+ +An example debugging session. +
+ +What makes this really wonderful is the fact that you see the process of +debugging straight in your source file. You don't have to constantly list the +code inside gdb, and you can just navigate around files to set or clear +breakpoints. + +Of course, since you're just debugging programs with gdb the usual rules of +debugging with gdb apply. For example, you need to compile your program with +debugging symbols, i.e. with the `-g` flag. + +## Afterword + +These are just of the plugins and configurations that use in vim to help me +write and debug software. This is, of course not meant as an in-depth tutorial +or guide on how to configure vim or how to properly debug programs. This is +just a mere guide on how one can use vim in conjunction with other tools to +develop and debug programs. There's a lot of information on the internet on how +to configure vim or how to debug programs with gdb, and using other tools such +as valgrind and ASan. + +If you want to know more about lsp in neovim, you can read the help page inside +neovim by entering `:help lsp`. The same goes with Termdebugger: `:help +Termdebugger`. + +If you are interested, you can check my full neovim config here: + + +
+ +![How to exit vim](quit-vim.jpg) + +If you have never used Vim before, and this article caused you to open it in a +whim and now you can't exit it, this picture might be helpful. +
+ +[^1]: Not completely Unix-y, but somewhat adherent to the Unix philosophy in the + sense that one program should do one thing and do it right. A shock coming + from Microsoft, I know. You can learn more about it here: + diff --git a/content/weblog/2022-01-25_debugging-in-vim/index.ru.md b/content/weblog/2022-01-25_debugging-in-vim/index.ru.md new file mode 100644 index 0000000..23f870e --- /dev/null +++ b/content/weblog/2022-01-25_debugging-in-vim/index.ru.md @@ -0,0 +1,237 @@ ++++ +title = "Как я отлаживаю программы в Vim" +date = 2022-01-25T19:49:30Z ++++ + +Мне нравиться использовать отдельные инструменты для разных задач, но также мне +нравится когда эти инструменты хорошо интегрируются с моими остальными +инструментами. К сожалению я не видел достаточной информации в интернете насчёт +возможного процесса отладки программ посредством Vim. Информация, точнее, есть, +но она разбросана по всему интернету. И так я решил написать о моём процессе +написания и отладки с Vim. + + + +Многие, а может и большинство, IDE уже имеют встроенные функции отладки +программ. Но также эти IDE не vim, и чаще всего они включаю в себя куча других +сомнительных функций. И нет, «Vim mode» плагины не считаются, большинство из них +даже половина функционала Vim'а не включают в себя. + +Я обожаю vim (на самом деле использую neovim, кому какая разница), и я считаю +его лучшим текстовым редактором. Я даже в полном серьёзе считаю что у Vim'а +лучшее UI/UX из любой программы. Да, он далеко не самая интуитивная программа +([вставьте мем про выхода из Vim'а](#exit-vim)), но выучить основные азы не так +сложно как кажется зато как только их выучить сложно будет пользоваться чем-то +другим. Короче говоря, Vim это не программа а образ жизни. + +Проблема заключается в том, что стать продвинутым пользователем вима всё-таки +занимает немалого времени. А чтобы эффективно разрабатывать программы, не +достаточно просто напечатать код программы. Так же необходимо быстро +перемещаться по исходным файлам, быстро исправлять самые банальные ошибки (вроде +опечатках), свести к минимуму механические задачи (например с помощью +автозаполнения), и конечно, отладка и профилировка. + +К счастью как Vim как и Neovim уже несколько лет предоставляют функционал для +интегрирования инструментов разработки и отладки с лёгкостью. Так как я +пользуюсь Neovim, то соответственно некоторые моменты могут отличаться от +ванильного Vim. + +## Инструменты разработки + +В старые добрые времена для каждого редактора/IDE отдельно должны были быть +разработаны плагины или компоненты. То есть, для каждой комбинации редактора и +языка программирования выполнялся труд по разработке инструмента, что означала +что далеко не все редакторы и не все языки имели достаточные инструменты для +разработки и отладки. + +К счастью, Мелкомягкие поступили совсем не по Мелкомягкому и вместо того чтобы +встроить совершенно новую реализацию среды разработки для VSCode, они +разработали так называемый Language Server Protocol[^1], и с помощью Red Hat и +других выложили в опен сорс и превратили его в некий стандарт. + +Если коротко говоря, то Language Server Protocol (LSP) это просто JSON RPC +протокол для общения между сервером, который предоставляет функции для +разработки программ, как например, автозаполнение, статический анализ, переход +на определение, и т.д., и клиент, то есть, IDE или редактор, который отображает +результат всех этих действий. Среди редакторов, которые поддерживают LSP, +находится Vim. В Neovim'е это поддержка даже встроена. + +### LSP + +Затем чтобы использовать LSP в neovim, необходимо включить плагин и настроить +LSP сервер(а) ваш(его/их) язык(а/ов). Хорошо то, что настройка этого функционала +довольно проста, так как есть официальный плагин который уже предоставляет +настройки по умолчанию для многих LSP разных языков. + +Я пользуюсь [vim-plug](https://github.com/junegunn/vim-plug) чтобы управлять +своими планинами, поэтому я сначала с его помощью добавил плагин +[lspconfig](https://github.com/neovim/nvim-lspconfig): + +```vim +"... + Plug 'neovim/nvim-lspconfig' +"... +``` + +Затем я его настроил под себя, добавляя конфигурации для LSP сервера C, Go, +Rust, Python, и Javascript, языки, которыми я чаще всего пользуюсь: + +```vim +" LSP + set omnifunc=v:lua.vim.lsp.omnifunc + lua require('lspconfig').clangd.setup{filetypes = { "c", "cpp", "objc", "objcpp", "ch" }} + lua require('lspconfig').gopls.setup{} + lua require('lspconfig').pylsp.setup{} + lua require('lspconfig').rls.setup{} + lua require('lspconfig').tsserver.setup{} + +" LSP keybinds + nmap gd lua vim.lsp.buf.definition() + nmap gD lua vim.lsp.buf.declaration() + nmap gK lua vim.lsp.buf.hover() + nmap n lua vim.lsp.buf.rename() + nmap b lua vim.lsp.buf.formatting() + +" neovim overrides my omnifunc with whatever ccomplete is, so I use this + autocmd FileType c,ch,header,cpp setlocal omnifunc=v:lua.vim.lsp.omnifunc +``` + +Ну и конечно, затем чтобы пользоваться функционалом, необходимо установить LSP +сервера. В случае Go и Rust, он уже входит в их официальные тулчейны. Для C +необходимо установить clang, насколько я знаю GCC не предоставляет имплементацию +сервера LSP. + +Привязка клавиш в моей конфигурации позволяют мне переходить к определению с +`gd` к декларации с `gD`, открыть всплывающее окно с информации имплементации и +документирующими комментариями с `gK`, переименовать переменные с `n` +(leader в моей конфигурации это пробел), и скормить исходный файл утилиты +форматирования кода (например, gofmt) с `b`. + +### Другие плагины + +LSP предоставляет большинство функций, которые стоит ожидать от IDE, но так же +есть и другие плагины, которые улучшают процесс редактирования кода. В том +числе: + +```vim +"... + Plug 'ervandew/supertab' + Plug 'majutsushi/tagbar' + Plug 'tpope/vim-commentary' +"... +``` + +Первый из них, [supertab](https://github.com/ervandew/supertab), делает более +удобным автозаполнение. По умолчанию автозаполнение `omnifunc` привязано к +\\, что не очень удобно. Обычно в консольных терминалах и в других +редакторах клавиша Tab вызывает функцию автозаполнения, что казалось бы +разумным. Но иногда также есть необходимость вставить символ табуляции вручную. +Для этого и годиться этот плагин. После нажатия на клавишу Tab, он +автоматически, в зависимости от контекста, либо вставляет символ табуляции либо +вызывает автозаполнения. + +Далее [tagbar](https://github.com/preservim/tagbar). Данный плагин позволяет +открывать панель со списком глобальных перемен, определений структур данных, и +функции в текущем исходном файле. Для его работы требуются ctags. + +Наконец [vim-commentary](https://github.com/tpope/vim-commentary), который +предоставляет возможность легко и быстро закомментировать несколько строк +одновременно. Не так прямо и полезно как другие плагины, но тем не менее. + +## Отладка + +Рано или поздно в процессе разработки программного обеспечения вы столкнётесь с +ошибкой, которая приведёт к неправильно исполнению программы. Можно найти +проблему вручную выпяливая код и чеща себе голову. Но есть и способ получше. +Можно открыть отладчик и анализировать программу наблюдая за её исполнение +пошагово. + +Некоторое время, по крайней мере с C и языками поддерживаемые gdb, я просто +открывал gdb в другом окне с терминалом и оставлял открытым в другом окне Vim с +исходным кодом. Но появились пару новинок в Vim'е, которые сделали этот процесс +намного удобнее. + +Первая это `terminal`, которая как имя и полагает, является терминальным +эмулятором внутри самого вима. Если коротко, то оно позволяет открывать новый +терминал внутри буфера вима. Лично для меня этот функционал сам по себе не очень +полезный, так как я пользуюсь плиточным оконным менеджером, которые мне и так +уже предоставляет возможность быстро и удобно перемещаться по окнам. Тем не +менее, этот плагин делает возможным следующий плагин, который зависит от него. + +Звезда нашего шоу — `Termdebug`, плагин, который встроенный как в Vim (версия >= +8.1) так и в Neovim. То, что он делает это открывать два буфера, один с консолью +gdb, а второй с выходом отлаживаемой программы. В буфере с консолью пви можно +традиционным способом пользоваться gdb вводя в неё команды, или через команды +вима. + +Для того, чтобы начать пользоваться Termbug'ом, необходимо сначала подгрузить +плагин: + +```vim +:packadd termdebug +``` + +Затем запускаем его передавая название бинарного файла, который требуется +отлаживать: + +```vim +:Termdebug <путь к бинарнику> +``` + +После запуска появятся два буфера, один с выводом отлаживаемой программы и +второй с консолью gdb. Оба буфера терминалы. Для того чтобы пользоваться +терминальными буферами в виме, нужно перейти в режим вставки чтобы печатать в +терминале. Чтобы выйти из режима вставки необходимо ввести \\. +Почему не Esc? А потому, что некоторые терминальные программы пользуются им, +например, режим vi в bash или zsh. Ну или например другая истанция вима внутриа +вима, почему бы и нет ¯\\_(ツ)_/¯. + +После того, как открыли Termdebug, можно запустить отлаживаемую программу +традиционным способом с gdb. Или из самого вима с `:Run <аргументы>`. Другие +полезные команды: `:Break` для того, чтобы вставить точку остановки в текущей +строке кода, `:Clear` чтобы её удалить, `:Continue` для того чтобы продолжить +выполнение программы и т.д. + +[![Снимок примера отладки программы в Vim](debugging-session.png)](debugging-session.png) +
+ +Пример отладки программы в Vim. +
+ +Что делает это замечательным это то, что можно наблюдать за процессом отладки +прямиком из исходного файла. Не надо постоянно вводить `list` в gdb, можно +просто переходить по файлам и ставить или удалять точки установки. + +Конечно, так как по сути отладка-то происходит в gdb, то правила и те же. +Например, необходимо компилировать программу с символами для отладки, т.е. с +флагом `-g`. + +## Послесловие + +Это только некоторые плагины, которые помогают мне более эффективно писать и +отлаживать программы в виме. Этот текст не является подробным туториалом +настройки вима или правильной отладки программ. Им я просто хотел показать как +можно удобно и легко пользоваться вимом наряду с другими инструментами для +упрощения разработки и отладки ПО. Существует множество информации в интернете +насчёт того, как правильно настроить вим, или как отлаживать программы с gdb или +другими утилитами как valgrind или ASan. + +Если хочется узнать больше про LSP в neovim, советую почитать страницу помощи в +neovim вводя `:help lsp`. Аналогично для Termdebugger `:help Termdebugger`. + +Если вам интересно, то моя конфигурация вима доступна по ссылке + + +
+ +![Как выйти из вима](quit-vim.jpg) + +Если вы никогда не пользовались вимом, и вы повелись на то, чтобы его открыть, +после того как прочитали этот текст, и вы не можете выйти из него то, эта +картинка вам в помощь. +
+ +[^1]: Не полностью соответствует философии Юникс, но по большей мере да, в том + плане что это программа, которая делает одну вещь и делает её хорошо. Да, я + тоже в шоке, не ожидал такого от Microsoft. Больше информации по LSP можно + прочитать здесь: diff --git a/content/weblog/2022-01-25_debugging-in-vim/quit-vim.jpg b/content/weblog/2022-01-25_debugging-in-vim/quit-vim.jpg new file mode 100644 index 0000000..b9025bd Binary files /dev/null and b/content/weblog/2022-01-25_debugging-in-vim/quit-vim.jpg differ diff --git a/sass/css/yaroslavps.scss b/sass/css/yaroslavps.scss index bc3d103..90209b3 100644 --- a/sass/css/yaroslavps.scss +++ b/sass/css/yaroslavps.scss @@ -95,12 +95,11 @@ pre { padding: 1em 0; overflow-x: auto; font-family: $font_mono; - color: $gray; background-color: $bg0; } -code { - color: $gray; +p > code { + color: $green; font-family: $font_mono; } diff --git a/templates/index.html b/templates/index.html index 6a1a949..e0192fa 100644 --- a/templates/index.html +++ b/templates/index.html @@ -25,15 +25,26 @@ {% if lang == "ru" %}

- Добро пожаловать в мой уголок интернета. Чувствуйте себя как дома. - Иногда пишу на русском, но по большей части пишу на английском. Что - можно найти на моём сайте: + Добро пожаловать в мой уголок интернета. Меня зовут Ярослав. Я занимаюсь + программировнием в качестве своей работы и увлечения. В данный момент я + работаю разработчиком ПО для технологии LTE и 5G NR. +

+

+ Иногда я также выкладываю то, что мне кажется интересным или полезным в + этом сайте, хоть и не так часто как хотелось. Иногда пишу на русском, но + по большей части пишу на английском. Что можно найти на моём сайте:

{% elif lang == "es" %}

- Bienvenido a mi pequeño rincón de internet. Siéntete libre de echar una - mirada. A veces escribo en español, pero la mayor parte del contenido - está en inglés. Esto es lo que puedes encontrar en mi sitio: + Bienvenido a mi pequeño rincón de internet. Yo soy Yaroslav. Me gano la + vida escribiendo software, aunque también es mi pasatiempo. Actualmente + trabajo escribiendo software para tecnologías LTE y 5G NR. +

+

+ A veces también publico cosas que me parecen interesantes o de valor a + este sitio, aunque no tan seguido como me gustaría. Siéntete libre de + echar una mirada. A veces escribo en español, pero la mayor parte del + contenido está en inglés. Esto es lo que puedes encontrar en mi sitio:

{% else %}

@@ -42,10 +53,9 @@ software for LTE and 5G NR technologies.

- I also sometimes post things - that I think are interesting or of value to this site, although maybe - not as often as I'd like. Feel free to take a look around. This is what - you can find on my website: + I also sometimes post things that I think are interesting or of value to + this site, although maybe not as often as I'd like. Feel free to take a + look around. This is what you can find on my website:

{% endif %} @@ -135,7 +145,7 @@ {{ trans(key="see_more", lang=lang) }} -

+

{% if lang == "ru" %} Связаться {% elif lang == "es" %} -- cgit v1.2.3