Skip to content

Commit

Permalink
Merge remote-tracking branch 'upstream/master'
Browse files Browse the repository at this point in the history
  • Loading branch information
adamperkowski committed Jul 28, 2024
2 parents a6db190 + fade4b2 commit 67db8f2
Show file tree
Hide file tree
Showing 27 changed files with 866 additions and 301 deletions.
15 changes: 8 additions & 7 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

1 change: 1 addition & 0 deletions book/src/SUMMARY.md
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@
- [Surround](./surround.md)
- [Textobjects](./textobjects.md)
- [Syntax aware motions](./syntax-aware-motions.md)
- [Pickers](./pickers.md)
- [Keymap](./keymap.md)
- [Commands](./commands.md)
- [Language support](./lang-support.md)
Expand Down
1 change: 1 addition & 0 deletions book/src/generated/lang-support.md
Original file line number Diff line number Diff line change
Expand Up @@ -96,6 +96,7 @@
| java |||| `jdtls` |
| javascript |||| `typescript-language-server` |
| jinja || | | |
| jjdescription || | | |
| jsdoc || | | |
| json |||| `vscode-json-language-server` |
| json5 || | | |
Expand Down
2 changes: 2 additions & 0 deletions book/src/keymap.md
Original file line number Diff line number Diff line change
Expand Up @@ -436,6 +436,8 @@ you to selectively add search terms to your selections.
## Picker

Keys to use within picker. Remapping currently not supported.
See the documentation page on [pickers](./pickers.md) for more info.
[Prompt](#prompt) keybinds also work in pickers, except where they conflict with picker keybinds.

| Key | Description |
| ----- | ------------- |
Expand Down
11 changes: 11 additions & 0 deletions book/src/pickers.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
## Using pickers

Helix has a variety of pickers, which are interactive windows used to select various kinds of items. These include a file picker, global search picker, and more. Most pickers are accessed via keybindings in [space mode](./keymap.md#space-mode). Pickers have their own [keymap](./keymap.md#picker) for navigation.

### Filtering Picker Results

Most pickers perform fuzzy matching using [fzf syntax](https://github.com/junegunn/fzf?tab=readme-ov-file#search-syntax). Two exceptions are the global search picker, which uses regex, and the workspace symbol picker, which passes search terms to the LSP. Note that OR operations (`|`) are not currently supported.

If a picker shows multiple columns, you may apply the filter to a specific column by prefixing the column name with `%`. Column names can be shortened to any prefix, so `%p`, `%pa` or `%pat` all mean the same as `%path`. For example, a query of `helix %p .toml !lang` in the global search picker searches for the term "helix" within files with paths ending in ".toml" but not including "lang".

You can insert the contents of a [register](./registers.md) using `Ctrl-r` followed by a register name. For example, one could insert the currently selected text using `Ctrl-r`-`.`, or the directory of the current file using `Ctrl-r`-`%` followed by `Ctrl-w` to remove the last path section. The global search picker will use the contents of the [search register](./registers.md#default-registers) if you press `Enter` without typing a filter. For example, pressing `*`-`Space-/`-`Enter` will start a global search for the currently selected text.
42 changes: 25 additions & 17 deletions contrib/completion/hx.bash
Original file line number Diff line number Diff line change
Expand Up @@ -2,23 +2,31 @@
# Bash completion script for Helix editor

_hx() {
# $1 command name
# $2 word being completed
# $3 word preceding
local cur prev languages
COMPREPLY=()
cur="${COMP_WORDS[COMP_CWORD]}"
prev="${COMP_WORDS[COMP_CWORD - 1]}"

case "$3" in
-g | --grammar)
COMPREPLY="$(compgen -W 'fetch build' -- $2)"
;;
--health)
local languages=$(hx --health |tail -n '+7' |awk '{print $1}' |sed 's/\x1b\[[0-9;]*m//g')
COMPREPLY="$(compgen -W """$languages""" -- $2)"
;;
*)
COMPREPLY="$(compgen -fd -W "-h --help --tutor -V --version -v -vv -vvv --health -g --grammar --vsplit --hsplit -c --config --log" -- """$2""")"
;;
esac
case "$prev" in
-g | --grammar)
COMPREPLY=($(compgen -W 'fetch build' -- "$cur"))
return 0
;;
--health)
languages=$(hx --health | tail -n '+7' | awk '{print $1}' | sed 's/\x1b\[[0-9;]*m//g')
COMPREPLY=($(compgen -W """$languages""" -- "$cur"))
return 0
;;
esac

local IFS=$'\n'
COMPREPLY=($COMPREPLY)
case "$2" in
-*)
COMPREPLY=($(compgen -W "-h --help --tutor -V --version -v -vv -vvv --health -g --grammar --vsplit --hsplit -c --config --log" -- """$2"""))
return 0
;;
*)
COMPREPLY=($(compgen -fd -- """$2"""))
return 0
;;
esac
} && complete -o filenames -F _hx hx
8 changes: 6 additions & 2 deletions contrib/completion/hx.nu
Original file line number Diff line number Diff line change
Expand Up @@ -4,15 +4,19 @@
# so it has not been specified here and will not be proposed in the autocompletion of Nushell.
# The help message won't be overriden though, so it will still be present here

def health_categories [] { ["all", "clipboard", "languages"] }
def health_categories [] {
let languages = ^hx --health languages | detect columns | get Language | filter { $in != null }
let completions = [ "all", "clipboard", "languages" ] | append $languages
return $completions
}

def grammar_categories [] { ["fetch", "build"] }

# A post-modern text editor.
export extern hx [
--help(-h), # Prints help information
--tutor, # Loads the tutorial
--health: string@health_categories = "all", # Checks for potential errors in editor setup
--health: string@health_categories, # Checks for potential errors in editor setup
--grammar(-g): string@grammar_categories, # Fetches or builds tree-sitter grammars listed in `languages.toml`
--config(-c): glob, # Specifies a file to use for configuration
-v, # Increases logging verbosity each use for up to 3 times
Expand Down
7 changes: 6 additions & 1 deletion helix-core/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,12 @@ ropey = { version = "1.6.1", default-features = false, features = ["simd"] }
smallvec = "1.13"
smartstring = "1.0.1"
unicode-segmentation = "1.11"
unicode-width = "0.1"
# unicode-width is changing width definitions
# that both break our logic and disagree with common
# width definitions in terminals, we need to replace it.
# For now lets lock the version to avoid rendering glitches
# when installing without `--locked`
unicode-width = "=0.1.12"
unicode-general-category = "0.6"
slotmap.workspace = true
tree-sitter.workspace = true
Expand Down
7 changes: 4 additions & 3 deletions helix-core/src/syntax.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1025,9 +1025,10 @@ impl Loader {
match capture {
InjectionLanguageMarker::Name(string) => self.language_config_for_name(string),
InjectionLanguageMarker::Filename(file) => self.language_config_for_file_name(file),
InjectionLanguageMarker::Shebang(shebang) => {
self.language_config_for_language_id(shebang)
}
InjectionLanguageMarker::Shebang(shebang) => self
.language_config_ids_by_shebang
.get(shebang)
.and_then(|&id| self.language_configs.get(id).cloned()),
}
}

Expand Down
28 changes: 22 additions & 6 deletions helix-lsp/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -284,10 +284,8 @@ pub mod util {
if replace_mode {
end += text
.chars_at(cursor)
.skip(1)
.take_while(|ch| chars::char_is_word(*ch))
.count()
+ 1;
.count();
}
(start, end)
}
Expand Down Expand Up @@ -677,7 +675,7 @@ impl Registry {

pub fn remove_by_id(&mut self, id: LanguageServerId) {
let Some(client) = self.inner.remove(id) else {
log::error!("client was already removed");
log::debug!("client was already removed");
return;
};
self.file_event_handler.remove_client(id);
Expand Down Expand Up @@ -737,6 +735,11 @@ impl Registry {
.iter()
.filter_map(|LanguageServerFeatures { name, .. }| {
if let Some(old_clients) = self.inner_by_name.remove(name) {
if old_clients.is_empty() {
log::info!("restarting client for '{name}' which was manually stopped");
} else {
log::info!("stopping existing clients for '{name}'");
}
for old_client in old_clients {
self.file_event_handler.remove_client(old_client.id());
self.inner.remove(old_client.id());
Expand Down Expand Up @@ -765,8 +768,13 @@ impl Registry {
}

pub fn stop(&mut self, name: &str) {
if let Some(clients) = self.inner_by_name.remove(name) {
for client in clients {
if let Some(clients) = self.inner_by_name.get_mut(name) {
// Drain the clients vec so that the entry in `inner_by_name` remains
// empty. We use the empty vec as a "tombstone" to mean that a server
// has been manually stopped with :lsp-stop and shouldn't be automatically
// restarted by `get`. :lsp-restart can be used to restart the server
// manually.
for client in clients.drain(..) {
self.file_event_handler.remove_client(client.id());
self.inner.remove(client.id());
tokio::spawn(async move {
Expand All @@ -786,6 +794,14 @@ impl Registry {
language_config.language_servers.iter().filter_map(
move |LanguageServerFeatures { name, .. }| {
if let Some(clients) = self.inner_by_name.get(name) {
// If the clients vec is empty, do not automatically start a client
// for this server. The empty vec is a tombstone left to mean that a
// server has been manually stopped and shouldn't be started automatically.
// See `stop`.
if clients.is_empty() {
return None;
}

if let Some((_, client)) = clients.iter().enumerate().find(|(i, client)| {
client.try_add_doc(&language_config.roots, root_dirs, doc_path, *i == 0)
}) {
Expand Down
27 changes: 24 additions & 3 deletions helix-stdx/src/faccess.rs
Original file line number Diff line number Diff line change
Expand Up @@ -74,6 +74,11 @@ mod imp {

Ok(())
}

pub fn hardlink_count(p: &Path) -> std::io::Result<u64> {
let metadata = p.metadata()?;
Ok(metadata.nlink())
}
}

// Licensed under MIT from faccess except for `chown`, `copy_metadata` and `is_acl_inherited`
Expand All @@ -94,16 +99,16 @@ mod imp {
SID_IDENTIFIER_AUTHORITY, TOKEN_DUPLICATE, TOKEN_QUERY,
};
use windows_sys::Win32::Storage::FileSystem::{
FILE_ACCESS_RIGHTS, FILE_ALL_ACCESS, FILE_GENERIC_EXECUTE, FILE_GENERIC_READ,
FILE_GENERIC_WRITE,
GetFileInformationByHandle, BY_HANDLE_FILE_INFORMATION, FILE_ACCESS_RIGHTS,
FILE_ALL_ACCESS, FILE_GENERIC_EXECUTE, FILE_GENERIC_READ, FILE_GENERIC_WRITE,
};
use windows_sys::Win32::System::Threading::{GetCurrentThread, OpenThreadToken};

use super::*;

use std::ffi::c_void;

use std::os::windows::{ffi::OsStrExt, fs::OpenOptionsExt};
use std::os::windows::{ffi::OsStrExt, fs::OpenOptionsExt, io::AsRawHandle};

struct SecurityDescriptor {
sd: PSECURITY_DESCRIPTOR,
Expand Down Expand Up @@ -411,6 +416,18 @@ mod imp {

Ok(())
}

pub fn hardlink_count(p: &Path) -> std::io::Result<u64> {
let file = std::fs::File::open(p)?;
let handle = file.as_raw_handle() as isize;
let mut info: BY_HANDLE_FILE_INFORMATION = unsafe { std::mem::zeroed() };

if unsafe { GetFileInformationByHandle(handle, &mut info) } == 0 {
Err(std::io::Error::last_os_error())
} else {
Ok(info.nNumberOfLinks as u64)
}
}
}

// Licensed under MIT from faccess except for `copy_metadata`
Expand Down Expand Up @@ -457,3 +474,7 @@ pub fn readonly(p: &Path) -> bool {
pub fn copy_metadata(from: &Path, to: &Path) -> io::Result<()> {
imp::copy_metadata(from, to)
}

pub fn hardlink_count(p: &Path) -> io::Result<u64> {
imp::hardlink_count(p)
}
1 change: 1 addition & 0 deletions helix-term/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -86,3 +86,4 @@ helix-loader = { path = "../helix-loader" }
smallvec = "1.13"
indoc = "2.0.5"
tempfile = "3.10.1"
same-file = "1.0.1"
2 changes: 1 addition & 1 deletion helix-term/src/application.rs
Original file line number Diff line number Diff line change
Expand Up @@ -575,7 +575,7 @@ impl Application {
doc_save_event.revision
);

doc.set_last_saved_revision(doc_save_event.revision);
doc.set_last_saved_revision(doc_save_event.revision, doc_save_event.save_time);

let lines = doc_save_event.text.len_lines();
let bytes = doc_save_event.text.len_bytes();
Expand Down
35 changes: 35 additions & 0 deletions helix-term/tests/test/commands/write.rs
Original file line number Diff line number Diff line change
Expand Up @@ -649,6 +649,41 @@ async fn test_symlink_write_relative() -> anyhow::Result<()> {
Ok(())
}

#[tokio::test(flavor = "multi_thread")]
#[cfg(not(target_os = "android"))]
async fn test_hardlink_write() -> anyhow::Result<()> {
let dir = tempfile::tempdir()?;

let mut file = tempfile::NamedTempFile::new_in(&dir)?;
let hardlink_path = dir.path().join("linked");
std::fs::hard_link(file.path(), &hardlink_path)?;

let mut app = helpers::AppBuilder::new()
.with_file(&hardlink_path, None)
.build()?;

test_key_sequence(
&mut app,
Some("ithe gostak distims the doshes<ret><esc>:w<ret>"),
None,
false,
)
.await?;

reload_file(&mut file).unwrap();
let mut file_content = String::new();
file.as_file_mut().read_to_string(&mut file_content)?;

assert_eq!(
LineFeedHandling::Native.apply("the gostak distims the doshes"),
file_content
);
assert!(helix_stdx::faccess::hardlink_count(&hardlink_path)? > 1);
assert!(same_file::is_same_file(file.path(), &hardlink_path)?);

Ok(())
}

async fn edit_file_with_content(file_content: &[u8]) -> anyhow::Result<()> {
let mut file = tempfile::NamedTempFile::new()?;

Expand Down
Loading

0 comments on commit 67db8f2

Please sign in to comment.